ParticleAPI 3.0.0
Performant particle system API in C++ for interactive graphics
pDomain.h
Go to the documentation of this file.
1/// PDomain.h
2///
3/// Copyright 1997-2007, 2022 by David K. McAllister
4///
5/// This file defines the pDomain struct and all of the classes that derive from it.
6
7#ifndef pdomain_h
8#define pdomain_h
9
10#include "Particle/pDeclarations.h"
11#include "Particle/pError.h"
12#include "Particle/pVec.h"
13
14#include <string>
15#include <vector>
16
17namespace PAPI {
18///< How small the dot product must be to declare that a point is in a plane for Within().
19#define P_PLANAR_EPSILON 1e-3f
20
21/// Enums for the different types of domains that can be stored in a pDomain
35};
36
37/// A representation of a region of space.
38///
39/// A Domain is a representation of a region of space. For example, the Source action uses a domain to describe the volume in which a particle
40/// will be created. A random point within the domain is chosen as the initial position of the particle. The Avoid, Sink and Bounce actions,
41/// for example, use domains to describe a volume in space for particles to steer around, die when they enter, or bounce off, respectively.
42///
43/// Domains can be used to describe velocities. Picture the velocity vector as having its tail at the origin and its tip being in the domain.
44/// Domains can be used to describe colors in any three-valued color space. They can be used to describe three-valued sizes, such as Length, Width, Height.
45///
46/// Several types of domains can be specified, such as points, lines, planes, discs, spheres, gaussian blobs, etc. Each subclass of the pDomain
47/// struct represents a different kind of domain.
48///
49/// All domains support two basic operations. The first is Generate, which returns a random point in the domain.
50///
51/// The second basic operation is Within, which tells whether a given point is within the domain.
52///
53/// The application programmer never calls the Generate or Within functions. The application will use the pDomain struct and its derivatives solely
54/// as a way to communicate the domain to the API. The API's action commands will then perform operations on the domain, such as generating particles within it.
55class pDomain {
56public:
57#define P_N_FLOATS_IN_DOMAIN 30
59 virtual bool Within(const pVec&) const = 0; ///< Returns true if the given point is within the domain.
60 virtual pVec Generate() const = 0; ///< Returns a random point in the domain.
61 virtual float Size() const = 0; ///< Returns the size of the domain (length, area, or volume).
62
63 virtual std::shared_ptr<pDomain> copy() const = 0; // Returns a pointer to a heap-allocated copy of the derived class
64};
65
66namespace {
67// Compute the inverse matrix of the plane basis.
68PINLINE void NewBasis(const pVec& u, const pVec& v, pVec& s1, pVec& s2)
69{
70 pVec w = Cross(u, v);
71
72 float det = 1.0f /
73 (w.z() * u.x() * v.y() - w.z() * u.y() * v.x() - u.z() * w.x() * v.y() - u.x() * v.z() * w.y() + v.z() * w.x() * u.y() + u.z() * v.x() * w.y());
74
75 s1 = pVec((v.y() * w.z() - v.z() * w.y()), (v.z() * w.x() - v.x() * w.z()), (v.x() * w.y() - v.y() * w.x()));
76 s1 *= det;
77 s2 = pVec((u.y() * w.z() - u.z() * w.y()), (u.z() * w.x() - u.x() * w.z()), (u.x() * w.y() - u.y() * w.x()));
78 s2 *= -det;
79}
80}; // namespace
81
82/// A CSG union of multiple domains.
83///
84/// A point is within this domain if it is within any subdomain.
85/// Generate returns a point from any subdomain.
86/// Within returns true if pos is within any subdomain.
87///
88/// All domains have a Size() that is used to apportion probability between the domains in the union.
89/// Sizes of domains of the same dimensionality are commensurate but sizes of differing dimensionality are not.
90/// Thus, to properly distribute probability of Generate() choosing each domain, it is wise to only combine domains that have the same
91/// dimensionality. Note that thin shelled cylinders, cones, and spheres, where InnerRadius==OuterRadius, are considered 2D, not 3D.
92/// Thin shelled discs (circles) are considered 1D. Points are 0D.
93struct PDUnion : public pDomain {
94 std::vector<std::shared_ptr<pDomain>> Doms;
95 float TotalSize;
96
97 PDUnion() /// Use this one to create an empty PDUnion then call .insert() to add each item to it.
98 {
100 TotalSize = 0.0f;
101 }
102
103 PDUnion(const pDomain& A, const pDomain& B)
104 {
107 Doms.push_back(A.copy());
108 Doms.push_back(B.copy());
109 }
110
111 PDUnion(const pDomain& A, const pDomain& B, const pDomain& C)
112 {
115 Doms.push_back(A.copy());
116 Doms.push_back(B.copy());
117 Doms.push_back(C.copy());
118 }
119
120 /// Makes a copy of all the subdomains and point to the copies.
121 ///
122 /// Note that the Generate() function goes faster if you supply DomList with the largest domains first.
123 PDUnion(const std::vector<std::shared_ptr<pDomain>>& DomList)
124 {
126 TotalSize = 0.0f;
127 for (std::vector<std::shared_ptr<pDomain>>::const_iterator it = DomList.begin(); it != DomList.end(); it++) {
128 Doms.push_back((*it)->copy());
129 TotalSize += (*it)->Size();
130 }
131 }
132
133 /// Makes a copy of all the subdomains and point to the copies.
134 PDUnion(const PDUnion& P)
135 {
137 TotalSize = 0.0f;
138 for (std::vector<std::shared_ptr<pDomain>>::const_iterator it = P.Doms.begin(); it != P.Doms.end(); it++) {
139 Doms.push_back((*it)->copy());
140 TotalSize += (*it)->Size();
141 }
142 }
143
144 /// Insert another domain into this PDUnion.
145 void insert(const pDomain& A)
146 {
147 TotalSize += A.Size();
148 Doms.push_back(A.copy());
149 }
150
151 bool Within(const pVec& pos) const /// Returns true if pos is within any of the domains.
152 {
153 for (std::vector<std::shared_ptr<pDomain>>::const_iterator it = Doms.begin(); it != Doms.end(); it++)
154 if ((*it)->Within(pos)) return true;
155 return false;
156 }
157
158 pVec Generate() const /// Generate a point in any subdomain, chosen by the ratio of their sizes.
159 {
160 float Choose = pRandf() * TotalSize, PastProb = 0.0f;
161 for (std::vector<std::shared_ptr<pDomain>>::const_iterator it = Doms.begin(); it != Doms.end(); it++) {
162 PastProb += (*it)->Size();
163 if (Choose <= PastProb) return (*it)->Generate();
164 }
165 throw PErrInternalError("Sizes didn't add up to TotalSize in PDUnion::Generate().");
166 }
167
168 float Size() const { return TotalSize; }
169
170 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDUnion(*this)); }
171};
172
173/// A single point.
174///
175/// Generate always returns this point. Within returns true if the point is exactly equal.
176struct PDPoint : public pDomain {
178
180 {
183 }
184
185 PINLINE void PDPoint_Cons(const pVec& p0) { p = p0; }
186
187 PINLINE bool Within(const pVec& pos) const /// Returns true if the point is exactly equal.
188 {
189 return p == pos;
190 }
191
192 PINLINE pVec Generate() const /// Returns the point
193 {
194 return p;
195 }
196
197 PINLINE float Size() const { return 1.0f; }
198
199 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDPoint(*this)); }
200};
201
202/// A line segment.
203///
204/// e0 and e1 are the endpoints of the segment.
205///
206/// Generate returns a random point on this segment. Within returns true for points within epsilon of the line segment.
207struct PDLine : public pDomain {
209 float len;
210
211 PINLINE PDLine(const pVec& e0, const pVec& e1)
212 {
213 Which = PDLine_e;
214 PDLine_Cons(e0, e1);
215 }
216
217 PINLINE void PDLine_Cons(const pVec& e0, const pVec& e1)
218 {
219 p0 = e0;
220 p1 = e1;
221 vec = e1 - e0;
222 vecNrm = vec;
225 }
226
227 PINLINE bool Within(const pVec& pos) const /// Returns true for points within epsilon of the line segment.
228 {
229 pVec to = pos - p0;
230 float d = dot(vecNrm, to);
231 float dif = fabs(d - to.length()) / len; // Has a sqrt(). Kind of slow.
232 return dif < 1e-7f; // It's inaccurate, so we need this epsilon.
233 }
234
235 PINLINE pVec Generate() const /// Returns a random point on this segment.
236 {
237 return p0 + vec * pRandf();
238 }
239
240 PINLINE float Size() const { return len; }
241
242 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDLine(*this)); }
243};
244
245/// A Triangle.
246///
247/// p0, p1, and p2 are the vertices of the triangle. The triangle can be used to define an arbitrary geometrical model for particles to
248/// bounce off, or generate particles on its surface (and explode them), etc.
249///
250/// Generate returns a random point in the triangle. Within returns true for points within epsilon of the triangle. Currently it is not
251/// possible to sink particles that enter/exit a polygonal model. Suggestions?
252struct PDTriangle : public pDomain {
254 float uLen, vLen, D, area;
255
256 PINLINE PDTriangle(const pVec& p0, const pVec& p1, const pVec& p2)
257 {
259 PDTriangle_Cons(p0, p1, p2);
260 }
261
262 PINLINE void PDTriangle_Cons(const pVec& p0, const pVec& p1, const pVec& p2)
263 {
264 p = p0;
265 u = p1 - p0;
266 v = p2 - p0;
267
269 uNrm = u / uLen;
271 vNrm = v / vLen;
272
273 nrm = Cross(uNrm, vNrm); // This is the non-unit normal.
274 nrm.normalize(); // Must normalize it.
275
276 D = -dot(p, nrm);
277
278 NewBasis(u, v, s1, s2); // Compute the inverse matrix of the plane basis.
279
280 // Compute the area
281 pVec Hgt = v - uNrm * dot(uNrm, v);
282 float h = Hgt.length();
283 area = 0.5f * uLen * h;
284 }
285
286 PINLINE bool Within(const pVec& pos) const /// Returns true for points within epsilon of the triangle.
287 {
288 pVec offset = pos - p;
289 float d = dot(offset, nrm);
290
291 if (d > P_PLANAR_EPSILON) return false;
292
293 // Dot product with basis vectors of old frame
294 // in terms of new frame gives position in uv frame.
295 float upos = dot(offset, s1);
296 float vpos = dot(offset, s2);
297
298 // Did it cross plane outside triangle?
299 return !(upos < 0 || vpos < 0 || (upos + vpos) > 1);
300 }
301
302 PINLINE pVec Generate() const /// Returns a random point in the triangle.
303 {
304 float r1 = pRandf();
305 float r2 = pRandf();
306 pVec pos;
307 if (r1 + r2 < 1.0f)
308 pos = p + u * r1 + v * r2;
309 else
310 pos = p + u * (1.0f - r1) + v * (1.0f - r2);
311
312 return pos;
313 }
314
315 PINLINE float Size() const { return area; }
316
317 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDTriangle(*this)); }
318};
319
320/// Rhombus-shaped planar region.
321///
322/// p0 is a point on the plane. u0 and v0 are (non-parallel) basis vectors in the plane. They don't need to be normal or orthogonal.
323///
324/// Generate returns a random point in the diamond-shaped patch whose corners are o, o+u, o+u+v, and o+v. Within returns true for points within epsilon of the patch.
325struct PDRectangle : public pDomain {
327 float uLen, vLen, D, area;
328
329 PINLINE PDRectangle(const pVec& p0, const pVec& u0, const pVec& v0)
330 {
332 PDRectangle_Cons(p0, u0, v0);
333 }
334
335 PINLINE void PDRectangle_Cons(const pVec& p0, const pVec& u0, const pVec& v0)
336 {
337 p = p0;
338 u = u0;
339 v = v0;
340
342 uNrm = u / uLen;
344 vNrm = v / vLen;
345
346 nrm = Cross(uNrm, vNrm); // This is the non-unit normal.
347 nrm.normalize(); // Must normalize it.
348
349 D = -dot(p, nrm);
350
351 NewBasis(u, v, s1, s2); // Compute the inverse matrix of the plane basis.
352
353 // Compute the area
354 pVec Hgt = v - uNrm * dot(uNrm, v);
355 float h = Hgt.length();
356 area = uLen * h;
357 }
358
359 PINLINE bool Within(const pVec& pos) const /// Returns true for points within epsilon of the patch.
360 {
361 pVec offset = pos - p;
362 float d = dot(offset, nrm);
363
364 if (d > P_PLANAR_EPSILON) return false;
365
366 // Dot product with basis vectors of old frame
367 // in terms of new frame gives position in uv frame.
368 float upos = dot(offset, s1);
369 float vpos = dot(offset, s2);
370
371 // Did it cross plane outside triangle?
372 return !(upos < 0 || upos > 1 || vpos < 0 || vpos > 1);
373 }
374
375 PINLINE pVec Generate() const /// Returns a random point in the diamond-shaped patch whose corners are o, o+u, o+u+v, and o+v.
376 {
377 pVec pos = p + u * pRandf() + v * pRandf();
378 return pos;
379 }
380
381 PINLINE float Size() const { return area; }
382
383 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDRectangle(*this)); }
384};
385
386/// Arbitrarily-oriented disc
387///
388/// The point Center is the center of a disc in the plane with normal Normal. The disc has an OuterRadius. If InnerRadius is greater than 0,
389/// the domain is a flat washer, rather than a disc. The normal will get normalized, so it need not already be unit length.
390///
391/// Generate returns a point inside the disc shell. Within returns true for points within epsilon of the disc.
392struct PDDisc : public pDomain {
395
396 PINLINE PDDisc(const pVec& Center, const pVec Normal, const float OuterRadius, const float InnerRadius = 0.0f)
397 {
398 if (InnerRadius < 0 || OuterRadius < 0) throw PErrInvalidValue("Can't have negative radius.");
399
400 Which = PDDisc_e;
401 PDDisc_Cons(Center, Normal, OuterRadius, InnerRadius);
402 }
403
404 PINLINE void PDDisc_Cons(const pVec& Center, const pVec Normal, const float OuterRadius, const float InnerRadius = 0.0f)
405 {
406 p = Center;
407 nrm = Normal;
409
410 if (OuterRadius > InnerRadius) {
411 radOut = OuterRadius;
412 radIn = InnerRadius;
413 } else {
414 radOut = InnerRadius;
415 radIn = OuterRadius;
416 }
417
420 dif = radOut - radIn;
421
422 // Find a vector orthogonal to n.
423 pVec basis = pVec(1.0f, 0.0f, 0.0f);
424 if (fabsf(dot(basis, nrm)) > 0.999f) basis = pVec(0.0f, 1.0f, 0.0f);
425
426 // Project away N component, normalize and cross to get
427 // second orthonormal vector.
428 u = basis - nrm * dot(basis, nrm);
430 v = Cross(nrm, u);
431 D = -dot(p, nrm);
432
433 if (dif == 0.0f)
434 area = 2.0f * M_PI * radOut; // Length of circle
435 else
436 area = M_PI * radOutSqr - M_PI * radInSqr; // Area or disc minus hole
437 }
438
439 PINLINE bool Within(const pVec& pos) const /// Returns true for points within epsilon of the disc.
440 {
441 pVec offset = pos - p;
442 float d = dot(offset, nrm);
443
444 if (d > P_PLANAR_EPSILON) return false;
445
446 float len = offset.lenSqr();
447 return len >= radInSqr && len <= radOutSqr;
448 }
449
450 PINLINE pVec Generate() const /// Returns a point inside the disc shell.
451 {
452 // Might be faster to generate a point in a square and reject if outside the circle
453 float theta = pRandf() * 2.0f * float(M_PI); // Angle around normal
454 // Distance from center
455 float r = radIn + pRandf() * dif;
456
457 float x = r * cosf(theta); // Weighting of each frame vector
458 float y = r * sinf(theta);
459
460 pVec pos = p + u * x + v * y;
461 return pos;
462 }
463
464 PINLINE float Size() const
465 {
466 return 1.0f; // A plane is infinite, so what sensible thing can I return?
467 }
468
469 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDDisc(*this)); }
470};
471
472/// Arbitrarily-oriented plane.
473///
474/// The point p0 is a point on the plane. Normal is the normal vector of the plane. If you have a plane in a,b,c,d form remember that
475/// n = [a,b,c] and you can compute a suitable point p0 as p0 = -n*d. The normal will get normalized, so it need not already be unit length.
476///
477/// Generate returns the point p0. Within returns true if the point is in the positive half-space of the plane (in the plane or on the side that Normal points to).
478struct PDPlane : public pDomain {
480 float D;
481
482 PINLINE PDPlane(const pVec& p0, const pVec& Normal)
483 {
485 PDPlane_Cons(p0, Normal);
486 }
487
488 PINLINE void PDPlane_Cons(const pVec& p0, const pVec& Normal)
489 {
490 p = p0;
491 nrm = Normal;
492 nrm.normalize(); // Must normalize it.
493 D = -dot(p, nrm);
494 }
495
496 // Distance from plane = n * p + d
497 // Inside is the positive half-space.
498 PINLINE bool
499 Within(const pVec& pos) const /// Returns true if the point is in the positive half-space of the plane (in the plane or on the side that Normal points to).
500 {
501 return dot(nrm, pos) >= -D;
502 }
503
504 // How do I sensibly make a point on an infinite plane?
505 PINLINE pVec Generate() const /// Returns the point p0.
506 {
507 return p;
508 }
509
510 PINLINE float Size() const
511 {
512 return 1.0f; // A plane is infinite, so what sensible thing can I return?
513 }
514
515 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDPlane(*this)); }
516};
517
518/// Axis-aligned bounding box (AABB)
519///
520/// e0 and e1 are opposite corners of an axis-aligned box. It doesn't matter which of each coordinate is min and which is max.
521///
522/// Generate returns a random point in this box. Within returns true if the point is in the box.
523struct PDBox : public pDomain {
524 // P0 is the min corner. p1 is the max corner.
526 float vol;
527
528 PINLINE PDBox(const pVec& e0, const pVec& e1)
529 {
530 Which = PDBox_e;
531 PDBox_Cons(e0, e1);
532 }
533
534 PINLINE void PDBox_Cons(const pVec& e0, const pVec& e1)
535 {
536 p0 = e0;
537 p1 = e1;
538 if (e1.x() < e0.x()) {
539 p0.x() = e1.x();
540 p1.x() = e0.x();
541 }
542 if (e1.y() < e0.y()) {
543 p0.y() = e1.y();
544 p1.y() = e0.y();
545 }
546 if (e1.z() < e0.z()) {
547 p0.z() = e1.z();
548 p1.z() = e0.z();
549 }
550
551 dif = p1 - p0;
553 }
554
555 PINLINE bool Within(const pVec& pos) const /// Returns true if the point is in the box.
556 {
557 return !((pos.x() < p0.x()) || (pos.x() > p1.x()) || (pos.y() < p0.y()) || (pos.y() > p1.y()) || (pos.z() < p0.z()) || (pos.z() > p1.z()));
558 }
559
560 PINLINE pVec Generate() const /// Returns a random point in this box.
561 {
562 // Scale and translate [0,1] random to fit box
564 }
565
566 PINLINE float Size() const { return vol; }
567
568 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDBox(*this)); }
569};
570
571/// Cylinder
572///
573/// e0 and e1 are the endpoints of the axis of the right cylinder. OuterRadius is the outer radius, and InnerRadius is the inner
574/// radius for a cylindrical shell. InnerRadius = 0 for a solid cylinder with no empty space in the middle.
575///
576/// Generate returns a random point in the cylindrical shell. Within returns true if the point is within the cylindrical shell.
577struct PDCylinder : public pDomain {
578 pVec apex, axis, u, v; // Apex is one end. Axis is vector from one end to the other.
581
582 PINLINE PDCylinder(const pVec& e0, const pVec& e1, const float OuterRadius, const float InnerRadius = 0.0f)
583 {
584 if (InnerRadius < 0 || OuterRadius < 0) throw PErrInvalidValue("Can't have negative radius.");
585
587 PDCylinder_Cons(e0, e1, OuterRadius, InnerRadius);
588 }
589
590 PINLINE void PDCylinder_Cons(const pVec& e0, const pVec& e1, const float OuterRadius, const float InnerRadius = 0.0f)
591 {
592 apex = e0;
593 axis = e1 - e0;
594
595 if (OuterRadius < InnerRadius) {
596 radOut = InnerRadius;
597 radIn = OuterRadius;
598 } else {
599 radOut = OuterRadius;
600 radIn = InnerRadius;
601 }
602
605
606 ThinShell = (radIn == radOut);
607 radDif = radOut - radIn;
608
609 // Given an arbitrary nonzero vector n, make two orthonormal
610 // vectors u and v forming a frame [u,v,n.normalize()].
611 pVec n = axis;
612 float axisLenSqr = axis.lenSqr();
613 float len = sqrtf(axisLenSqr);
614 axisLenInvSqr = axisLenSqr ? (1.0f / axisLenSqr) : 0.0f;
615 n *= sqrtf(axisLenInvSqr);
616
617 // Find a vector orthogonal to n.
618 pVec basis = pVec(1.0f, 0.0f, 0.0f);
619 if (fabsf(dot(basis, n)) > 0.999f) basis = pVec(0.0f, 1.0f, 0.0f);
620
621 // Project away N component, normalize and cross to get
622 // second orthonormal vector.
623 u = basis - n * dot(basis, n);
625 v = Cross(n, u);
626
627 float EndCapArea = M_PI * radOutSqr - M_PI * radInSqr;
628 if (ThinShell)
629 vol = len * 2.0f * M_PI * radOut; // Surface area of cylinder
630 else
631 vol = EndCapArea * len; // Volume of cylindrical shell
632 }
633
634 PINLINE bool Within(const pVec& pos) const /// Returns true if the point is within the cylindrical shell.
635 {
636 // This is painful and slow. Might be better to do quick accept/reject tests.
637 // Axis is vector from base to tip of the cylinder.
638 // x is vector from base to pos.
639 // x . axis
640 // dist = ---------- = projected distance of x along the axis
641 // axis. axis ranging from 0 (base) to 1 (tip)
642 //
643 // rad = x - dist * axis = projected vector of x along the base
644
645 pVec x = pos - apex;
646
647 // Check axial distance
648 float dist = dot(axis, x) * axisLenInvSqr;
649 if (dist < 0.0f || dist > 1.0f) return false;
650
651 // Check radial distance
652 pVec xrad = x - axis * dist; // Radial component of x
653 float rSqr = xrad.lenSqr();
654
655 return (rSqr >= radInSqr && rSqr <= radOutSqr);
656 }
657
658 PINLINE pVec Generate() const /// Returns a random point in the cylindrical shell.
659 {
660 float dist = pRandf(); // Distance between base and tip
661 float theta = pRandf() * 2.0f * float(M_PI); // Angle around axis
662 // Distance from axis
663 float r = radIn + pRandf() * radDif;
664
665 // Another way to do this is to choose a random point in a square and keep it if it's in the circle.
666 float x = r * cosf(theta);
667 float y = r * sinf(theta);
668
669 pVec pos = apex + axis * dist + u * x + v * y;
670 return pos;
671 }
672
673 PINLINE float Size() const /// Returns the thick cylindrical shell volume or the thin cylindrical shell area if OuterRadius==InnerRadius.
674 {
675 return vol;
676 }
677
678 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDCylinder(*this)); }
679};
680
681/// Cone
682///
683/// e0 is the apex of the cone and e1 is the endpoint of the axis at the cone's base. OuterRadius is the radius of the base of the cone.
684/// InnerRadius is the radius of the base of a cone to subtract from the first cone to create a conical shell. This is similar to the
685/// cylindrical shell, which can be thought of as a large cylinder with a smaller cylinder subtracted from the middle. Both cones share the
686/// same apex and axis, which implies that the thickness of the conical shell tapers to 0 at the apex. InnerRadius = 0 for a solid cone with
687/// no empty space in the middle.
688///
689/// Generate returns a random point in the conical shell. Within returns true if the point is within the conical shell.
690struct PDCone : public pDomain {
691 pVec apex, axis, u, v; // Apex is one end. Axis is vector from one end to the other.
694
695 PINLINE PDCone(const pVec& e0, const pVec& e1, const float OuterRadius, const float InnerRadius = 0.0f)
696 {
697 if (InnerRadius < 0 || OuterRadius < 0) throw PErrInvalidValue("Can't have negative radius.");
698
699 Which = PDCone_e;
700 PDCone_Cons(e0, e1, OuterRadius, InnerRadius);
701 }
702
703 PINLINE void PDCone_Cons(const pVec& e0, const pVec& e1, const float OuterRadius, const float InnerRadius = 0.0f)
704 {
705 apex = e0;
706 axis = e1 - e0;
707
708 if (OuterRadius < InnerRadius) {
709 radOut = InnerRadius;
710 radIn = OuterRadius;
711 } else {
712 radOut = OuterRadius;
713 radIn = InnerRadius;
714 }
715
718
719 ThinShell = (radIn == radOut);
720 radDif = radOut - radIn;
721
722 // Given an arbitrary nonzero vector n, make two orthonormal
723 // vectors u and v forming a frame [u,v,n.normalize()].
724 pVec n = axis;
725 float axisLenSqr = axis.lenSqr();
726 float len = sqrtf(axisLenSqr);
727 axisLenInvSqr = axisLenSqr ? 1.0f / axisLenSqr : 0.0f;
728 n *= sqrtf(axisLenInvSqr);
729
730 // Find a vector orthogonal to n.
731 pVec basis = pVec(1.0f, 0.0f, 0.0f);
732 if (fabsf(dot(basis, n)) > 0.999f) basis = pVec(0.0f, 1.0f, 0.0f);
733
734 // Project away N component, normalize and cross to get
735 // second orthonormal vector.
736 u = basis - n * dot(basis, n);
738 v = Cross(n, u);
739
740 if (ThinShell)
741 vol = sqrtf(axisLenSqr + radOutSqr) * M_PI * radOut; // Surface area of cone (not counting endcap)
742 else {
743 float OuterVol = 0.33333333f * M_PI * radOutSqr * len;
744 float InnerVol = 0.33333333f * M_PI * radInSqr * len;
745 vol = OuterVol - InnerVol; // Volume of conical shell
746 }
747 }
748
749 PINLINE bool Within(const pVec& pos) const /// Returns true if the point is within the conical shell.
750 {
751 // This is painful and slow. Might be better to do quick
752 // accept/reject tests.
753 // Let axis = vector from base to tip of the cone
754 // x = vector from base to test point
755 // x . axis
756 // dist = ---------- = projected distance of x along the axis
757 // axis. axis ranging from 0 (base) to 1 (tip)
758 //
759 // rad = x - dist * axis = projected vector of x along the base
760
761 pVec x = pos - apex;
762
763 // Check axial distance
764 // axisLenInvSqr stores 1 / dot(axis, axis)
765 float dist = dot(axis, x) * axisLenInvSqr;
766 if (dist < 0.0f || dist > 1.0f) return false;
767
768 // Check radial distance; scale radius along axis for cones
769 pVec xrad = x - axis * dist; // Radial component of x
770 float rSqr = xrad.lenSqr();
771
772 return (rSqr >= fsqr(dist * radIn) && rSqr <= fsqr(dist * radOut));
773 }
774
775 PINLINE pVec Generate() const /// Returns a random point in the conical shell.
776 {
777 float dist = pRandf(); // Distance between base and tip
778 float theta = pRandf() * 2.0f * float(M_PI); // Angle around axis
779 // Distance from axis
780 float r = radIn + pRandf() * radDif;
781
782 // Another way to do this is to choose a random point in a square and keep it if it's in the circle.
783 float x = r * cosf(theta);
784 float y = r * sinf(theta);
785
786 // Scale radius along axis for cones
787 x *= dist;
788 y *= dist;
789
790 pVec pos = apex + axis * dist + u * x + v * y;
791 return pos;
792 }
793
794 PINLINE float Size() const /// Returns the thick conical shell volume or the thin conical shell area if OuterRadius==InnerRadius.
795 {
796 return vol;
797 }
798
799 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDCone(*this)); }
800};
801
802/// Sphere
803///
804/// The point Center is the center of the sphere. OuterRadius is the outer radius of the spherical shell and InnerRadius is the inner radius.
805///
806/// Generate returns a random point in the thick shell at a distance between OuterRadius and InnerRadius from point Center. If InnerRadius
807/// is 0, then it is the whole sphere. Within returns true if the point lies within the thick shell at a distance between InnerRadius to OuterRadius from point Center.
808struct PDSphere : public pDomain {
812
813 PINLINE PDSphere(const pVec& Center, const float OuterRadius, const float InnerRadius = 0.0f)
814 {
815 if (InnerRadius < 0 || OuterRadius < 0) throw PErrInvalidValue("Can't have negative radius.");
816
818 PDSphere_Cons(Center, OuterRadius, InnerRadius);
819 }
820
821 PINLINE void PDSphere_Cons(const pVec& Center, const float OuterRadius, const float InnerRadius = 0.0f)
822 {
823 ctr = Center;
824
825 if (OuterRadius < InnerRadius) {
826 radOut = InnerRadius;
827 radIn = OuterRadius;
828 } else {
829 radOut = OuterRadius;
830 radIn = InnerRadius;
831 }
832
835
836 ThinShell = (radIn == radOut);
837 radDif = radOut - radIn;
838
839 if (ThinShell)
840 vol = 4.0f * M_PI * radOutSqr; // Surface area of sphere
841 else {
842 float OuterVol = 1.33333333f * M_PI * radOutSqr * radOut;
843 float InnerVol = 1.33333333f * M_PI * radInSqr * radIn;
844 vol = OuterVol - InnerVol; // Volume of spherical shell
845 }
846 }
847
848 PINLINE bool Within(const pVec& pos) const /// Returns true if the point lies within the thick shell.
849 {
850 pVec rvec(pos - ctr);
851 float rSqr = rvec.lenSqr();
852 return rSqr <= radOutSqr && rSqr >= radInSqr;
853 }
854
855 PINLINE pVec Generate() const /// Returns a random point in the thick spherical shell.
856 {
857 pVec pos;
858
859 do {
860 pos = pRandVec() - pVec(0.5f, 0.5f, 0.5f); // Point on [-0.5,0.5] box
861 } while (pos.lenSqr() > fsqr(0.5)); // Make sure it's also on r=0.5 sphere.
862 pos.normalize(); // Now it's on r=1 spherical shell
863
864 // Scale unit sphere pos by [0..r] and translate
865 if (ThinShell) // I can't remember why I make this distinction. Is it for precision or speed? Speed doesn't seem likely.
866 pos = ctr + pos * radOut;
867 else
868 pos = ctr + pos * (radIn + pRandf() * radDif);
869
870 return pos;
871 }
872
873 PINLINE float Size() const /// Returns the thick spherical shell volume or the thin spherical shell area if OuterRadius==InnerRadius.
874 {
875 return vol;
876 }
877
878 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDSphere(*this)); }
879};
880
881/// Gaussian blob
882///
883/// The point Center is the center of a normal probability density of standard deviation StandardDev. The density is radially symmetrical.
884/// The blob domain allows for some very natural-looking effects because there is no sharp, artificial-looking boundary at the edge of the domain.
885///
886/// Generate returns a point with normal probability density. Within has a probability of returning true equal to the probability density at the specified point.
887struct PDBlob : public pDomain {
890
891 PINLINE PDBlob(const pVec& Center, const float StandardDev)
892 {
893 Which = PDBlob_e;
894 PDBlob_Cons(Center, StandardDev);
895 }
896
897 PINLINE void PDBlob_Cons(const pVec& Center, const float StandardDev)
898 {
899 ctr = Center;
900 stdev = StandardDev;
901 float oneOverSigma = 1.0f / (stdev + 0.000000000001f);
902 Scale1 = -0.5f * fsqr(oneOverSigma);
903 Scale2 = P_ONEOVERSQRT2PI * oneOverSigma;
904 }
905
906 PINLINE bool Within(const pVec& pos) const /// Has a probability of returning true equal to the probability density at the specified point.
907 {
908 pVec x = pos - ctr;
909 float Gx = expf(x.lenSqr() * Scale1) * Scale2;
910 return (pRandf() < Gx);
911 }
912
913 PINLINE pVec Generate() const /// Returns a point with normal probability density.
914 {
915 return ctr + pNRandVec(stdev);
916 }
917
918 PINLINE float Size() const /// Returns the probability density integral, which is 1.0.
919 {
920 return 1.0f;
921 }
922
923 std::shared_ptr<pDomain> copy() const { return std::shared_ptr<pDomain>(new PDBlob(*this)); }
924};
925}; // namespace PAPI
926
927#endif
A representation of a region of space.
Definition: pDomain.h:55
virtual float Size() const =0
Returns the size of the domain (length, area, or volume).
virtual pVec Generate() const =0
Returns a random point in the domain.
pDomainType_E Which
Definition: pDomain.h:58
virtual bool Within(const pVec &) const =0
Returns true if the given point is within the domain.
virtual std::shared_ptr< pDomain > copy() const =0
A single-precision floating point three-vector.
Definition: pVec.h:63
friend pVec CompMult(const pVec &a, const pVec &b)
Definition: pVec.h:159
float normalize(const float toLen=1.f)
Definition: pVec.h:84
pVec(float ax, float ay, float az)
Definition: pVec.h:67
pVec operator+(const pVec &a) const
Definition: pVec.h:106
pVec operator*(const float s) const
Definition: pVec.h:98
friend float dot(const pVec &a, const pVec &b)
Definition: pVec.h:95
const float & x() const
Definition: pVec.h:72
float & y()
Definition: pVec.h:77
pVec operator-(const pVec &a) const
Definition: pVec.h:108
bool operator==(const pVec &a) const
Definition: pVec.h:110
pVec & operator*=(const float a)
Definition: pVec.h:136
float & x()
Definition: pVec.h:76
float length() const
Definition: pVec.h:80
const float & y() const
Definition: pVec.h:73
float lenSqr() const
Definition: pVec.h:82
pVec operator/(const float s) const
Definition: pVec.h:100
const float & z() const
Definition: pVec.h:74
float & z()
Definition: pVec.h:78
friend pVec Cross(const pVec &a, const pVec &b)
Definition: pVec.h:161
PAPIClasses.h.
Definition: pAPIContext.h:16
float pRandf()
Definition: pVec.h:34
pDomainType_E
Enums for the different types of domains that can be stored in a pDomain.
Definition: pDomain.h:22
@ PDCylinder_e
Definition: pDomain.h:31
@ PDPlane_e
Definition: pDomain.h:29
@ PDBlob_e
Definition: pDomain.h:34
@ PDCone_e
Definition: pDomain.h:32
@ PDTriangle_e
Definition: pDomain.h:26
@ PDPoint_e
Definition: pDomain.h:24
@ PDDisc_e
Definition: pDomain.h:28
@ PDSphere_e
Definition: pDomain.h:33
@ PDRectangle_e
Definition: pDomain.h:27
@ PDBox_e
Definition: pDomain.h:30
@ PDLine_e
Definition: pDomain.h:25
@ PDUnion_e
Definition: pDomain.h:23
pVec pNRandVec(float sigma)
Definition: pVec.h:179
const float P_ONEOVERSQRT2PI
Definition: pVec.h:25
float fsqr(float f)
Definition: pVec.h:27
pVec pRandVec()
Definition: pVec.h:177
#define P_PLANAR_EPSILON
< How small the dot product must be to declare that a point is in a plane for Within().
Definition: pDomain.h:19
#define PINLINE
Definition: pVec.h:20
#define M_PI
PVec.h - yet another 3D vector class.
Definition: pVec.h:14
Gaussian blob.
Definition: pDomain.h:887
float Scale2
Definition: pDomain.h:889
PDBlob(const pVec &Center, const float StandardDev)
Definition: pDomain.h:891
void PDBlob_Cons(const pVec &Center, const float StandardDev)
Definition: pDomain.h:897
float Size() const
Returns the probability density integral, which is 1.0.
Definition: pDomain.h:918
float Scale1
Definition: pDomain.h:889
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:923
float stdev
Definition: pDomain.h:889
pVec ctr
Definition: pDomain.h:888
bool Within(const pVec &pos) const
Has a probability of returning true equal to the probability density at the specified point.
Definition: pDomain.h:906
pVec Generate() const
Returns a point with normal probability density.
Definition: pDomain.h:913
Axis-aligned bounding box (AABB)
Definition: pDomain.h:523
void PDBox_Cons(const pVec &e0, const pVec &e1)
Definition: pDomain.h:534
pVec dif
Definition: pDomain.h:525
pVec p0
Definition: pDomain.h:525
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:566
float vol
Definition: pDomain.h:526
PDBox(const pVec &e0, const pVec &e1)
Definition: pDomain.h:528
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:568
pVec p1
Definition: pDomain.h:525
bool Within(const pVec &pos) const
Returns true if the point is in the box.
Definition: pDomain.h:555
pVec Generate() const
Returns a random point in this box.
Definition: pDomain.h:560
Cone.
Definition: pDomain.h:690
float radInSqr
Definition: pDomain.h:692
float radOutSqr
Definition: pDomain.h:692
bool ThinShell
Definition: pDomain.h:693
float radIn
Definition: pDomain.h:692
float radOut
Definition: pDomain.h:692
void PDCone_Cons(const pVec &e0, const pVec &e1, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:703
pVec u
Definition: pDomain.h:691
float axisLenInvSqr
Definition: pDomain.h:692
float Size() const
Returns the thick conical shell volume or the thin conical shell area if OuterRadius==InnerRadius.
Definition: pDomain.h:794
pVec v
Definition: pDomain.h:691
float vol
Definition: pDomain.h:692
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:799
PDCone(const pVec &e0, const pVec &e1, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:695
pVec axis
Definition: pDomain.h:691
pVec apex
Definition: pDomain.h:691
float radDif
Definition: pDomain.h:692
bool Within(const pVec &pos) const
Returns true if the point is within the conical shell.
Definition: pDomain.h:749
pVec Generate() const
Returns a random point in the conical shell.
Definition: pDomain.h:775
Cylinder.
Definition: pDomain.h:577
float radInSqr
Definition: pDomain.h:579
float radOutSqr
Definition: pDomain.h:579
bool ThinShell
Definition: pDomain.h:580
float radIn
Definition: pDomain.h:579
float radOut
Definition: pDomain.h:579
pVec u
Definition: pDomain.h:578
float axisLenInvSqr
Definition: pDomain.h:579
float Size() const
Returns the thick cylindrical shell volume or the thin cylindrical shell area if OuterRadius==InnerRa...
Definition: pDomain.h:673
pVec v
Definition: pDomain.h:578
float vol
Definition: pDomain.h:579
void PDCylinder_Cons(const pVec &e0, const pVec &e1, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:590
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:678
pVec axis
Definition: pDomain.h:578
pVec apex
Definition: pDomain.h:578
PDCylinder(const pVec &e0, const pVec &e1, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:582
float radDif
Definition: pDomain.h:579
bool Within(const pVec &pos) const
Returns true if the point is within the cylindrical shell.
Definition: pDomain.h:634
pVec Generate() const
Returns a random point in the cylindrical shell.
Definition: pDomain.h:658
Arbitrarily-oriented disc.
Definition: pDomain.h:392
PDDisc(const pVec &Center, const pVec Normal, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:396
float radInSqr
Definition: pDomain.h:394
float radOutSqr
Definition: pDomain.h:394
float radIn
Definition: pDomain.h:394
float radOut
Definition: pDomain.h:394
pVec p
Definition: pDomain.h:393
float dif
Definition: pDomain.h:394
pVec u
Definition: pDomain.h:393
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:464
pVec v
Definition: pDomain.h:393
float area
Definition: pDomain.h:394
float D
Definition: pDomain.h:394
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:469
void PDDisc_Cons(const pVec &Center, const pVec Normal, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:404
pVec nrm
Definition: pDomain.h:393
bool Within(const pVec &pos) const
Returns true for points within epsilon of the disc.
Definition: pDomain.h:439
pVec Generate() const
Returns a point inside the disc shell.
Definition: pDomain.h:450
A line segment.
Definition: pDomain.h:207
pVec vecNrm
Definition: pDomain.h:208
pVec p0
Definition: pDomain.h:208
pVec vec
Definition: pDomain.h:208
void PDLine_Cons(const pVec &e0, const pVec &e1)
Definition: pDomain.h:217
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:240
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:242
pVec p1
Definition: pDomain.h:208
PDLine(const pVec &e0, const pVec &e1)
Definition: pDomain.h:211
float len
Definition: pDomain.h:209
bool Within(const pVec &pos) const
Returns true for points within epsilon of the line segment.
Definition: pDomain.h:227
pVec Generate() const
Returns a random point on this segment.
Definition: pDomain.h:235
Arbitrarily-oriented plane.
Definition: pDomain.h:478
pVec p
Definition: pDomain.h:479
void PDPlane_Cons(const pVec &p0, const pVec &Normal)
Definition: pDomain.h:488
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:510
float D
Definition: pDomain.h:480
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:515
pVec nrm
Definition: pDomain.h:479
PDPlane(const pVec &p0, const pVec &Normal)
Definition: pDomain.h:482
bool Within(const pVec &pos) const
Returns true if the point is in the positive half-space of the plane (in the plane or on the side tha...
Definition: pDomain.h:499
pVec Generate() const
Returns the point p0.
Definition: pDomain.h:505
A single point.
Definition: pDomain.h:176
void PDPoint_Cons(const pVec &p0)
Definition: pDomain.h:185
pVec p
Definition: pDomain.h:177
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:197
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:199
PDPoint(const pVec &p0)
Definition: pDomain.h:179
bool Within(const pVec &pos) const
Returns true if the point is exactly equal.
Definition: pDomain.h:187
pVec Generate() const
Returns the point.
Definition: pDomain.h:192
Rhombus-shaped planar region.
Definition: pDomain.h:325
float uLen
Definition: pDomain.h:327
void PDRectangle_Cons(const pVec &p0, const pVec &u0, const pVec &v0)
Definition: pDomain.h:335
pVec uNrm
Definition: pDomain.h:326
pVec p
Definition: pDomain.h:326
pVec vNrm
Definition: pDomain.h:326
pVec u
Definition: pDomain.h:326
float vLen
Definition: pDomain.h:327
PDRectangle(const pVec &p0, const pVec &u0, const pVec &v0)
Definition: pDomain.h:329
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:381
pVec v
Definition: pDomain.h:326
float area
Definition: pDomain.h:327
float D
Definition: pDomain.h:327
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:383
pVec nrm
Definition: pDomain.h:326
pVec s1
Definition: pDomain.h:326
pVec s2
Definition: pDomain.h:326
bool Within(const pVec &pos) const
Returns true for points within epsilon of the patch.
Definition: pDomain.h:359
pVec Generate() const
Returns a random point in the diamond-shaped patch whose corners are o, o+u, o+u+v,...
Definition: pDomain.h:375
Sphere.
Definition: pDomain.h:808
float radInSqr
Definition: pDomain.h:810
float radOutSqr
Definition: pDomain.h:810
bool ThinShell
Definition: pDomain.h:811
float radIn
Definition: pDomain.h:810
float radOut
Definition: pDomain.h:810
void PDSphere_Cons(const pVec &Center, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:821
float Size() const
Returns the thick spherical shell volume or the thin spherical shell area if OuterRadius==InnerRadius...
Definition: pDomain.h:873
float vol
Definition: pDomain.h:810
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:878
PDSphere(const pVec &Center, const float OuterRadius, const float InnerRadius=0.0f)
Definition: pDomain.h:813
float radDif
Definition: pDomain.h:810
pVec ctr
Definition: pDomain.h:809
bool Within(const pVec &pos) const
Returns true if the point lies within the thick shell.
Definition: pDomain.h:848
pVec Generate() const
Returns a random point in the thick spherical shell.
Definition: pDomain.h:855
A Triangle.
Definition: pDomain.h:252
float uLen
Definition: pDomain.h:254
PDTriangle(const pVec &p0, const pVec &p1, const pVec &p2)
Definition: pDomain.h:256
pVec uNrm
Definition: pDomain.h:253
pVec p
Definition: pDomain.h:253
pVec vNrm
Definition: pDomain.h:253
pVec u
Definition: pDomain.h:253
float vLen
Definition: pDomain.h:254
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:315
pVec v
Definition: pDomain.h:253
float area
Definition: pDomain.h:254
float D
Definition: pDomain.h:254
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:317
pVec nrm
Definition: pDomain.h:253
pVec s1
Definition: pDomain.h:253
pVec s2
Definition: pDomain.h:253
bool Within(const pVec &pos) const
Returns true for points within epsilon of the triangle.
Definition: pDomain.h:286
pVec Generate() const
Returns a random point in the triangle.
Definition: pDomain.h:302
void PDTriangle_Cons(const pVec &p0, const pVec &p1, const pVec &p2)
Definition: pDomain.h:262
A CSG union of multiple domains.
Definition: pDomain.h:93
PDUnion(const std::vector< std::shared_ptr< pDomain > > &DomList)
Makes a copy of all the subdomains and point to the copies.
Definition: pDomain.h:123
PDUnion()
Use this one to create an empty PDUnion then call .insert() to add each item to it.
Definition: pDomain.h:97
PDUnion(const pDomain &A, const pDomain &B, const pDomain &C)
Definition: pDomain.h:111
PDUnion(const pDomain &A, const pDomain &B)
Definition: pDomain.h:103
std::vector< std::shared_ptr< pDomain > > Doms
Definition: pDomain.h:94
float Size() const
Returns the size of the domain (length, area, or volume).
Definition: pDomain.h:168
std::shared_ptr< pDomain > copy() const
Definition: pDomain.h:170
void insert(const pDomain &A)
Insert another domain into this PDUnion.
Definition: pDomain.h:145
PDUnion(const PDUnion &P)
Makes a copy of all the subdomains and point to the copies.
Definition: pDomain.h:134
float TotalSize
Definition: pDomain.h:95
bool Within(const pVec &pos) const
Returns true if pos is within any of the domains.
Definition: pDomain.h:151
pVec Generate() const
Generate a point in any subdomain, chosen by the ratio of their sizes.
Definition: pDomain.h:158
Internal API error (a PASSERT failed)
Definition: pError.h:28
PErrInternalError(const std::string Er)
Definition: pError.h:29
An invalid value was passed to an API call.
Definition: pError.h:40
PErrInvalidValue(const std::string Er)
Definition: pError.h:41