src/thirdparty/particles/pDomain.h

Go to the documentation of this file.
00001 #ifndef PDOMAIN_H_INCLUDED
00002 #define PDOMAIN_H_INCLUDED
00003 
00004 #include "pVec.h"
00005 
00006 class pDomain
00007 {
00008 public:
00009     virtual bool Within(const pVec &) const = 0;
00010     virtual pVec Generate() const = 0;
00011 
00012     virtual pDomain *copy() const = 0; // Returns a pointer to a heap-allocated copy of the derived class
00013 
00014     virtual ~pDomain() {}
00015 };
00016 
00017 // Single point
00018 class PDPoint : public pDomain
00019 {
00020 public:
00021     pVec p;
00022 
00023 public:
00024     PDPoint(const pVec &p0)
00025     {
00026         p = p0;
00027     }
00028 
00029     ~PDPoint()
00030     {
00031     }
00032 
00033     bool Within(const pVec &pos) const
00034     {
00035         return false;
00036     }
00037 
00038     pVec Generate() const
00039     {
00040         return p;
00041     }
00042 
00043     pDomain *copy() const
00044     {
00045         PDPoint *P = new PDPoint(*this);
00046         return P;
00047     }
00048 };
00049 
00050 // Line segment
00051 class PDLine : public pDomain
00052 {
00053 public:
00054     pVec p0, p1;
00055 
00056 public:
00057     PDLine(const pVec &e0, const pVec &e1)
00058     {
00059         p0 = e0;
00060         p1 = e1 - e0;
00061     }
00062 
00063     ~PDLine()
00064     {
00065     }
00066 
00067     bool Within(const pVec &pos) const
00068     {
00069         return false;
00070     }
00071 
00072     pVec Generate() const
00073     {
00074         return p0 + p1 * pRandf();
00075     }
00076 
00077     pDomain *copy() const
00078     {
00079         PDLine *P = new PDLine(*this);
00080         return P;
00081     }
00082 };
00083 
00084 // Triangle
00085 class PDTriangle : public pDomain
00086 {
00087 public:
00088     pVec p, u, v, uNrm, vNrm, nrm;
00089     float uLen, vLen, D;
00090 
00091 public:
00092     PDTriangle(const pVec &p0, const pVec &p1, const pVec &p2)
00093     {
00094         p = p0;
00095         u = p1 - p0;
00096         v = p2 - p0;
00097 
00098         // The rest of this is needed for Avoid and Bounce.
00099         uLen = u.length();
00100         uNrm = u / uLen;
00101         vLen = v.length();
00102         vNrm = v / vLen;
00103 
00104         nrm = Cross(uNrm, vNrm); // This is the non-unit normal.
00105         nrm.normalize(); // Must normalize it.
00106 
00107         D = -(p * nrm);
00108     }
00109 
00110     ~PDTriangle()
00111     {
00112     }
00113 
00114     bool Within(const pVec &pos) const
00115     {
00116         return false;
00117     }
00118 
00119     pVec Generate() const
00120     {
00121         float r1 = pRandf();
00122         float r2 = pRandf();
00123         pVec pos;
00124         if(r1 + r2 < 1.0f)
00125             pos = p + u * r1 + v * r2;
00126         else
00127             pos = p + u * (1.0f-r1) + v * (1.0f-r2);
00128 
00129         return pos;
00130     }
00131 
00132     pDomain *copy() const
00133     {
00134         PDTriangle *P = new PDTriangle(*this);
00135         return P;
00136     }
00137 };
00138 
00139 // Rhombus-shaped planar region
00140 class PDRectangle : public pDomain
00141 {
00142 public:
00143     pVec p, u, v, uNrm, vNrm, nrm;
00144     float uLen, vLen, D;
00145 
00146 public:
00147     PDRectangle(const pVec &p0, const pVec &u0, const pVec &v0)
00148     {
00149         p = p0;
00150         u = u0;
00151         v = v0;
00152 
00153         // The rest of this is needed for Avoid and Bounce.
00154         uLen = u.length();
00155         uNrm = u / uLen;
00156         vLen = v.length();
00157         vNrm = v / vLen;
00158 
00159         nrm = Cross(uNrm, vNrm); // This is the non-unit normal.
00160         nrm.normalize(); // Must normalize it.
00161 
00162         D = -(p * nrm);
00163     }
00164 
00165     ~PDRectangle()
00166     {
00167         // std::cerr << "del " << typeid(*this).name() << this << std::endl;
00168     }
00169 
00170     bool Within(const pVec &pos) const
00171     {
00172         return false;
00173     }
00174 
00175     pVec Generate() const
00176     {
00177         pVec pos = p + u * pRandf() + v * pRandf();
00178         return pos;
00179     }
00180 
00181     pDomain *copy() const
00182     {
00183         PDRectangle *P = new PDRectangle(*this);
00184         return P;
00185     }
00186 };
00187 
00188 // Arbitrarily-oriented plane
00189 class PDPlane : public pDomain
00190 {
00191 public:
00192     pVec p, nrm;
00193     float D;
00194 
00195 public:
00196     PDPlane(const pVec &p0, const pVec &nrm0)
00197     {
00198         p = p0;
00199         nrm = nrm0;
00200         nrm.normalize(); // Must normalize it.
00201         D = -(p * nrm);
00202     }
00203 
00204     ~PDPlane()
00205     {
00206     }
00207 
00208     // Distance from plane = n * p + d
00209     // Inside is the positive half-space.
00210     bool Within(const pVec &pos) const
00211     {
00212         return nrm * pos >= -D;
00213     }
00214 
00215     // How do I sensibly make a point on an infinite plane?
00216     pVec Generate() const
00217     {
00218         return p;
00219     }
00220 
00221     pDomain *copy() const
00222     {
00223         PDPlane *P = new PDPlane(*this);
00224         return P;
00225     }
00226 };
00227 
00228 // Axis-aligned box
00229 class PDBox : public pDomain
00230 {
00231 public:
00232     // p0 is the min corner. p1 is the max corner.
00233     pVec p0, p1, dif;
00234 
00235 public:
00236     PDBox(const pVec &e0, const pVec &e1)
00237     {
00238         p0 = e0;
00239         p1 = e1;
00240         if(e1.x() < e0.x()) { p0.x() = e1.x(); p1.x() = e1.x(); }
00241         if(e1.y() < e0.y()) { p0.y() = e1.y(); p1.y() = e1.y(); }
00242         if(e1.z() < e0.z()) { p0.z() = e1.z(); p1.z() = e1.z(); }
00243 
00244         dif = p1 - p0;
00245     }
00246 
00247     ~PDBox()
00248     {
00249     }
00250 
00251     bool Within(const pVec &pos) const
00252     {
00253         return !((pos.x() < p0.x()) || (pos.x() > p1.x()) ||
00254                  (pos.y() < p0.y()) || (pos.y() > p1.y()) ||
00255                  (pos.z() < p0.z()) || (pos.z() > p1.z()));
00256     }
00257 
00258     pVec Generate() const
00259     {
00260         // Scale and translate [0,1] random to fit box
00261         return p0 + CompMult(pRandVec(), dif);
00262     }
00263 
00264     pDomain *copy() const
00265     {
00266         PDBox *P = new PDBox(*this);
00267         return P;
00268     }
00269 };
00270 
00271 // Cylinder
00272 class PDCylinder : public pDomain
00273 {
00274 public:
00275     pVec apex, axis, u, v; // Apex is one end. Axis is vector from one end to the other.
00276     float len, radOut, radIn, radOutSqr, radInSqr, radDif, axisLenInvSqr;
00277     bool ThinShell;
00278 
00279 public:
00280     PDCylinder(const pVec &e0, const pVec &e1, const float radOut0, const float radIn0 = 0.0f)
00281     {
00282         apex = e0;
00283         axis = e1 - e0;
00284 
00285         if(radOut0 < radIn0) {
00286             radOut = radIn0; radIn = radOut0;
00287         } else {
00288             radOut = radOut0; radIn = radIn0;
00289         }
00290         radOutSqr = fsqr(radOut);
00291         radInSqr = fsqr(radIn);
00292 
00293         ThinShell = (radIn == radOut);
00294         radDif = radOut - radIn;
00295 
00296         // Given an arbitrary nonzero vector n, make two orthonormal
00297         // vectors u and v forming a frame [u,v,n.normalize()].
00298         pVec n = axis;
00299         float axisLenSqr = axis.length2();
00300         axisLenInvSqr = axisLenSqr ? (1.0f / axisLenSqr) : 0.0f;
00301         n *= sqrtf(axisLenInvSqr);
00302 
00303         // Find a vector orthogonal to n.
00304         pVec basis(1.0f, 0.0f, 0.0f);
00305         if (fabsf(basis * n) > 0.999f)
00306             basis = pVec(0.0f, 1.0f, 0.0f);
00307 
00308         // Project away N component, normalize and cross to get
00309         // second orthonormal vector.
00310         u = basis - n * (basis * n);
00311         u.normalize();
00312         v = Cross(n, u);
00313     }
00314 
00315     ~PDCylinder()
00316     {
00317     }
00318 
00319     bool Within(const pVec &pos) const
00320     {
00321         // This is painful and slow. Might be better to do quick accept/reject tests.
00322         // Axis is vector from base to tip of the cylinder.
00323         // x is vector from base to pos.
00324         //         x . axis
00325         // dist = ---------- = projected distance of x along the axis
00326         //        axis. axis   ranging from 0 (base) to 1 (tip)
00327         //
00328         // rad = x - dist * axis = projected vector of x along the base
00329 
00330         pVec x = pos - apex;
00331 
00332         // Check axial distance
00333         float dist = (axis * x) * axisLenInvSqr;
00334         if(dist < 0.0f || dist > 1.0f)
00335             return false;
00336 
00337         // Check radial distance
00338         pVec xrad = x - axis * dist; // Radial component of x
00339         float rSqr = xrad.length2();
00340 
00341         return (rSqr <= radInSqr && rSqr >= radOutSqr);
00342     }
00343 
00344     pVec Generate() const
00345     {
00346         float dist = pRandf(); // Distance between base and tip
00347         float theta = pRandf() * 2.0f * float(M_PI); // Angle around axis
00348         // Distance from axis
00349         float r = radIn + pRandf() * radDif;
00350 
00351         // Another way to do this is to choose a random point in a square and keep it if it's in the circle.
00352         float x = r * cosf(theta);
00353         float y = r * sinf(theta);
00354 
00355         pVec pos = apex + axis * dist + u * x + v * y;
00356         return pos;
00357     }
00358 
00359     pDomain *copy() const
00360     {
00361         PDCylinder *P = new PDCylinder(*this);
00362         return P;
00363     }
00364 };
00365 
00366 // Cone
00367 class PDCone : public pDomain
00368 {
00369 public:
00370     pVec apex, axis, u, v; // Apex is one end. Axis is vector from one end to the other.
00371     float len, radOut, radIn, radOutSqr, radInSqr, radDif, axisLenInvSqr;
00372     bool ThinShell;
00373 
00374 public:
00375     PDCone(const pVec &e0, const pVec &e1, const float radOut0, const float radIn0 = 0.0f)
00376     {
00377         apex = e0;
00378         axis = e1 - e0;
00379 
00380         if(radOut0 < radIn0) {
00381             radOut = radIn0; radIn = radOut0;
00382         } else {
00383             radOut = radOut0; radIn = radIn0;
00384         }
00385         radOutSqr = fsqr(radOut);
00386         radInSqr = fsqr(radIn);
00387 
00388         ThinShell = (radIn == radOut);
00389         radDif = radOut - radIn;
00390 
00391         // Given an arbitrary nonzero vector n, make two orthonormal
00392         // vectors u and v forming a frame [u,v,n.normalize()].
00393         pVec n = axis;
00394         float axisLenSqr = axis.length2();
00395         axisLenInvSqr = axisLenSqr ? 1.0f / axisLenSqr : 0.0f;
00396         n *= sqrtf(axisLenInvSqr);
00397 
00398         // Find a vector orthogonal to n.
00399         pVec basis(1.0f, 0.0f, 0.0f);
00400         if (fabsf(basis * n) > 0.999f)
00401             basis = pVec(0.0f, 1.0f, 0.0f);
00402 
00403         // Project away N component, normalize and cross to get
00404         // second orthonormal vector.
00405         u = basis - n * (basis * n);
00406         u.normalize();
00407         v = Cross(n, u);
00408     }
00409 
00410     ~PDCone()
00411     {
00412     }
00413 
00414     bool Within(const pVec &pos) const
00415     {
00416         // This is painful and slow. Might be better to do quick
00417         // accept/reject tests.
00418         // Let axis = vector from base to tip of the cylinder
00419         // x = vector from base to test point
00420         //         x . axis
00421         // dist = ---------- = projected distance of x along the axis
00422         //        axis. axis   ranging from 0 (base) to 1 (tip)
00423         //
00424         // rad = x - dist * axis = projected vector of x along the base
00425 
00426         pVec x = pos - apex;
00427 
00428         // Check axial distance
00429         // axisLenInvSqr stores 1 / (axis.axis)
00430         float dist = (axis * x) * axisLenInvSqr;
00431         if(dist < 0.0f || dist > 1.0f)
00432             return false;
00433 
00434         // Check radial distance; scale radius along axis for cones
00435         pVec xrad = x - axis * dist; // Radial component of x
00436         float rSqr = xrad.length2();
00437 
00438         return (rSqr <= fsqr(dist * radIn) && rSqr >= fsqr(dist * radOut));
00439     }
00440 
00441     pVec Generate() const
00442     {
00443         float dist = pRandf(); // Distance between base and tip
00444         float theta = pRandf() * 2.0f * float(M_PI); // Angle around axis
00445         // Distance from axis
00446         float r = radIn + pRandf() * radDif;
00447 
00448         // Another way to do this is to choose a random point in a square and keep it if it's in the circle.
00449         float x = r * cosf(theta);
00450         float y = r * sinf(theta);
00451 
00452         // Scale radius along axis for cones
00453         x *= dist;
00454         y *= dist;
00455 
00456         pVec pos = apex + axis * dist + u * x + v * y;
00457         return pos;
00458     }
00459 
00460     pDomain *copy() const
00461     {
00462         PDCone *P = new PDCone(*this);
00463         return P;
00464     }
00465 };
00466 
00467 // Sphere
00468 class PDSphere : public pDomain
00469 {
00470 public:
00471     pVec ctr;
00472     float radOut, radIn, radOutSqr, radInSqr, radDif;
00473     bool ThinShell;
00474 
00475 public:
00476     PDSphere(const pVec &ctr0, const float radOut0, const float radIn0 = 0.0f)
00477     {
00478         ctr = ctr0;
00479         if(radOut0 < radIn0) {
00480             radOut = radIn0; radIn = radOut0;
00481         } else {
00482             radOut = radOut0; radIn = radIn0;
00483         }
00484         if(radIn < 0.0f) radIn = 0.0f;
00485 
00486         radOutSqr = fsqr(radOut);
00487         radInSqr = fsqr(radIn);
00488 
00489         ThinShell = (radIn == radOut);
00490         radDif = radOut - radIn;
00491     }
00492 
00493     ~PDSphere()
00494     {
00495     }
00496 
00497     bool Within(const pVec &pos) const
00498     {
00499         pVec rvec(pos - ctr);
00500         float rSqr = rvec.length2();
00501         return rSqr <= radOutSqr && rSqr >= radInSqr;
00502     }
00503 
00504     pVec Generate() const
00505     {
00506         pVec pos;
00507 
00508         do {
00509             pos = pRandVec() - vHalf; // Point on [-0.5,0.5] box
00510         } while (pos.length2() > fsqr(0.5)); // Make sure it's also on r=0.5 sphere.
00511         pos.normalize(); // Now it's on r=1 spherical shell
00512 
00513         // Scale unit sphere pos by [0..r] and translate
00514         if(ThinShell)
00515             pos = ctr + pos * radOut;
00516         else
00517             pos = ctr + pos * (radIn + pRandf() * radDif);
00518 
00519         return pos;
00520     }
00521 
00522     pDomain *copy() const
00523     {
00524         PDSphere *P = new PDSphere(*this);
00525         return P;
00526     }
00527 };
00528 
00529 // Gaussian blob
00530 class PDBlob : public pDomain
00531 {
00532 public:
00533     pVec ctr;
00534     float stdev, Scale1, Scale2;
00535 
00536 public:
00537     PDBlob(const pVec &ctr0, const float stdev0)
00538     {
00539         ctr = ctr0;
00540         stdev = stdev0;
00541         float oneOverSigma = 1.0f/(stdev+0.000000000001f);
00542         Scale1 = -0.5f*fsqr(oneOverSigma);
00543         Scale2 = P_ONEOVERSQRT2PI * oneOverSigma;
00544     }
00545 
00546     ~PDBlob()
00547     {
00548     }
00549 
00550     bool Within(const pVec &pos) const
00551     {
00552         pVec x = pos - ctr;
00553         // return exp(-0.5 * xSq * Sqr(oneOverSigma)) * P_ONEOVERSQRT2PI * oneOverSigma;
00554         float Gx = expf(x.length2() * Scale1) * Scale2;
00555         return (pRandf() < Gx);
00556     }
00557 
00558     pVec Generate() const
00559     {
00560         return ctr + pNRandVec(stdev);
00561     }
00562 
00563     pDomain *copy() const
00564     {
00565         PDBlob *P = new PDBlob(*this);
00566         return P;
00567     }
00568 };
00569 
00570 // Arbitrarily-oriented disc
00571 class PDDisc : public pDomain
00572 {
00573 public:
00574     pVec p, nrm, u, v;
00575     float radIn, radOut, radInSqr, radOutSqr, dif, D;
00576 
00577 public:
00578     PDDisc(const pVec &ctr0, const pVec nrm0, const float radOut0, const float radIn0 = 0.0f)
00579     {
00580         p = ctr0;
00581         nrm = nrm0;
00582         nrm.normalize();
00583 
00584         if(radOut0 > radIn0) {
00585             radOut = radOut0; radIn = radIn0;
00586         } else {
00587             radOut = radIn0; radIn = radOut0;
00588         }
00589         dif = radOut - radIn;
00590         radInSqr = fsqr(radIn);
00591         radOutSqr = fsqr(radOut);
00592 
00593         // Find a vector orthogonal to n.
00594         pVec basis(1.0f, 0.0f, 0.0f);
00595         if (fabsf(basis * nrm) > 0.999f)
00596             basis = pVec(0.0f, 1.0f, 0.0f);
00597 
00598         // Project away N component, normalize and cross to get
00599         // second orthonormal vector.
00600         u = basis - nrm * (basis * nrm);
00601         u.normalize();
00602         v = Cross(nrm, u);
00603         D = -(p * nrm);
00604     }
00605 
00606     ~PDDisc()
00607     {
00608     }
00609 
00610     bool Within(const pVec &pos) const
00611     {
00612         return false;
00613     }
00614 
00615     pVec Generate() const
00616     {
00617         // Might be faster to generate a point in a square and reject if outside the circle
00618         float theta = pRandf() * 2.0f * float(M_PI); // Angle around normal
00619         // Distance from center
00620         float r = radIn + pRandf() * dif;
00621 
00622         float x = r * cosf(theta); // Weighting of each frame vector
00623         float y = r * sinf(theta);
00624 
00625         pVec pos = p + u * x + v * y;
00626         return pos;
00627     }
00628 
00629     pDomain *copy() const
00630     {
00631         PDDisc *P = new PDDisc(*this);
00632         return P;
00633     }
00634 };
00635 
00636 #endif // PDOMAIN_H_INCLUDED

Generated on Sat Mar 15 22:55:58 2008 for Armagetron Advanced by  doxygen 1.5.4