OGRE: OgreAxisAlignedBox.h Source File - OGRE Documentation

# OgreAxisAlignedBox.h

Go to the documentation of this file.

    00001 /*
    00002 -----------------------------------------------------------------------------
    00003 This source file is part of OGRE
    00004 (Object-oriented Graphics Rendering Engine)
    00005 For the latest info, see http://www.ogre3d.org/
    00006
    00007 Copyright (c) 2000-2006 Torus Knot Software Ltd
    00008 Also see acknowledgements in Readme.html
    00009
    00010 This program is free software; you can redistribute it and/or modify it under
    00011 the terms of the GNU Lesser General Public License as published by the Free Software
    00012 Foundation; either version 2 of the License, or (at your option) any later
    00013 version.
    00014
    00015 This program is distributed in the hope that it will be useful, but WITHOUT
    00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
    00018
    00019 You should have received a copy of the GNU Lesser General Public License along with
    00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
    00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
    00022 http://www.gnu.org/copyleft/lesser.txt.
    00023
    00024 You may alternatively use this source under the terms of a specific version of
    00025 the OGRE Unrestricted License provided you have obtained such a license from
    00026 Torus Knot Software Ltd.
    00027 -----------------------------------------------------------------------------
    00028 */
    00029 #ifndef __AxisAlignedBox_H_
    00030 #define __AxisAlignedBox_H_
    00031
    00032 // Precompiler options
    00033 #include "OgrePrerequisites.h"
    00034
    00035 #include "OgreVector3.h"
    00036 #include "OgreMatrix4.h"
    00037
    00038 namespace Ogre {
    00039
    00049     class _OgreExport AxisAlignedBox
    00050     {
    00051     public:
    00052         enum Extent
    00053         {
    00054             EXTENT_NULL,
    00055             EXTENT_FINITE,
    00056             EXTENT_INFINITE
    00057         };
    00058     protected:
    00059
    00060         Vector3 mMinimum;
    00061         Vector3 mMaximum;
    00062         Extent mExtent;
    00063         mutable Vector3* mpCorners;
    00064
    00065     public:
    00066         /*
    00067         1-----2
    00068         /|    /|
    00069         / |   / |
    00070         5-----4  |
    00071         |  0--|--3
    00072         | /   | /
    00073         |/    |/
    00074         6-----7
    00075         */
    00076         typedef enum {
    00077             FAR_LEFT_BOTTOM = 0,
    00078             FAR_LEFT_TOP = 1,
    00079             FAR_RIGHT_TOP = 2,
    00080             FAR_RIGHT_BOTTOM = 3,
    00081             NEAR_RIGHT_BOTTOM = 7,
    00082             NEAR_LEFT_BOTTOM = 6,
    00083             NEAR_LEFT_TOP = 5,
    00084             NEAR_RIGHT_TOP = 4
    00085         } CornerEnum;
    00086         inline AxisAlignedBox() : mpCorners(0)
    00087         {
    00088             // Default to a null box
    00089             setMinimum( -0.5, -0.5, -0.5 );
    00090             setMaximum( 0.5, 0.5, 0.5 );
    00091             mExtent = EXTENT_NULL;
    00092         }
    00093         inline AxisAlignedBox(Extent e) : mpCorners(0)
    00094         {
    00095             setMinimum( -0.5, -0.5, -0.5 );
    00096             setMaximum( 0.5, 0.5, 0.5 );
    00097             mExtent = e;
    00098         }
    00099
    00100         inline AxisAlignedBox(const AxisAlignedBox & rkBox) : mpCorners(0)
    00101         {
    00102             if (rkBox.isNull())
    00103                 setNull();
    00104             else if (rkBox.isInfinite())
    00105                 setInfinite();
    00106             else
    00107                 setExtents( rkBox.mMinimum, rkBox.mMaximum );
    00108         }
    00109
    00110         inline AxisAlignedBox( const Vector3& min, const Vector3& max ) : mpCorners(0)
    00111         {
    00112             setExtents( min, max );
    00113         }
    00114
    00115         inline AxisAlignedBox(
    00116             Real mx, Real my, Real mz,
    00117             Real Mx, Real My, Real Mz ) : mpCorners(0)
    00118         {
    00119             setExtents( mx, my, mz, Mx, My, Mz );
    00120         }
    00121
    00122         AxisAlignedBox& operator=(const AxisAlignedBox& rhs)
    00123         {
    00124             // Specifically override to avoid copying mpCorners
    00125             if (rhs.isNull())
    00126                 setNull();
    00127             else if (rhs.isInfinite())
    00128                 setInfinite();
    00129             else
    00130                 setExtents(rhs.mMinimum, rhs.mMaximum);
    00131
    00132             return *this;
    00133         }
    00134
    00135         ~AxisAlignedBox()
    00136         {
    00137             if (mpCorners)
    00138                 OGRE_FREE(mpCorners, MEMCATEGORY_SCENE_CONTROL);
    00139         }
    00140
    00141
    00144         inline const Vector3& getMinimum(void) const
    00145         {
    00146             return mMinimum;
    00147         }
    00148
    00152         inline Vector3& getMinimum(void)
    00153         {
    00154             return mMinimum;
    00155         }
    00156
    00159         inline const Vector3& getMaximum(void) const
    00160         {
    00161             return mMaximum;
    00162         }
    00163
    00167         inline Vector3& getMaximum(void)
    00168         {
    00169             return mMaximum;
    00170         }
    00171
    00172
    00175         inline void setMinimum( const Vector3& vec )
    00176         {
    00177             mExtent = EXTENT_FINITE;
    00178             mMinimum = vec;
    00179         }
    00180
    00181         inline void setMinimum( Real x, Real y, Real z )
    00182         {
    00183             mExtent = EXTENT_FINITE;
    00184             mMinimum.x = x;
    00185             mMinimum.y = y;
    00186             mMinimum.z = z;
    00187         }
    00188
    00192         inline void setMinimumX(Real x)
    00193         {
    00194             mMinimum.x = x;
    00195         }
    00196
    00197         inline void setMinimumY(Real y)
    00198         {
    00199             mMinimum.y = y;
    00200         }
    00201
    00202         inline void setMinimumZ(Real z)
    00203         {
    00204             mMinimum.z = z;
    00205         }
    00206
    00209         inline void setMaximum( const Vector3& vec )
    00210         {
    00211             mExtent = EXTENT_FINITE;
    00212             mMaximum = vec;
    00213         }
    00214
    00215         inline void setMaximum( Real x, Real y, Real z )
    00216         {
    00217             mExtent = EXTENT_FINITE;
    00218             mMaximum.x = x;
    00219             mMaximum.y = y;
    00220             mMaximum.z = z;
    00221         }
    00222
    00226         inline void setMaximumX( Real x )
    00227         {
    00228             mMaximum.x = x;
    00229         }
    00230
    00231         inline void setMaximumY( Real y )
    00232         {
    00233             mMaximum.y = y;
    00234         }
    00235
    00236         inline void setMaximumZ( Real z )
    00237         {
    00238             mMaximum.z = z;
    00239         }
    00240
    00243         inline void setExtents( const Vector3& min, const Vector3& max )
    00244         {
    00245             assert( (min.x <= max.x && min.y <= max.y && min.z <= max.z) &&
    00246                 "The minimum corner of the box must be less than or equal to maximum corner" );
    00247
    00248             mExtent = EXTENT_FINITE;
    00249             mMinimum = min;
    00250             mMaximum = max;
    00251         }
    00252
    00253         inline void setExtents(
    00254             Real mx, Real my, Real mz,
    00255             Real Mx, Real My, Real Mz )
    00256         {
    00257             assert( (mx <= Mx && my <= My && mz <= Mz) &&
    00258                 "The minimum corner of the box must be less than or equal to maximum corner" );
    00259
    00260             mExtent = EXTENT_FINITE;
    00261
    00262             mMinimum.x = mx;
    00263             mMinimum.y = my;
    00264             mMinimum.z = mz;
    00265
    00266             mMaximum.x = Mx;
    00267             mMaximum.y = My;
    00268             mMaximum.z = Mz;
    00269
    00270         }
    00271
    00296         inline const Vector3* getAllCorners(void) const
    00297         {
    00298             assert( (mExtent == EXTENT_FINITE) && "Can't get corners of a null or infinite AAB" );
    00299
    00300             // The order of these items is, using right-handed co-ordinates:
    00301             // Minimum Z face, starting with Min(all), then anticlockwise
    00302             //   around face (looking onto the face)
    00303             // Maximum Z face, starting with Max(all), then anticlockwise
    00304             //   around face (looking onto the face)
    00305             // Only for optimization/compatibility.
    00306             if (!mpCorners)
    00307                 mpCorners = OGRE_ALLOC_T(Vector3, 8, MEMCATEGORY_SCENE_CONTROL);
    00308
    00309             mpCorners[0] = mMinimum;
    00310             mpCorners[1].x = mMinimum.x; mpCorners[1].y = mMaximum.y; mpCorners[1].z = mMinimum.z;
    00311             mpCorners[2].x = mMaximum.x; mpCorners[2].y = mMaximum.y; mpCorners[2].z = mMinimum.z;
    00312             mpCorners[3].x = mMaximum.x; mpCorners[3].y = mMinimum.y; mpCorners[3].z = mMinimum.z;
    00313
    00314             mpCorners[4] = mMaximum;
    00315             mpCorners[5].x = mMinimum.x; mpCorners[5].y = mMaximum.y; mpCorners[5].z = mMaximum.z;
    00316             mpCorners[6].x = mMinimum.x; mpCorners[6].y = mMinimum.y; mpCorners[6].z = mMaximum.z;
    00317             mpCorners[7].x = mMaximum.x; mpCorners[7].y = mMinimum.y; mpCorners[7].z = mMaximum.z;
    00318
    00319             return mpCorners;
    00320         }
    00321
    00324         Vector3 getCorner(CornerEnum cornerToGet) const
    00325         {
    00326             switch(cornerToGet)
    00327             {
    00328             case FAR_LEFT_BOTTOM:
    00329                 return mMinimum;
    00330             case FAR_LEFT_TOP:
    00331                 return Vector3(mMinimum.x, mMaximum.y, mMinimum.z);
    00332             case FAR_RIGHT_TOP:
    00333                 return Vector3(mMaximum.x, mMaximum.y, mMinimum.z);
    00334             case FAR_RIGHT_BOTTOM:
    00335                 return Vector3(mMaximum.x, mMinimum.y, mMinimum.z);
    00336             case NEAR_RIGHT_BOTTOM:
    00337                 return Vector3(mMaximum.x, mMinimum.y, mMaximum.z);
    00338             case NEAR_LEFT_BOTTOM:
    00339                 return Vector3(mMinimum.x, mMinimum.y, mMaximum.z);
    00340             case NEAR_LEFT_TOP:
    00341                 return Vector3(mMinimum.x, mMaximum.y, mMaximum.z);
    00342             case NEAR_RIGHT_TOP:
    00343                 return mMaximum;
    00344             default:
    00345                 return Vector3();
    00346             }
    00347         }
    00348
    00349         _OgreExport friend std::ostream& operator<<( std::ostream& o, const AxisAlignedBox aab )
    00350         {
    00351             switch (aab.mExtent)
    00352             {
    00353             case EXTENT_NULL:
    00354                 o << "AxisAlignedBox(null)";
    00355                 return o;
    00356
    00357             case EXTENT_FINITE:
    00358                 o << "AxisAlignedBox(min=" << aab.mMinimum << ", max=" << aab.mMaximum << ")";
    00359                 return o;
    00360
    00361             case EXTENT_INFINITE:
    00362                 o << "AxisAlignedBox(infinite)";
    00363                 return o;
    00364
    00365             default: // shut up compiler
    00366                 assert( false && "Never reached" );
    00367                 return o;
    00368             }
    00369         }
    00370
    00374         void merge( const AxisAlignedBox& rhs )
    00375         {
    00376             // Do nothing if rhs null, or this is infinite
    00377             if ((rhs.mExtent == EXTENT_NULL) || (mExtent == EXTENT_INFINITE))
    00378             {
    00379                 return;
    00380             }
    00381             // Otherwise if rhs is infinite, make this infinite, too
    00382             else if (rhs.mExtent == EXTENT_INFINITE)
    00383             {
    00384                 mExtent = EXTENT_INFINITE;
    00385             }
    00386             // Otherwise if current null, just take rhs
    00387             else if (mExtent == EXTENT_NULL)
    00388             {
    00389                 setExtents(rhs.mMinimum, rhs.mMaximum);
    00390             }
    00391             // Otherwise merge
    00392             else
    00393             {
    00394                 Vector3 min = mMinimum;
    00395                 Vector3 max = mMaximum;
    00396                 max.makeCeil(rhs.mMaximum);
    00397                 min.makeFloor(rhs.mMinimum);
    00398
    00399                 setExtents(min, max);
    00400             }
    00401
    00402         }
    00403
    00406         inline void merge( const Vector3& point )
    00407         {
    00408             switch (mExtent)
    00409             {
    00410             case EXTENT_NULL: // if null, use this point
    00411                 setExtents(point, point);
    00412                 return;
    00413
    00414             case EXTENT_FINITE:
    00415                 mMaximum.makeCeil(point);
    00416                 mMinimum.makeFloor(point);
    00417                 return;
    00418
    00419             case EXTENT_INFINITE: // if infinite, makes no difference
    00420                 return;
    00421             }
    00422
    00423             assert( false && "Never reached" );
    00424         }
    00425
    00435         inline void transform( const Matrix4& matrix )
    00436         {
    00437             // Do nothing if current null or infinite
    00438             if( mExtent != EXTENT_FINITE )
    00439                 return;
    00440
    00441             Vector3 oldMin, oldMax, currentCorner;
    00442
    00443             // Getting the old values so that we can use the existing merge method.
    00444             oldMin = mMinimum;
    00445             oldMax = mMaximum;
    00446
    00447             // reset
    00448             setNull();
    00449
    00450             // We sequentially compute the corners in the following order :
    00451             // 0, 6, 5, 1, 2, 4 ,7 , 3
    00452             // This sequence allows us to only change one member at a time to get at all corners.
    00453
    00454             // For each one, we transform it using the matrix
    00455             // Which gives the resulting point and merge the resulting point.
    00456
    00457             // First corner
    00458             // min min min
    00459             currentCorner = oldMin;
    00460             merge( matrix * currentCorner );
    00461
    00462             // min,min,max
    00463             currentCorner.z = oldMax.z;
    00464             merge( matrix * currentCorner );
    00465
    00466             // min max max
    00467             currentCorner.y = oldMax.y;
    00468             merge( matrix * currentCorner );
    00469
    00470             // min max min
    00471             currentCorner.z = oldMin.z;
    00472             merge( matrix * currentCorner );
    00473
    00474             // max max min
    00475             currentCorner.x = oldMax.x;
    00476             merge( matrix * currentCorner );
    00477
    00478             // max max max
    00479             currentCorner.z = oldMax.z;
    00480             merge( matrix * currentCorner );
    00481
    00482             // max min max
    00483             currentCorner.y = oldMin.y;
    00484             merge( matrix * currentCorner );
    00485
    00486             // max min min
    00487             currentCorner.z = oldMin.z;
    00488             merge( matrix * currentCorner );
    00489         }
    00490
    00502         void transformAffine(const Matrix4& m)
    00503         {
    00504             assert(m.isAffine());
    00505
    00506             // Do nothing if current null or infinite
    00507             if ( mExtent != EXTENT_FINITE )
    00508                 return;
    00509
    00510             Vector3 centre = getCenter();
    00511             Vector3 halfSize = getHalfSize();
    00512
    00513             Vector3 newCentre = m.transformAffine(centre);
    00514             Vector3 newHalfSize(
    00515                 Math::Abs(m[0][0]) * halfSize.x + Math::Abs(m[0][1]) * halfSize.y + Math::Abs(m[0][2]) * halfSize.z,
    00516                 Math::Abs(m[1][0]) * halfSize.x + Math::Abs(m[1][1]) * halfSize.y + Math::Abs(m[1][2]) * halfSize.z,
    00517                 Math::Abs(m[2][0]) * halfSize.x + Math::Abs(m[2][1]) * halfSize.y + Math::Abs(m[2][2]) * halfSize.z);
    00518
    00519             setExtents(newCentre - newHalfSize, newCentre + newHalfSize);
    00520         }
    00521
    00524         inline void setNull()
    00525         {
    00526             mExtent = EXTENT_NULL;
    00527         }
    00528
    00531         inline bool isNull(void) const
    00532         {
    00533             return (mExtent == EXTENT_NULL);
    00534         }
    00535
    00538         bool isFinite(void) const
    00539         {
    00540             return (mExtent == EXTENT_FINITE);
    00541         }
    00542
    00545         inline void setInfinite()
    00546         {
    00547             mExtent = EXTENT_INFINITE;
    00548         }
    00549
    00552         bool isInfinite(void) const
    00553         {
    00554             return (mExtent == EXTENT_INFINITE);
    00555         }
    00556
    00558         inline bool intersects(const AxisAlignedBox& b2) const
    00559         {
    00560             // Early-fail for nulls
    00561             if (this->isNull() || b2.isNull())
    00562                 return false;
    00563
    00564             // Early-success for infinites
    00565             if (this->isInfinite() || b2.isInfinite())
    00566                 return true;
    00567
    00568             // Use up to 6 separating planes
    00569             if (mMaximum.x < b2.mMinimum.x)
    00570                 return false;
    00571             if (mMaximum.y < b2.mMinimum.y)
    00572                 return false;
    00573             if (mMaximum.z < b2.mMinimum.z)
    00574                 return false;
    00575
    00576             if (mMinimum.x > b2.mMaximum.x)
    00577                 return false;
    00578             if (mMinimum.y > b2.mMaximum.y)
    00579                 return false;
    00580             if (mMinimum.z > b2.mMaximum.z)
    00581                 return false;
    00582
    00583             // otherwise, must be intersecting
    00584             return true;
    00585
    00586         }
    00587
    00589         inline AxisAlignedBox intersection(const AxisAlignedBox& b2) const
    00590         {
    00591             if (this->isNull() || b2.isNull())
    00592             {
    00593                 return AxisAlignedBox();
    00594             }
    00595             else if (this->isInfinite())
    00596             {
    00597                 return b2;
    00598             }
    00599             else if (b2.isInfinite())
    00600             {
    00601                 return *this;
    00602             }
    00603
    00604             Vector3 intMin = mMinimum;
    00605             Vector3 intMax = mMaximum;
    00606
    00607             intMin.makeCeil(b2.getMinimum());
    00608             intMax.makeFloor(b2.getMaximum());
    00609
    00610             // Check intersection isn't null
    00611             if (intMin.x < intMax.x &&
    00612                 intMin.y < intMax.y &&
    00613                 intMin.z < intMax.z)
    00614             {
    00615                 return AxisAlignedBox(intMin, intMax);
    00616             }
    00617
    00618             return AxisAlignedBox();
    00619         }
    00620
    00622         Real volume(void) const
    00623         {
    00624             switch (mExtent)
    00625             {
    00626             case EXTENT_NULL:
    00627                 return 0.0f;
    00628
    00629             case EXTENT_FINITE:
    00630                 {
    00631                     Vector3 diff = mMaximum - mMinimum;
    00632                     return diff.x * diff.y * diff.z;
    00633                 }
    00634
    00635             case EXTENT_INFINITE:
    00636                 return Math::POS_INFINITY;
    00637
    00638             default: // shut up compiler
    00639                 assert( false && "Never reached" );
    00640                 return 0.0f;
    00641             }
    00642         }
    00643
    00645         inline void scale(const Vector3& s)
    00646         {
    00647             // Do nothing if current null or infinite
    00648             if (mExtent != EXTENT_FINITE)
    00649                 return;
    00650
    00651             // NB assumes centered on origin
    00652             Vector3 min = mMinimum * s;
    00653             Vector3 max = mMaximum * s;
    00654             setExtents(min, max);
    00655         }
    00656
    00658         bool intersects(const Sphere& s) const
    00659         {
    00660             return Math::intersects(s, *this);
    00661         }
    00663         bool intersects(const Plane& p) const
    00664         {
    00665             return Math::intersects(p, *this);
    00666         }
    00668         bool intersects(const Vector3& v) const
    00669         {
    00670             switch (mExtent)
    00671             {
    00672             case EXTENT_NULL:
    00673                 return false;
    00674
    00675             case EXTENT_FINITE:
    00676                 return(v.x >= mMinimum.x  &&  v.x <= mMaximum.x  &&
    00677                     v.y >= mMinimum.y  &&  v.y <= mMaximum.y  &&
    00678                     v.z >= mMinimum.z  &&  v.z <= mMaximum.z);
    00679
    00680             case EXTENT_INFINITE:
    00681                 return true;
    00682
    00683             default: // shut up compiler
    00684                 assert( false && "Never reached" );
    00685                 return false;
    00686             }
    00687         }
    00689         Vector3 getCenter(void) const
    00690         {
    00691             assert( (mExtent == EXTENT_FINITE) && "Can't get center of a null or infinite AAB" );
    00692
    00693             return Vector3(
    00694                 (mMaximum.x + mMinimum.x) * 0.5,
    00695                 (mMaximum.y + mMinimum.y) * 0.5,
    00696                 (mMaximum.z + mMinimum.z) * 0.5);
    00697         }
    00699         Vector3 getSize(void) const
    00700         {
    00701             switch (mExtent)
    00702             {
    00703             case EXTENT_NULL:
    00704                 return Vector3::ZERO;
    00705
    00706             case EXTENT_FINITE:
    00707                 return mMaximum - mMinimum;
    00708
    00709             case EXTENT_INFINITE:
    00710                 return Vector3(
    00711                     Math::POS_INFINITY,
    00712                     Math::POS_INFINITY,
    00713                     Math::POS_INFINITY);
    00714
    00715             default: // shut up compiler
    00716                 assert( false && "Never reached" );
    00717                 return Vector3::ZERO;
    00718             }
    00719         }
    00721         Vector3 getHalfSize(void) const
    00722         {
    00723             switch (mExtent)
    00724             {
    00725             case EXTENT_NULL:
    00726                 return Vector3::ZERO;
    00727
    00728             case EXTENT_FINITE:
    00729                 return (mMaximum - mMinimum) * 0.5;
    00730
    00731             case EXTENT_INFINITE:
    00732                 return Vector3(
    00733                     Math::POS_INFINITY,
    00734                     Math::POS_INFINITY,
    00735                     Math::POS_INFINITY);
    00736
    00737             default: // shut up compiler
    00738                 assert( false && "Never reached" );
    00739                 return Vector3::ZERO;
    00740             }
    00741         }
    00742
    00745         bool contains(const Vector3& v) const
    00746         {
    00747             if (isNull())
    00748                 return false;
    00749             if (isInfinite())
    00750                 return true;
    00751
    00752             return mMinimum.x <= v.x && v.x <= mMaximum.x &&
    00753                    mMinimum.y <= v.y && v.y <= mMaximum.y &&
    00754                    mMinimum.z <= v.z && v.z <= mMaximum.z;
    00755         }
    00756
    00759         bool contains(const AxisAlignedBox& other) const
    00760         {
    00761             if (other.isNull() || this->isInfinite())
    00762                 return true;
    00763
    00764             if (this->isNull() || other.isInfinite())
    00765                 return false;
    00766
    00767             return this->mMinimum.x <= other.mMinimum.x &&
    00768                    this->mMinimum.y <= other.mMinimum.y &&
    00769                    this->mMinimum.z <= other.mMinimum.z &&
    00770                    other.mMaximum.x <= this->mMaximum.x &&
    00771                    other.mMaximum.y <= this->mMaximum.y &&
    00772                    other.mMaximum.z <= this->mMaximum.z;
    00773         }
    00774
    00777         bool operator== (const AxisAlignedBox& rhs) const
    00778         {
    00779             if (this->mExtent != rhs.mExtent)
    00780                 return false;
    00781
    00782             if (!this->isFinite())
    00783                 return true;
    00784
    00785             return this->mMinimum == rhs.mMinimum &&
    00786                    this->mMaximum == rhs.mMaximum;
    00787         }
    00788
    00791         bool operator!= (const AxisAlignedBox& rhs) const
    00792         {
    00793             return !(*this == rhs);
    00794         }
    00795
    00796         // special values
    00797         static const AxisAlignedBox BOX_NULL;
    00798         static const AxisAlignedBox BOX_INFINITE;
    00799
    00800
    00801     };
    00802
    00803 } // namespace Ogre
    00804
    00805 #endif

Copyright © 2008 Torus Knot Software Ltd
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.
Last modified Sun Jul 26 11:32:16 2009