1 /********************************************************************************
2 * ReactPhysics3D physics library, http://www.reactphysics3d.com                 *
3 * Copyright (c) 2010-2020 Daniel Chappuis                                       *
4 *********************************************************************************
5 *                                                                               *
6 * This software is provided 'as-is', without any express or implied warranty.   *
7 * In no event will the authors be held liable for any damages arising from the  *
8 * use of this software.                                                         *
9 *                                                                               *
10 * Permission is granted to anyone to use this software for any purpose,         *
11 * including commercial applications, and to alter it and redistribute it        *
12 * freely, subject to the following restrictions:                                *
13 *                                                                               *
14 * 1. The origin of this software must not be misrepresented; you must not claim *
15 *    that you wrote the original software. If you use this software in a        *
16 *    product, an acknowledgment in the product documentation would be           *
17 *    appreciated but is not required.                                           *
18 *                                                                               *
19 * 2. Altered source versions must be plainly marked as such, and must not be    *
20 *    misrepresented as being the original software.                             *
21 *                                                                               *
22 * 3. This notice may not be removed or altered from any source distribution.    *
23 *                                                                               *
24 ********************************************************************************/
25 
26 // Libraries
27 #include <reactphysics3d/mathematics/mathematics_functions.h>
28 #include <reactphysics3d/mathematics/Vector3.h>
29 #include <reactphysics3d/mathematics/Vector2.h>
30 #include <cassert>
31 
32 using namespace reactphysics3d;
33 
34 
35 // Function to test if two vectors are (almost) equal
approxEqual(const Vector3 & vec1,const Vector3 & vec2,decimal epsilon)36 bool reactphysics3d::approxEqual(const Vector3& vec1, const Vector3& vec2, decimal epsilon) {
37     return approxEqual(vec1.x, vec2.x, epsilon) && approxEqual(vec1.y, vec2.y, epsilon) &&
38            approxEqual(vec1.z, vec2.z, epsilon);
39 }
40 
41 // Function to test if two vectors are (almost) equal
approxEqual(const Vector2 & vec1,const Vector2 & vec2,decimal epsilon)42 bool reactphysics3d::approxEqual(const Vector2& vec1, const Vector2& vec2, decimal epsilon) {
43     return approxEqual(vec1.x, vec2.x, epsilon) && approxEqual(vec1.y, vec2.y, epsilon);
44 }
45 
46 // Compute the barycentric coordinates u, v, w of a point p inside the triangle (a, b, c)
47 // This method uses the technique described in the book Real-Time collision detection by
48 // Christer Ericson.
computeBarycentricCoordinatesInTriangle(const Vector3 & a,const Vector3 & b,const Vector3 & c,const Vector3 & p,decimal & u,decimal & v,decimal & w)49 void reactphysics3d::computeBarycentricCoordinatesInTriangle(const Vector3& a, const Vector3& b, const Vector3& c,
50                                              const Vector3& p, decimal& u, decimal& v, decimal& w) {
51     const Vector3 v0 = b - a;
52     const Vector3 v1 = c - a;
53     const Vector3 v2 = p - a;
54 
55     decimal d00 = v0.dot(v0);
56     decimal d01 = v0.dot(v1);
57     decimal d11 = v1.dot(v1);
58     decimal d20 = v2.dot(v0);
59     decimal d21 = v2.dot(v1);
60 
61     decimal denom = d00 * d11 - d01 * d01;
62     v = (d11 * d20 - d01 * d21) / denom;
63     w = (d00 * d21 - d01 * d20) / denom;
64     u = decimal(1.0) - v - w;
65 }
66 
67 // Clamp a vector such that it is no longer than a given maximum length
clamp(const Vector3 & vector,decimal maxLength)68 Vector3 reactphysics3d::clamp(const Vector3& vector, decimal maxLength) {
69     if (vector.lengthSquare() > maxLength * maxLength) {
70         return vector.getUnit() * maxLength;
71     }
72     return vector;
73 }
74 
75 // Return true if two vectors are parallel
areParallelVectors(const Vector3 & vector1,const Vector3 & vector2)76 bool reactphysics3d::areParallelVectors(const Vector3& vector1, const Vector3& vector2) {
77     return vector1.cross(vector2).lengthSquare() < decimal(0.00001);
78 }
79 
80 // Return true if two vectors are orthogonal
areOrthogonalVectors(const Vector3 & vector1,const Vector3 & vector2)81 bool reactphysics3d::areOrthogonalVectors(const Vector3& vector1, const Vector3& vector2) {
82     return std::abs(vector1.dot(vector2)) < decimal(0.001);
83 }
84 
85 // Compute and return a point on segment from "segPointA" and "segPointB" that is closest to point "pointC"
computeClosestPointOnSegment(const Vector3 & segPointA,const Vector3 & segPointB,const Vector3 & pointC)86 Vector3 reactphysics3d::computeClosestPointOnSegment(const Vector3& segPointA, const Vector3& segPointB, const Vector3& pointC) {
87 
88 	const Vector3 ab = segPointB - segPointA;
89 
90 	decimal abLengthSquare = ab.lengthSquare();
91 
92 	// If the segment has almost zero length
93 	if (abLengthSquare < MACHINE_EPSILON) {
94 
95 		// Return one end-point of the segment as the closest point
96 		return segPointA;
97 	}
98 
99 	// Project point C onto "AB" line
100 	decimal t = (pointC - segPointA).dot(ab) / abLengthSquare;
101 
102 	// If projected point onto the line is outside the segment, clamp it to the segment
103 	if (t < decimal(0.0)) t = decimal(0.0);
104 	if (t > decimal(1.0)) t = decimal(1.0);
105 
106 	// Return the closest point on the segment
107 	return segPointA + t * ab;
108 }
109 
110 // Compute the closest points between two segments
111 // This method uses the technique described in the book Real-Time
112 // collision detection by Christer Ericson.
computeClosestPointBetweenTwoSegments(const Vector3 & seg1PointA,const Vector3 & seg1PointB,const Vector3 & seg2PointA,const Vector3 & seg2PointB,Vector3 & closestPointSeg1,Vector3 & closestPointSeg2)113 void reactphysics3d::computeClosestPointBetweenTwoSegments(const Vector3& seg1PointA, const Vector3& seg1PointB,
114 										   const Vector3& seg2PointA, const Vector3& seg2PointB,
115 										   Vector3& closestPointSeg1, Vector3& closestPointSeg2) {
116 
117 	const Vector3 d1 = seg1PointB - seg1PointA;
118 	const Vector3 d2 = seg2PointB - seg2PointA;
119 	const Vector3 r = seg1PointA - seg2PointA;
120 	decimal a = d1.lengthSquare();
121 	decimal e = d2.lengthSquare();
122 	decimal f = d2.dot(r);
123 	decimal s, t;
124 
125 	// If both segments degenerate into points
126 	if (a <= MACHINE_EPSILON && e <= MACHINE_EPSILON) {
127 
128 		closestPointSeg1 = seg1PointA;
129 		closestPointSeg2 = seg2PointA;
130 		return;
131 	}
132 	if (a <= MACHINE_EPSILON) {   // If first segment degenerates into a point
133 
134 		s = decimal(0.0);
135 
136 		// Compute the closest point on second segment
137 		t = clamp(f / e, decimal(0.0), decimal(1.0));
138 	}
139 	else {
140 
141 		decimal c = d1.dot(r);
142 
143 		// If the second segment degenerates into a point
144 		if (e <= MACHINE_EPSILON) {
145 
146 			t = decimal(0.0);
147 			s = clamp(-c / a, decimal(0.0), decimal(1.0));
148 		}
149 		else {
150 
151 			decimal b = d1.dot(d2);
152 			decimal denom = a * e - b * b;
153 
154 			// If the segments are not parallel
155 			if (denom != decimal(0.0)) {
156 
157 				// Compute the closest point on line 1 to line 2 and
158 				// clamp to first segment.
159 				s = clamp((b * f - c * e) / denom, decimal(0.0), decimal(1.0));
160 			}
161 			else {
162 
163 				// Pick an arbitrary point on first segment
164 				s = decimal(0.0);
165 			}
166 
167 			// Compute the point on line 2 closest to the closest point
168 			// we have just found
169 			t = (b * s + f) / e;
170 
171 			// If this closest point is inside second segment (t in [0, 1]), we are done.
172 			// Otherwise, we clamp the point to the second segment and compute again the
173 			// closest point on segment 1
174 			if (t < decimal(0.0)) {
175 				t = decimal(0.0);
176 				s = clamp(-c / a, decimal(0.0), decimal(1.0));
177 			}
178 			else if (t > decimal(1.0)) {
179 				t = decimal(1.0);
180 				s = clamp((b - c) / a, decimal(0.0), decimal(1.0));
181 			}
182 		}
183 	}
184 
185 	// Compute the closest points on both segments
186 	closestPointSeg1 = seg1PointA + d1 * s;
187 	closestPointSeg2 = seg2PointA + d2 * t;
188 }
189 
190 // Compute the intersection between a plane and a segment
191 // Let the plane define by the equation planeNormal.dot(X) = planeD with X a point on the plane and "planeNormal" the plane normal. This method
192 // computes the intersection P between the plane and the segment (segA, segB). The method returns the value "t" such
193 // that P = segA + t * (segB - segA). Note that it only returns a value in [0, 1] if there is an intersection. Otherwise,
194 // there is no intersection between the plane and the segment.
computePlaneSegmentIntersection(const Vector3 & segA,const Vector3 & segB,const decimal planeD,const Vector3 & planeNormal)195 decimal reactphysics3d::computePlaneSegmentIntersection(const Vector3& segA, const Vector3& segB, const decimal planeD, const Vector3& planeNormal) {
196 
197     const decimal parallelEpsilon = decimal(0.0001);
198 	decimal t = decimal(-1);
199 
200     decimal nDotAB = planeNormal.dot(segB - segA);
201 
202 	// If the segment is not parallel to the plane
203     if (std::abs(nDotAB) > parallelEpsilon) {
204 		t = (planeD - planeNormal.dot(segA)) / nDotAB;
205 	}
206 
207 	return t;
208 }
209 
210 // Compute the distance between a point "point" and a line given by the points "linePointA" and "linePointB"
computePointToLineDistance(const Vector3 & linePointA,const Vector3 & linePointB,const Vector3 & point)211 decimal reactphysics3d::computePointToLineDistance(const Vector3& linePointA, const Vector3& linePointB, const Vector3& point) {
212 
213 	decimal distAB = (linePointB - linePointA).length();
214 
215 	if (distAB < MACHINE_EPSILON) {
216 		return (point - linePointA).length();
217 	}
218 
219 	return ((point - linePointA).cross(point - linePointB)).length() / distAB;
220 }
221 
222 // Clip a segment against multiple planes and return the clipped segment vertices
223 // This method implements the Sutherland–Hodgman clipping algorithm
clipSegmentWithPlanes(const Vector3 & segA,const Vector3 & segB,const List<Vector3> & planesPoints,const List<Vector3> & planesNormals,MemoryAllocator & allocator)224 List<Vector3> reactphysics3d::clipSegmentWithPlanes(const Vector3& segA, const Vector3& segB,
225                                                            const List<Vector3>& planesPoints,
226                                                            const List<Vector3>& planesNormals,
227                                                            MemoryAllocator& allocator) {
228     assert(planesPoints.size() == planesNormals.size());
229 
230     List<Vector3> inputVertices(allocator, 2);
231     List<Vector3> outputVertices(allocator, 2);
232 
233     inputVertices.add(segA);
234     inputVertices.add(segB);
235 
236     // For each clipping plane
237     for (uint p=0; p<planesPoints.size(); p++) {
238 
239         // If there is no more vertices, stop
240         if (inputVertices.size() == 0) return inputVertices;
241 
242         assert(inputVertices.size() == 2);
243 
244         outputVertices.clear();
245 
246         Vector3& v1 = inputVertices[0];
247         Vector3& v2 = inputVertices[1];
248 
249         decimal v1DotN = (v1 - planesPoints[p]).dot(planesNormals[p]);
250         decimal v2DotN = (v2 - planesPoints[p]).dot(planesNormals[p]);
251 
252         // If the second vertex is in front of the clippling plane
253         if (v2DotN >= decimal(0.0)) {
254 
255             // If the first vertex is not in front of the clippling plane
256             if (v1DotN < decimal(0.0)) {
257 
258                 // The second point we keep is the intersection between the segment v1, v2 and the clipping plane
259                 decimal t = computePlaneSegmentIntersection(v1, v2, planesNormals[p].dot(planesPoints[p]), planesNormals[p]);
260 
261                 if (t >= decimal(0) && t <= decimal(1.0)) {
262                     outputVertices.add(v1 + t * (v2 - v1));
263                 }
264                 else {
265                     outputVertices.add(v2);
266                 }
267             }
268             else {
269                 outputVertices.add(v1);
270             }
271 
272             // Add the second vertex
273             outputVertices.add(v2);
274         }
275         else {  // If the second vertex is behind the clipping plane
276 
277             // If the first vertex is in front of the clippling plane
278             if (v1DotN >= decimal(0.0)) {
279 
280                 outputVertices.add(v1);
281 
282                 // The first point we keep is the intersection between the segment v1, v2 and the clipping plane
283                 decimal t = computePlaneSegmentIntersection(v1, v2, -planesNormals[p].dot(planesPoints[p]), -planesNormals[p]);
284 
285                 if (t >= decimal(0.0) && t <= decimal(1.0)) {
286                     outputVertices.add(v1 + t * (v2 - v1));
287                 }
288             }
289         }
290 
291         inputVertices = outputVertices;
292     }
293 
294     return outputVertices;
295 }
296 
297 // Clip a polygon against multiple planes and return the clipped polygon vertices
298 // This method implements the Sutherland–Hodgman clipping algorithm
clipPolygonWithPlanes(const List<Vector3> & polygonVertices,const List<Vector3> & planesPoints,const List<Vector3> & planesNormals,MemoryAllocator & allocator)299 List<Vector3> reactphysics3d::clipPolygonWithPlanes(const List<Vector3>& polygonVertices, const List<Vector3>& planesPoints,
300                                                     const List<Vector3>& planesNormals, MemoryAllocator& allocator) {
301 
302     assert(planesPoints.size() == planesNormals.size());
303 
304         uint nbMaxElements = polygonVertices.size() + planesPoints.size();
305         List<Vector3> inputVertices(allocator, nbMaxElements);
306         List<Vector3> outputVertices(allocator, nbMaxElements);
307 
308         inputVertices.addRange(polygonVertices);
309 
310         // For each clipping plane
311         for (uint p=0; p<planesPoints.size(); p++) {
312 
313             outputVertices.clear();
314 
315             uint nbInputVertices = inputVertices.size();
316             uint vStart = nbInputVertices - 1;
317 
318             // For each edge of the polygon
319             for (uint vEnd = 0; vEnd<nbInputVertices; vEnd++) {
320 
321                 Vector3& v1 = inputVertices[vStart];
322                 Vector3& v2 = inputVertices[vEnd];
323 
324                 decimal v1DotN = (v1 - planesPoints[p]).dot(planesNormals[p]);
325                 decimal v2DotN = (v2 - planesPoints[p]).dot(planesNormals[p]);
326 
327                 // If the second vertex is in front of the clippling plane
328                 if (v2DotN >= decimal(0.0)) {
329 
330                     // If the first vertex is not in front of the clippling plane
331                     if (v1DotN < decimal(0.0)) {
332 
333                         // The second point we keep is the intersection between the segment v1, v2 and the clipping plane
334                         decimal t = computePlaneSegmentIntersection(v1, v2, planesNormals[p].dot(planesPoints[p]), planesNormals[p]);
335 
336                         if (t >= decimal(0) && t <= decimal(1.0)) {
337                             outputVertices.add(v1 + t * (v2 - v1));
338                         }
339                         else {
340                             outputVertices.add(v2);
341                         }
342                     }
343 
344                     // Add the second vertex
345                     outputVertices.add(v2);
346                 }
347                 else {  // If the second vertex is behind the clipping plane
348 
349                     // If the first vertex is in front of the clippling plane
350                     if (v1DotN >= decimal(0.0)) {
351 
352                         // The first point we keep is the intersection between the segment v1, v2 and the clipping plane
353                         decimal t = computePlaneSegmentIntersection(v1, v2, -planesNormals[p].dot(planesPoints[p]), -planesNormals[p]);
354 
355                         if (t >= decimal(0.0) && t <= decimal(1.0)) {
356                             outputVertices.add(v1 + t * (v2 - v1));
357                         }
358                         else {
359                             outputVertices.add(v1);
360                         }
361                     }
362                 }
363 
364                 vStart = vEnd;
365             }
366 
367             inputVertices = outputVertices;
368         }
369 
370         return outputVertices;
371 }
372 
373 // Project a point onto a plane that is given by a point and its unit length normal
projectPointOntoPlane(const Vector3 & point,const Vector3 & unitPlaneNormal,const Vector3 & planePoint)374 Vector3 reactphysics3d::projectPointOntoPlane(const Vector3& point, const Vector3& unitPlaneNormal, const Vector3& planePoint) {
375 	return point - unitPlaneNormal.dot(point - planePoint) * unitPlaneNormal;
376 }
377 
378 // Return the distance between a point and a plane (the plane normal must be normalized)
computePointToPlaneDistance(const Vector3 & point,const Vector3 & planeNormal,const Vector3 & planePoint)379 decimal reactphysics3d::computePointToPlaneDistance(const Vector3& point, const Vector3& planeNormal, const Vector3& planePoint) {
380     return planeNormal.dot(point - planePoint);
381 }
382 
383 // Return true if the given number is prime
isPrimeNumber(int number)384 bool reactphysics3d::isPrimeNumber(int number) {
385 
386     // If it's a odd number
387     if ((number & 1) != 0) {
388 
389         int limit = static_cast<int>(std::sqrt(number));
390 
391         for (int divisor = 3; divisor <= limit; divisor += 2) {
392 
393             // If we have found a divisor
394             if ((number % divisor) == 0) {
395 
396                 // It is not a prime number
397                 return false;
398             }
399         }
400 
401         return true;
402     }
403 
404     return number == 2;
405 }
406 
407 // Return an unique integer from two integer numbers (pairing function)
408 /// Here we assume that the two parameter numbers are sorted such that
409 /// number1 = max(number1, number2)
410 /// http://szudzik.com/ElegantPairing.pdf
pairNumbers(uint32 number1,uint32 number2)411 uint64 reactphysics3d::pairNumbers(uint32 number1, uint32 number2) {
412     assert(number1 == std::max(number1, number2));
413     return number1 * number1 + number1 + number2;
414 }
415 
416