1 | // tGeometry.h  |
2 | //  |
3 | // Idealized geometric shapes. These include triangles, circles, boxes, spheres, rays, lines, planes, cylinders,  |
4 | // capsules, frustums, and ellipses. When the shape primitives have a number in their name, it refers to the space the  |
5 | // shape is in, not the dimensionality of the shape itself. eg. A tCircle3 is a (2D) circle in R3 while a tCircle2 is a  |
6 | // 2D circle in R2. For shapes that are in R3 we drop the 3 because the R3 primitives are more general.  |
7 | //  |
8 | // Copyright (c) 2006, 2016, 2019 Tristan Grimmer.  |
9 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
10 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
11 | //  |
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
13 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
14 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
15 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
16 | // PERFORMANCE OF THIS SOFTWARE.  |
17 |   |
18 | #pragma once  |
19 | #include "Math/tVector2.h"  |
20 | #include "Math/tVector3.h"  |
21 | #include "Math/tVector4.h"  |
22 | #include "Math/tMatrix2.h"  |
23 | #include "Math/tMatrix4.h"  |
24 | namespace tMath  |
25 | {  |
26 |   |
27 |   |
28 | // Edges are simply line segments with two end points. The tEdge data structure is just a set of 2 indexes into any  |
29 | // sort of vertex array. It is therefore dimension-agnostic and can be used for edges in R2 and R3.  |
30 | struct tEdge  |
31 | {  |
32 | tEdge() { }  |
33 | tEdge(const tEdge& s) { for (int i = 0; i < 2; i++) Index[i] = s.Index[i]; }  |
34 | bool operator==(const tEdge& e) const { return (Index[0] == e.Index[0]) && (Index[1] == e.Index[1]); }  |
35 | bool operator!=(const tEdge& e) const { return (Index[0] != e.Index[0]) || (Index[1] != e.Index[1]); }  |
36 | tEdge& operator=(const tEdge& e) { Index[0] = e.Index[0]; Index[1] = e.Index[1]; return *this; }  |
37 | int Index[2];  |
38 | };  |
39 |   |
40 |   |
41 | // A triangular face. It has 3 vertices and 3 edges. It may be degenerate. The tTriFace data structure is just a set of  |
42 | // 3 indexes into any sort of vertex array. It is therefore dimension-agnostic and can be used for tris in R2 and R3.  |
43 | struct tTriFace  |
44 | {  |
45 | tTriFace() { }  |
46 | tTriFace(const tTriFace& s) { for (int i = 0; i < 3; i++) Index[i] = s.Index[i]; }  |
47 | bool operator==(const tTriFace& f) const { return (Index[0] == f.Index[0]) && (Index[1] == f.Index[1]) && (Index[2] == f.Index[2]); }  |
48 | bool operator!=(const tTriFace& f) const { return (Index[0] != f.Index[0]) || (Index[1] != f.Index[1]) || (Index[2] != f.Index[2]); }  |
49 | tTriFace& operator=(const tTriFace& f) { Index[0] = f.Index[0]; Index[1] = f.Index[1]; Index[2] = f.Index[2]; return *this; }  |
50 | int Index[3];  |
51 | };  |
52 | typedef tTriFace tTri; // A short synonym for a triangular face.  |
53 |   |
54 |   |
55 | // The same as a tTriFace but using only 16 bit indices.  |
56 | struct tTriFaceShort  |
57 | {  |
58 | tTriFaceShort() { }  |
59 | tTriFaceShort(const tTriFaceShort& s) { for (int v = 0; v < 3; v++) Index[v] = s.Index[v]; }  |
60 | bool operator==(const tTriFaceShort& f) const { return (Index[0] == f.Index[0]) && (Index[1] == f.Index[1]) && (Index[2] == f.Index[2]); }  |
61 | bool operator!=(const tTriFaceShort& f) const { return (Index[0] != f.Index[0]) || (Index[1] != f.Index[1]) || (Index[2] != f.Index[2]); }  |
62 | tTriFaceShort& operator=(const tTriFaceShort& f) { Index[0] = f.Index[0]; Index[1] = f.Index[1]; Index[2] = f.Index[2]; return *this; }  |
63 | uint16 Index[3];  |
64 | };  |
65 | typedef tTriFaceShort tTriShort; // A short synonym for a short triangular face.  |
66 |   |
67 |   |
68 | // A quad face. It has 4 vertices and 4 edges. It may be degenerate. The tQuadFace data structure is just a set of  |
69 | // 4 indexes into any sort of vertex array. It is therefore dimension-agnostic and can be used for quads in R2 and R3.  |
70 | struct tQuadFace  |
71 | {  |
72 | tQuadFace() { }  |
73 | tQuadFace(const tQuadFace& s) { for (int i = 0; i < 4; i++) Index[i] = s.Index[i]; }  |
74 | bool operator==(const tQuadFace& f) const { return (Index[0] == f.Index[0]) && (Index[1] == f.Index[1]) && (Index[2] == f.Index[2]) && (Index[3] == f.Index[3]); }  |
75 | bool operator!=(const tQuadFace& f) const { return (Index[0] != f.Index[0]) || (Index[1] != f.Index[1]) || (Index[2] != f.Index[2]) || (Index[3] != f.Index[3]); }  |
76 | tQuadFace& operator=(const tQuadFace& f) { Index[0] = f.Index[0]; Index[1] = f.Index[1]; Index[2] = f.Index[2]; Index[3] = f.Index[3]; return *this; }  |
77 | int Index[4];  |
78 | };  |
79 | typedef tQuadFace tQuad; // A short synonym for a quadrilateral face.  |
80 |   |
81 |   |
82 | // The same as a tQuadFace but using only 16 bit indices.  |
83 | struct tQuadFaceShort  |
84 | {  |
85 | tQuadFaceShort() { }  |
86 | tQuadFaceShort(const tQuadFaceShort& s) { for (int i = 0; i < 4; i++) Index[i] = s.Index[i]; }  |
87 | bool operator==(const tQuadFaceShort& f) const { return (Index[0] == f.Index[0]) && (Index[1] == f.Index[1]) && (Index[2] == f.Index[2]) && (Index[3] == f.Index[3]); }  |
88 | bool operator!=(const tQuadFaceShort& f) const { return (Index[0] != f.Index[0]) || (Index[1] != f.Index[1]) || (Index[2] != f.Index[2]) || (Index[3] != f.Index[3]); }  |
89 | tQuadFaceShort& operator=(const tQuadFaceShort& f) { Index[0] = f.Index[0]; Index[1] = f.Index[1]; Index[2] = f.Index[2]; Index[3] = f.Index[3]; return *this; }  |
90 | uint16 Index[4];  |
91 | };  |
92 | typedef tQuadFace tQuad; // A short synonym for a quadrilateral face.  |
93 |   |
94 |   |
95 | // A line segment goes from point A to point B. Ends are included. The 2 means in R2.  |
96 | struct tLineSeg2  |
97 | {  |
98 | tLineSeg2() { }  |
99 | tLineSeg2(float x0, float y0, float x1, float y1) { A.Set(x0, y0); B.Set(x1, y1); }  |
100 | tLineSeg2(const tVector2& a, const tVector2& b) : A(a), B(b) { }  |
101 | tLineSeg2(const tLineSeg2& s) : A(s.A), B(s.B) { }  |
102 | tVector2 A, B;  |
103 | };  |
104 |   |
105 |   |
106 | // A line goes through point A and point B.  |
107 | struct tLine2  |
108 | {  |
109 | tLine2() { }  |
110 | tLine2(float x0, float y0, float x1, float y1) { A.Set(x0, y0); B.Set(x1, y1); }  |
111 | tLine2(const tVector2& a, const tVector2& b) : A(a), B(b) { }  |
112 | tLine2(const tLineSeg2& s) : A(s.A), B(s.B) { }  |
113 | tLine2(const tLine2& s) : A(s.A), B(s.B) { }  |
114 | tVector2 A, B;  |
115 | };  |
116 |   |
117 |   |
118 | // A ray from Start in (normalized) direction Dir.  |
119 | struct tRay2  |
120 | {  |
121 | tRay2() { }  |
122 | tVector2 Start;  |
123 | tVector2 Dir; // Normalized.  |
124 | };  |
125 |   |
126 |   |
127 | // An axis aligned rectangle in R2.  |
128 | struct tARect2  |
129 | {  |
130 | enum Side  |
131 | {  |
132 | Side_None = 0x00000000,  |
133 | Side_Left = 0x00000001,  |
134 | Side_Right = 0x00000002,  |
135 | Side_Bottom = 0x00000004,  |
136 | Side_Top = 0x00000008  |
137 | };  |
138 |   |
139 | tARect2() { Clear(); }  |
140 | tARect2(const tARect2& src) { Set(src); }  |
141 | tARect2(const tVector2& origin, float sideLength) { Set(origin, sideLength); }  |
142 | tARect2(const tVector2& min, float width, float height) { Set(min, width, height); }  |
143 | tARect2(const tVector2& min, const tVector2& max) { Set(min, max); }  |
144 | tARect2(float minx, float miny, float maxx, float maxy) { Set(minx, miny, maxx, maxy); }  |
145 |   |
146 | void Set(const tARect2& src) { Min.Set(src.Min); Max.Set(src.Max); }  |
147 | void Set(const tVector2& origin, float sideLength) { Min.Set(origin.x - sideLength/2.0f, origin.y - sideLength/2.0f); Max.Set(origin.x + sideLength/2.0f, origin.y + sideLength/2.0f); }  |
148 | void Set(const tVector2& min, float width, float height) { Min.Set(min); Max.Set(min.x + width, min.y + height); }  |
149 | void Set(const tVector2& min, const tVector2& max) { Min.Set(min); Max.Set(max); }  |
150 | void Set(float minx, float miny, float maxx, float maxy) { Min.Set(minx, miny); Max.Set(maxx, maxy); }  |
151 |   |
152 | void AddPoint(const tVector2&);  |
153 | void Expand(float e) { Min.x -= e; Min.y -= e; Max.x += e; Max.y += e; }  |
154 | void Clear() { Min = tVector2(PosInfinity, PosInfinity); Max = tVector2(NegInfinity, NegInfinity); }  |
155 | void Empty() { Clear(); }  |
156 |   |
157 | // Boundary included.  |
158 | bool IsPointInside(const tVector2& point) const { return ((point.x >= Min.x) && (point.x <= Max.x) && (point.y >= Min.y) && (point.y <= Max.y)); }  |
159 |   |
160 | // The boundary is included. Positive margin values increase the rectangle size.  |
161 | bool IsPointInside(const tVector2& point, float margin) const { return ((point.x >= Min.x-margin) && (point.x <= Max.x+margin) && (point.y >= Min.y-margin) && (point.y <= Max.y+margin)); }  |
162 |   |
163 | // Similar to above except you can control which boundary sides are included. With default bias values this  |
164 | // function includes the 2 minimum boundary lines and does not include the 2 maximum lines. This is useful for  |
165 | // situations such as spatial partitioning where a single point must not be inside multiple adjacent boxes.  |
166 | bool IsPointInsideBias  |
167 | (  |
168 | const tVector2& point,  |
169 | tIntervalBias biasX = tIntervalBias::Low,  |
170 | tIntervalBias biasY = tIntervalBias::Low  |
171 | ) const;  |
172 |   |
173 | // Transforms Min and Max vectors and builds the new box from them.  |
174 | void Transform(const tMatrix2& xform);  |
175 |   |
176 | tVector2 ComputeCenter() const { return (Min + Max)/2.0f; }  |
177 | tVector2 ComputeExtents() const { return (Max - Min)/2.0f; }  |
178 | bool Overlaps(const tARect2&) const;  |
179 |   |
180 | tVector2 Min;  |
181 | tVector2 Max;  |
182 | };  |
183 | typedef tARect2 tRect2;  |
184 |   |
185 |   |
186 | struct tORect2  |
187 | {  |
188 | tORect2() : Center(0.0f, 0.0f), Extents(0.0f, 0.0f) { Axes[0].Set(1.0f, 0.0f); Axes[1].Set(0.0f, 1.0f); }  |
189 | bool IsInside(const tVector2& point) const;  |
190 | tVector2 Center;  |
191 | tVector2 Axes[2];  |
192 | tVector2 Extents;  |
193 | };  |
194 |   |
195 |   |
196 | struct tCircle2  |
197 | {  |
198 | tCircle2() { }  |
199 | float Area() const { return Pi * Radius * Radius; }  |
200 | float Radius;  |
201 | tVector2 Center;  |
202 | };  |
203 |   |
204 |   |
205 | // An ellipse in R2. The ellipse equation is: (x-a)^2 / r1^2 + (y-b)^2 / r2^2 = 1 where (a, b) is the center location,  |
206 | // r1 is the x 'radius', and r2 is the y 'radius'.  |
207 | struct tEllipse2  |
208 | {  |
209 | tEllipse2() { }  |
210 | float Area() const { return Pi * RadiusX * RadiusY; }  |
211 |   |
212 | float RadiusX, RadiusY;  |
213 | tVector2 Center;  |
214 | };  |
215 |   |
216 |   |
217 | // Primitives in 3D space.  |
218 |   |
219 |   |
220 | // A line segment has the same members as a line, but only goes from A to B (inclusive).  |
221 | struct tLineSeg  |
222 | {  |
223 | tLineSeg() { }  |
224 | tLineSeg(const tVector3& a, const tVector3& b) : A(a), B(b) { }  |
225 | tLineSeg(const tLineSeg& src) : A(src.A), B(src.B) { }  |
226 | tVector3 A, B;  |
227 | };  |
228 |   |
229 |   |
230 | // A ray from Start in normalized direction Dir.  |
231 | struct tRay  |
232 | {  |
233 | tRay() { }  |
234 | tRay(const tVector3& start, const tVector3& dir) : Start(start), Dir(dir) { }  |
235 | tRay(const tLineSeg& s) { Set(s); }  |
236 | void Set(const tVector3& start, const tVector3& dir) { Start = start; Dir = dir; }  |
237 | void Set(const tLineSeg& s) { Start = s.A; Dir = s.B - s.A; Dir.NormalizeSafe(); }  |
238 |   |
239 | tVector3 Start;  |
240 | tVector3 Dir;  |
241 | };  |
242 |   |
243 |   |
244 | // Goes through A and B and extends infinitely.  |
245 | struct tLine  |
246 | {  |
247 | tLine() { }  |
248 | tLine(const tRay& ray) { Set(ray); }  |
249 | tLine(const tLineSeg& seg) { Set(seg); }  |
250 | void Set(const tRay& ray) { A = ray.Start; B = ray.Start + ray.Dir; }  |
251 | void Set(const tLineSeg& seg) { A = seg.A; B = seg.B; }  |
252 |   |
253 | tVector3 A, B;  |
254 | };  |
255 |   |
256 |   |
257 | // A cylinder. Represented as a line segment with a radius.  |
258 | struct tCylinder  |
259 | {  |
260 | tCylinder() { }  |
261 | tCylinder(const tVector3& a, const tVector3& b, float radius) : Radius(radius), A(a), B(b) { }  |
262 | tCylinder(const tLineSeg& s, float radius) : Radius(radius), A(s.A), B(s.B) { }  |
263 |   |
264 | float Radius;  |
265 | tVector3 A, B;  |
266 | };  |
267 |   |
268 |   |
269 | // A capsule is just a cylinder with hemispherical ends. It uses the same representation as a tCylinder. The only  |
270 | // difference is in how the state varialble are interpreted. For a capsule, the ends parts also have a radius.  |
271 | typedef tCylinder tCapsule;  |
272 |   |
273 |   |
274 | struct tTriangle  |
275 | {  |
276 | tTriangle() { }  |
277 | tTriangle(const tVector3& a, const tVector3& b, const tVector3& c) : A(a), B(b), C(c) { }  |
278 |   |
279 | tVector3 A, B, C;  |
280 | };  |
281 |   |
282 |   |
283 | // This is an axis-aligned box in R3.  |
284 | struct tABox  |
285 | {  |
286 | tABox() : Min(PosInfinity, PosInfinity, PosInfinity), Max(NegInfinity, NegInfinity, NegInfinity) { }  |
287 | tABox(const tABox& src) : Min(src.Min), Max(src.Max) { }  |
288 | tABox(const tVector3& min, float width, float height, float depth) : Min(min), Max(min.x + width, min.y + height, min.z + depth) { }  |
289 | tABox(const tVector3& min, const tVector3& max) : Min(min), Max(max) { }  |
290 | tABox(float minx, float miny, float minz, float maxx, float maxy, float maxz) : Min(minx, miny, minz), Max(maxx, maxy, maxz) { }  |
291 |   |
292 | void AddPoint(const tVector3&);  |
293 | void Expand(float e) { Min.x -= e; Min.y -= e; Min.z -= e; Max.x += e; Max.y += e; Max.z += e; }  |
294 | void Empty() { Min = tVector3(PosInfinity, PosInfinity, PosInfinity); Max = tVector3(NegInfinity, NegInfinity, NegInfinity); }  |
295 | void Transform(const tMatrix4& xform); // Affine and projection transforms supported.  |
296 |   |
297 | // Boundary included.  |
298 | bool IsPointInside(const tVector3& point) const { return ((point.x >= Min.x) && (point.x <= Max.x) && (point.y >= Min.y) && (point.y <= Max.y) && (point.z >= Min.z) && (point.z <= Max.z)); }  |
299 |   |
300 | // The boundary is included. Positive margin values increase the rectangle size.  |
301 | bool IsPointInside(const tVector3& point, float margin) const { return ((point.x >= Min.x-margin) && (point.x <= Max.x+margin) && (point.y >= Min.y-margin) && (point.y <= Max.y+margin) && (point.z >= Min.z-margin) && (point.z <= Max.z+margin)); }  |
302 |   |
303 | // Similar to above except you can control which boundary planes are included. With default bias values this  |
304 | // function includes the 3 minimum boundary planes and does not include the 3 maximum planes. This is useful for  |
305 | // situations such as spatial partitioning where a single point must not be inside multiple adjacent boxes.  |
306 | bool IsPointInsideBias  |
307 | (  |
308 | const tVector3& point,  |
309 | tIntervalBias biasX = tIntervalBias::Low,  |
310 | tIntervalBias biasY = tIntervalBias::Low,  |
311 | tIntervalBias biasZ = tIntervalBias::Low  |
312 | ) const;  |
313 |   |
314 | tVector3 ComputeCenter() const { return (Min + Max)/2.0f; }  |
315 | tVector3 ComputeExtents() const { return (Max - Min)/2.0f; }  |
316 | bool Overlaps(const tABox&) const { tAssert(!"Not implemented yet." ); return false; }  |
317 |   |
318 | tVector3 Min;  |
319 | tVector3 Max;  |
320 | };  |
321 | typedef tABox tBox;  |
322 | typedef tABox tAABB;  |
323 |   |
324 |   |
325 | // @todo The oriented box is mostly unimplemented.  |
326 | struct tOBox  |
327 | {  |
328 | tOBox() : Center(0.0f, 0.0f, 0.0f), Extents(0.0f, 0.0f, 0.0f) { Axes[0].Set(1.0f, 0.0f, 0.0f); Axes[1].Set(0.0f, 1.0f, 0.0f); Axes[2].Set(0.0f, 0.0f, 1.0f); }  |
329 | bool IsInside(const tVector3& point) const;  |
330 |   |
331 | tVector3 Center;  |
332 | tVector3 Axes[3];  |
333 | tVector3 Extents;  |
334 | };  |
335 | typedef tOBox tOBB;  |
336 |   |
337 |   |
338 | struct tCircle  |
339 | {  |
340 | tCircle() { }  |
341 | float Area() const { return Pi * Radius * Radius; }  |
342 |   |
343 | float Radius;  |
344 | tVector3 Center;  |
345 | tVector3 Normal;  |
346 | };  |
347 |   |
348 |   |
349 | struct tSphere  |
350 | {  |
351 | tSphere() { }  |
352 | tSphere(const tVector3& center, float radius) : Center(center), Radius(radius) { }  |
353 | void Set(const tVector3& center, float radius) { Center = center; Radius = radius; }  |
354 |   |
355 | tVector3 Center;  |
356 | float Radius;  |
357 | };  |
358 |   |
359 |   |
360 | // A tPlane essentially represents the plane equation ax + by + cz + d = 0 using two member variables: Normal and  |
361 | // Distance. These two values act as the coefficients of the plane equation with a = Normal.x, b = Normal.y,  |
362 | // c = Normal.z, and d = Distance.  |
363 | struct tPlane  |
364 | {  |
365 | public:  |
366 | tPlane() { }  |
367 | tPlane(float a, float b, float c, float d = 0.0f) { Set(a, b, c, d); }  |
368 | tPlane(const tVector3& normal, float d = 0.0f) { Set(normal, d); }  |
369 | tPlane(const tVector4& v) { Set(v); }  |
370 | tPlane(const tVector3& normal, const tVector3& p) { Set(normal, p); }  |
371 | tPlane(const tVector3& p1, const tVector3& p2, const tVector3& p3) { Set(p1, p2, p3); }  |
372 |   |
373 | void Set(float a, float b, float c, float d) { Normal.Set(a, b, c); Distance = d; }  |
374 | void Set(const tVector3& normal, float d) { Normal = normal; Distance = d; }  |
375 | void Set(const tVector4& v) { Normal = tVector3(v); Distance = v.w; }  |
376 | void Set(const tVector3& normal, const tVector3& p) { Normal = normal; Distance = -(normal*p); }  |
377 | void Set(const tVector3& p1, const tVector3& p2, const tVector3& p3) { tVector3 v1 = p2 - p1; tVector3 v2 = p3 - p1; Normal = v1 % v2; Distance = -Normal * p3; }  |
378 |   |
379 | // Makes Normal length 1. When this is done the Distance member will be how far away the plane is from the origin in  |
380 | // the direction of Normal. The Distance /= length in the implementation is CORRECT.  |
381 | void Normalize() { float l = Normal.Length(); Normal /= l; Distance /= l; }  |
382 |   |
383 | // If the plane is normalized this returns the shortest distance from 'point' to the plane. Normalized or not, if  |
384 | // the result is < 0 then the point is in the negative half-space, > 0 for positive half-space, and 0 on the plane.  |
385 | float GetDistance(const tVector3& point) const { return Normal.x*point.x + Normal.y*point.y + Normal.z*point.z + Distance; }  |
386 |   |
387 | // Computes two unit vectors for the plane that form an orthogonal basis. The plane d-value is irrelevant and the  |
388 | // plane may be considered to go through the origin. The plane does not need to be normalized before calling.  |
389 | void ComputeOrthogonalBasisVectors(tVector3& u, tVector3& v) const;  |
390 |   |
391 | // The Normal is a vector perpendicular to the surface of the plane.  |
392 | tVector3 Normal;  |
393 |   |
394 | // the Distance only represents the distance to the plane if the plane is normalized. If it is not normalized, this  |
395 | // is MOT a multiplier to get the normal to the plane because ax+by+cz+d=0 is the same plane as kax+kby+kcz+kd=0.  |
396 | // Distance (d) more as the offset of the point (a,b,c) from the origin. They grow and shrink together.  |
397 | float Distance;  |
398 | };  |
399 |   |
400 |   |
401 | class tFrustum  |
402 | {  |
403 | public:  |
404 | tFrustum() { }  |
405 | tFrustum(const tMatrix4& m) { Set(m); }  |
406 | tFrustum(const tPlane p[6]) { Set(p); }  |
407 | tFrustum(const tVector4 v[6]) { Set(v); }  |
408 |   |
409 | void Set(const tMatrix4&);  |
410 | void Set(const tPlane[6]);  |
411 | void Set(const tVector4[6]);  |
412 |   |
413 | // Use to index into the Planes array.  |
414 | enum Plane  |
415 | {  |
416 | Plane_Right,  |
417 | Plane_Left,  |
418 | Plane_Top,  |
419 | Plane_Bottom,  |
420 | Plane_Near,  |
421 | Plane_Far,  |
422 | Plane_NumPlanes  |
423 | };  |
424 |   |
425 | // Normals are interior-facing. The near distance (mNear.mDist) will be the negative of the far.  |
426 | tPlane Planes[int(Plane_NumPlanes)];  |
427 | };  |
428 |   |
429 |   |
430 | // This function finds a small sphere that encloses the supplied points. The algorithm is 'stable' in that continuous  |
431 | // changes in source positions do not result in discontinuities in the output sphere. Runs in expected O(n) time and  |
432 | // finds the smallest, or rather something close to the smallest, sphere that encloses them all. If numpoints is <= 0  |
433 | // or points is nullptr returns false and does not modify the sphere. For numPoints = 1 returns a sphere with the  |
434 | // center at the point and radius minRadius. This also happens if all points have identical positions.  |
435 | //  |
436 | // Uses the Bouncing Bubble solution to the minimal enclosing ball problem. See  |
437 | // "https://en.wikipedia.org/wiki/Bounding_sphere#Bouncing_Bubble". The minRadius must not be too close to zero or  |
438 | // the algorithm will not work. The minRadius is forced to be >= Epsilon if too small a value is passed in. The  |
439 | // implementation is based on Andres Hernandez's code as shown here:  |
440 | // http://stackoverflow.com/questions/17331203/bouncing-bubble-algorithm-for-smallest-enclosing-sphere which is  |
441 | // based on a paper by Tian Bo.  |
442 | bool tComputeSmallEnclosingSphere(tSphere& result, const tVector3* points, int numPoints, float minRadius = 0.00001);  |
443 |   |
444 | // This is an older unstable function for computing a minimal bounding sphere. It is based on Graphics Gems vol II.  |
445 | // Runs fast but is an approximation only. For a precise answer we should use the algorithm found here:  |
446 | // http://www.geometrictools.com/LibFoundation/Containment/Containment.html which is based on the Emo Welzl algorithm  |
447 | // that has an expected O(n) running time and is generally much faster than the Megiddo algorithm that guarantees O(n).  |
448 | // A possible benefit of this unstable implementation is no minimum radius. Performance and result quality have not been  |
449 | // tested.  |
450 | bool tComputeSmallEnclosingSphere_Unstable(tSphere& result, const tVector3* points, int numPoints);  |
451 | float tDistanceToLine(const tVector3& point, const tLine&);  |
452 |   |
453 | // @todo Extend the use of tIntersectResult to the 3D tests.  |
454 | enum class tIntersectResult  |
455 | {  |
456 | None, // No intersection.  |
457 | Parallel,  |
458 | Point, // Intersects at a single point.  |
459 | Segment, // Intersects along a line segment.  |
460 | Coincident // Primitives overlap.  |
461 | };  |
462 |   |
463 | // Intersections. These functions determine if various primitives intersect or not. If the function has the word 'test'  |
464 | // in it, only that determination is made. If the function has the word 'find' in it, the location of intersection is  |
465 | // also computed and returned. 'Find' functions are slower. The functions are named using the convention:  |
466 | // tIntersectTestPrim1Prim2(...) and tIntersectFindPrim1Prim2(...).  |
467 |   |
468 | // Finds the intersection point between 2 2D lines.  |
469 | tIntersectResult tIntersectFindLineLine(const tLine2&, const tLine2&, tVector2& intersection);  |
470 |   |
471 | // The segment intersection functions are currently not mathematically consistent since 'coincident' is returned if  |
472 | // the segments lie on the same line even if they do not intersect. What should be returned in this case is either  |
473 | // 'None' or, if they overlap, a line segment for the overlap region.  |
474 | tIntersectResult tIntersectFindSegSeg(const tLineSeg2&, const tLineSeg2&, tVector2& intersection);  |
475 | tIntersectResult tIntersectFindRaySeg(const tRay2&, const tLineSeg2&, tVector2& intersection);  |
476 |   |
477 | // Returns the number of intersection points found. Only 0, 1, or 2 will ever be returned. You may only read that many  |
478 | // results from the 'intersection' array. If the ray is lined up with one of the rectangle edges there will not be any  |
479 | // results for that edge but you will still receive results for the adjacent edges and the function will still return  |
480 | // 0, 1, or 2. If sides is supplied, it is filled with the tARect2 Side bitfields.  |
481 | int tIntersectFindRayRect(const tRay2&, const tARect2&, tVector2 intersection[2], uint32* sides = nullptr);  |
482 |   |
483 | // The tRay.Dir argument must be normalized. This function modifies t such that the intersection point is given by  |
484 | // tRay.Start + tRay.Dir*t. t holds the closer of the two possible intersections and is always positive. Returns false  |
485 | // if there was no intersection, true otherwise.  |
486 | bool tIntersectFindRaySphere(float& t, const tRay&, const tSphere&);  |
487 | bool tIntersectTestLineSegSphere(const tLineSeg&, const tSphere&);  |
488 |   |
489 | bool tIntersectTestCapsulePoint(const tCapsule&, const tVector3&);  |
490 |   |
491 | // This function requires that ray start from the center of the ellipse. The ellipse's center is ignored and so is the  |
492 | // ray's start. The result 't' is set to how much the ray's dir needs to by multiplied by to land on the ellipse.  |
493 | bool tIntersectFindRayEllipse(float& t, const tRay2&, const tEllipse2&);  |
494 |   |
495 | // This test assumes that the triangle faces one direction. An anticlockwise winding of the verts allows the right-hand  |
496 | // rule to be used in determining the direction of the triangle's normal -- assuming a right-handed coordinate system.  |
497 | bool tIntersectTestRayTriangle(const tRay&, const tTriangle&);  |
498 |   |
499 | // Returns true if the sphere is partly or completely inside the volume of the view frustum.  |
500 | bool tIntersectTestFrustumSphere(const tFrustum&, const tSphere&);  |
501 |   |
502 | // @todo Not implemented.  |
503 | bool tIntersectTestTriangleTriangle(const tTriangle&, const tTriangle&);  |
504 |   |
505 | // @todo Not implemented.  |
506 | bool IntersectFindTriangleTriangle(tLineSeg& intersection, const tTriangle&, const tTriangle&);  |
507 |   |
508 |   |
509 | }  |
510 |   |
511 |   |
512 | // Implementation below this line.  |
513 |   |
514 |   |
515 | inline void tMath::tARect2::AddPoint(const tVector2& p)  |
516 | {  |
517 | Max.x = tMax(p.x, Max.x);  |
518 | Max.y = tMax(p.y, Max.y);  |
519 | Min.x = tMin(p.x, Min.x);  |
520 | Min.y = tMin(p.y, Min.y);  |
521 | }  |
522 |   |
523 |   |
524 | inline void tMath::tARect2::Transform(const tMatrix2& t)  |
525 | {  |
526 | // Transform the four rectangle points to the new space and grow a new rectangle around them.  |
527 | tARect2 rect;  |
528 | rect.AddPoint(t * tVector2(Min.x, Min.y));  |
529 | rect.AddPoint(t * tVector2(Max.x, Min.y));  |
530 | rect.AddPoint(t * tVector2(Min.x, Max.y));  |
531 | rect.AddPoint(t * tVector2(Max.x, Max.y));  |
532 | *this = rect;  |
533 | }  |
534 |   |
535 |   |
536 | inline bool tMath::tARect2::Overlaps(const tARect2& rect) const  |
537 | {  |
538 | tVector2 diff = rect.ComputeCenter() - ComputeCenter();  |
539 | tVector2 extents = rect.ComputeExtents() + ComputeExtents();  |
540 | if ((tAbs(diff.x) <= extents.x) && (tAbs(diff.y) <= extents.y))  |
541 | return true;  |
542 |   |
543 | return false;  |
544 | }  |
545 |   |
546 |   |
547 | inline bool tMath::tARect2::IsPointInsideBias(const tVector2& point, tIntervalBias biasX, tIntervalBias biasY) const  |
548 | {  |
549 | std::function<bool(float,float)> lessX = tBiasLess(biasX);  |
550 | std::function<bool(float,float)> grtrX = tBiasGreater(biasX);  |
551 | bool inX = grtrX(point.x, Min.x) && lessX(point.x, Max.x);  |
552 |   |
553 | std::function<bool(float,float)> lessY = tBiasLess(biasY);  |
554 | std::function<bool(float,float)> grtrY = tBiasGreater(biasY);  |
555 | bool inY = grtrY(point.y, Min.y) && lessY(point.y, Max.y);  |
556 |   |
557 | return inX && inY;  |
558 | }  |
559 |   |
560 |   |
561 | inline void tMath::tABox::AddPoint(const tVector3& p)  |
562 | {  |
563 | Max.x = tMax(p.x, Max.x);  |
564 | Max.y = tMax(p.y, Max.y);  |
565 | Max.z = tMax(p.z, Max.z);  |
566 |   |
567 | Min.x = tMin(p.x, Min.x);  |
568 | Min.y = tMin(p.y, Min.y);  |
569 | Min.z = tMin(p.z, Min.z);  |
570 | }  |
571 |   |
572 |   |
573 | inline bool tMath::tABox::IsPointInsideBias(const tVector3& point, tIntervalBias biasX, tIntervalBias biasY, tIntervalBias biasZ) const  |
574 | {  |
575 | std::function<bool(float,float)> lessX = tBiasLess(biasX);  |
576 | std::function<bool(float,float)> grtrX = tBiasGreater(biasX);  |
577 | bool inX = grtrX(point.x, Min.x) && lessX(point.x, Max.x);  |
578 |   |
579 | std::function<bool(float,float)> lessY = tBiasLess(biasY);  |
580 | std::function<bool(float,float)> grtrY = tBiasGreater(biasY);  |
581 | bool inY = grtrY(point.y, Min.y) && lessY(point.y, Max.y);  |
582 |   |
583 | std::function<bool(float,float)> lessZ = tBiasLess(biasZ);  |
584 | std::function<bool(float,float)> grtrZ = tBiasGreater(biasZ);  |
585 | bool inZ = grtrZ(point.z, Min.z) && lessZ(point.z, Max.z);  |
586 |   |
587 | return inX && inY && inZ;  |
588 | }  |
589 |   |
590 |   |
591 | inline void tMath::tFrustum::Set(const tPlane planes[6])  |
592 | {  |
593 | for (int p = 0; p < 6; p++)  |
594 | Planes[p] = planes[p];  |
595 | }  |
596 |   |
597 |   |
598 | inline void tMath::tFrustum::Set(const tVector4 planes[6])  |
599 | {  |
600 | for (int p = 0; p < 6; p++)  |
601 | Planes[p] = tPlane(planes[p]);  |
602 | }  |
603 |   |
604 |   |
605 | inline bool tMath::tIntersectTestCapsulePoint(const tCapsule& c, const tVector3& p)  |
606 | {  |
607 | return tMath::tIntersectTestLineSegSphere  |
608 | (  |
609 | tLineSeg(c.A, c.B),  |
610 | tSphere(p, c.Radius)  |
611 | );  |
612 | }  |
613 | |