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