Radium Engine  1.5.20
Loading...
Searching...
No Matches
DistanceQueries.hpp
1#pragma once
2
3#include <Core/Geometry/TriangleMesh.hpp>
4#include <Core/RaCore.hpp>
5#include <Core/Types.hpp>
6
7#include <algorithm>
8#include <limits>
9
12namespace Ra {
13namespace Core {
14namespace Geometry {
15//
16// Point-to-line distance
17//
18
20inline RA_CORE_API Scalar pointToLineSq( const Vector3& q, const Vector3& a, const Vector3& dir );
21
24inline RA_CORE_API Scalar projectOnSegment( const Vector3& q, const Vector3& a, const Vector3& ab );
25
28inline RA_CORE_API Scalar pointToSegmentSq( const Vector3& q, const Vector3& a, const Vector3& ab );
29
30//
31// Point-to-triangle distance
32//
33
37 enum Flags { HIT_FACE = 0, HIT_VERTEX = 1, HIT_EDGE = 2 };
38
40 meshPoint( { 0, 0, 0 } ),
42 flags( 0 ) {}
43
44 Vector3 meshPoint;
46 uchar flags;
49
51 inline Flags getHitPrimitive() const { return Flags( flags & 0x3u ); }
54 uint getHitIndex() const { return ( flags & 0xcu ) >> 2; }
55};
56
58inline RA_CORE_API PointToTriangleOutput pointToTriSq( const Vector3& q,
59 const Vector3& a,
60 const Vector3& b,
61 const Vector3& c );
62
63//
64// Line-to-segment distance
65//
66
69 Scalar distance;
70 Scalar sqrDistance;
71 Scalar parameter[2];
72 Vector3 closestPoint[2];
73};
74
76inline RA_CORE_API LineToSegmentOutput lineToSegSq( const Vector3& lineOrigin,
77 Vector3 lineDirection,
78 const Vector3& segCenter,
79 Vector3 segDirection,
80 const Scalar segExtent );
81
82//
83// Line-to-triangle distance
84//
85
88 Scalar distance;
89 Scalar sqrDistance;
90 Scalar lineParameter;
91 Scalar triangleParameter[3];
92 Vector3 closestPoint[2];
93};
94
96inline RA_CORE_API LineToTriangleOutput lineToTriSq( const Vector3& lineOrigin,
97 Vector3 lineDirection,
98 const Vector3 v[3] );
99
100//
101// Segment-to-triangle distance
102//
103
106 Scalar distance;
107 Scalar sqrDistance;
108 Scalar segmentParameter;
109 Scalar triangleParameter[3];
110 Vector3 closestPoint[2];
111};
112
114inline RA_CORE_API SegmentToTriangleOutput segmentToTriSq( const Vector3& segCenter,
115 Vector3 segDirection,
116 const Scalar segExtent,
117 const Vector3 v[3] );
118
119//
120// Triangle-to-triangle distance
121
124 Scalar distance;
125 Scalar sqrDistance;
126 Scalar triangleParameter1[3];
127 Scalar triangleParameter2[3];
128 Vector3 closestPoint[2];
129};
130
132inline RA_CORE_API TriangleToTriangleOutput triangleToTriSq( const Vector3 v1[3],
133 const Vector3 v2[3] );
134
135// Line funcs
136
137inline RA_CORE_API Scalar pointToLineSq( const Vector3& q, const Vector3& a, const Vector3& dir ) {
138 // http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
139 return ( dir.cross( q - a ) ).squaredNorm() / dir.squaredNorm();
140}
141
142inline RA_CORE_API Scalar projectOnSegment( const Vector3& q,
143 const Vector3& a,
144 const Vector3& ab ) {
145 // Edge case : segment has length 0
146 if ( UNLIKELY( ab.squaredNorm() == 0 ) ) { return 0; }
147 return std::clamp( ( q - a ).dot( ab ) / ( ab.squaredNorm() ), Scalar( 0 ), Scalar( 1 ) );
148}
149
150inline RA_CORE_API Scalar pointToSegmentSq( const Vector3& q,
151 const Vector3& a,
152 const Vector3& ab ) {
153 const Scalar t = projectOnSegment( q, a, ab );
154 return ( q - ( a + t * ( ab ) ) ).squaredNorm();
155}
156
157// Triangle funcs
158enum FlagsInternal {
159 HIT_FACE = PointToTriangleOutput::HIT_FACE,
160
161 HIT_A = 0x0 | PointToTriangleOutput::HIT_VERTEX,
162 HIT_B = 0x4 | PointToTriangleOutput::HIT_VERTEX,
163 HIT_C = 0x8 | PointToTriangleOutput::HIT_VERTEX,
164
165 HIT_AB = 0x0 | PointToTriangleOutput::HIT_EDGE,
166 HIT_BC = 0x4 | PointToTriangleOutput::HIT_EDGE,
167 HIT_CA = 0x8 | PointToTriangleOutput::HIT_EDGE,
168};
169
170inline RA_CORE_API PointToTriangleOutput pointToTriSq( const Vector3& q,
171 const Vector3& a,
172 const Vector3& b,
173 const Vector3& c ) {
174 /*
175 * This function projects the query point Q on the triangle plane to solve the
176 * planar problem described by the following schema of Voronoi zones :
177 * 3 /
178 * -- C
179 * |\\ 6
180 * | \\
181 * 5 | \\ /
182 * | \\ /
183 * -- A ------ B 2
184 * | |
185 * 1 | 4 |
186 *
187 * It first checks if the nearest point is a vertex (zones 1,2,3), then if
188 * it belongs to an edge (zone, 4,5,6). If not the nearest point lies on the
189 * triangle.
190 * Reference : Fast Distance Queries for Triangles, Lines and Points using SSE instructions
191 * Evan Shellshear, Robin Ytterlid.
192 * http://jcgt.org/published/0003/04/05/paper.pdf
193 * Journal of Computer Graphics Techniques, Vol.3 No. 4 (2014)
194 */
195
196 PointToTriangleOutput output;
197
198 const Vector3 ab = b - a;
199 const Vector3 ac = c - a;
200 const Vector3 aq = q - a;
201
202 CORE_ASSERT( ab.cross( ac ).squaredNorm() > 0, "Triangle ABC is degenerate" );
203
204 const Scalar d1 = ab.dot( aq );
205 const Scalar d2 = ac.dot( aq );
206
207 // Closest point is A. (zone 1)
208 const bool m1 = d1 <= 0 && d2 <= 0;
209 if ( m1 ) {
210 output.meshPoint = a;
211 output.distanceSquared = aq.squaredNorm();
212 output.flags = FlagsInternal::HIT_A;
213 return output;
214 }
215
216 const Vector3 bq = q - b;
217 const Scalar d3 = ab.dot( bq );
218 const Scalar d4 = ac.dot( bq );
219
220 // Closest point is B (zone 2)
221 const bool m2 = d3 >= 0 && d4 <= d3;
222 if ( m2 ) {
223 output.meshPoint = b;
224 output.distanceSquared = bq.squaredNorm();
225 output.flags = FlagsInternal::HIT_B;
226 return output;
227 }
228
229 const Vector3 cq = q - c;
230 const Scalar d5 = ab.dot( cq );
231 const Scalar d6 = ac.dot( cq );
232
233 // Closest point is C (zone 3)
234 const bool m3 = ( d6 >= 0 && d5 <= d6 );
235 if ( m3 ) {
236 output.meshPoint = c;
237 output.distanceSquared = cq.squaredNorm();
238 output.flags = FlagsInternal::HIT_C;
239 return output;
240 }
241
242 const Scalar vc = d1 * d4 - d3 * d2;
243 const Scalar v1 = d1 / ( d1 - d3 );
244
245 // Closest point is on AB (zone 4)
246 const bool m4 = vc <= 0 && d1 >= 0 && d3 <= 0;
247 if ( m4 ) {
248 output.meshPoint = a + v1 * ab;
249 output.distanceSquared = ( output.meshPoint - q ).squaredNorm();
250 output.flags = FlagsInternal::HIT_AB;
251 return output;
252 }
253
254 const Scalar vb = d5 * d2 - d1 * d6;
255 const Scalar w1 = d2 / ( d2 - d6 );
256
257 // Closest point is on AC (zone 5)
258 const bool m5 = vb <= 0 && d2 >= 0 && d6 <= 0;
259 if ( m5 ) {
260 output.meshPoint = a + w1 * ac;
261 output.distanceSquared = ( output.meshPoint - q ).squaredNorm();
262 output.flags = FlagsInternal::HIT_CA;
263 return output;
264 }
265
266 const Scalar va = d3 * d6 - d5 * d4;
267 const Scalar w2 = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );
268
269 // Closest point is on BC (zone 6)
270 const bool m6 = va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0;
271 if ( m6 ) {
272 output.meshPoint = b + w2 * ( c - b );
273 output.distanceSquared = ( output.meshPoint - q ).squaredNorm();
274 output.flags = FlagsInternal::HIT_BC;
275 return output;
276 }
277
278 // Closest point is on the triangle
279 const Scalar d = Scalar( 1 ) / ( va + vb + vc );
280 const Scalar v2 = vb * d;
281 const Scalar w3 = vc * d;
282
283 output.meshPoint = a + v2 * ab + w3 * ac;
284 output.distanceSquared = ( output.meshPoint - q ).squaredNorm();
285 output.flags = 0;
286 return output;
287}
288
289inline RA_CORE_API LineToSegmentOutput lineToSegSq( const Vector3& lineOrigin,
290 Vector3 lineDirection,
291 const Vector3& segCenter,
292 Vector3 segDirection,
293 const Scalar segExtent ) {
294 // Reference : Geometric Tools,
295 // https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/GteDistLineSegment.h
296
297 LineToSegmentOutput output;
298
299 segDirection.normalize();
300 lineDirection.normalize();
301
302 Vector3 diff = lineOrigin - segCenter;
303 Scalar a01 = -lineDirection.dot( segDirection );
304 Scalar b0 = diff.dot( lineDirection );
305 Scalar s0, s1;
306
307 if ( std::abs( a01 ) < (Scalar)1 ) {
308 // The line and the segment are not parallel
309 Scalar det = (Scalar)1 - a01 * a01;
310 Scalar extDet = segExtent * det;
311 Scalar b1 = -diff.dot( segDirection );
312 s1 = a01 * b0 - b1;
313
314 if ( s1 >= -extDet ) {
315 if ( s1 <= extDet ) {
316 // 2 interior points are closest, one on the line and one on the segment
317 s0 = ( a01 * b1 - b0 ) / det;
318 s1 /= det;
319 }
320 else {
321 // The endpoint e1 of the segment and an interior point of the line are closest
322 s1 = segExtent;
323 s0 = -( a01 * s1 + b0 );
324 }
325 }
326 else {
327 // The endpoint e0 of the segment and an interior point of the line are closest
328 s1 = -segExtent;
329 s0 = -( a01 * s1 + b0 );
330 }
331 }
332 else {
333 // The line and the segment are parallel
334 // We choose the closest pair so that one point is at the segment origin
335 s1 = (Scalar)0;
336 s0 = -b0;
337 }
338
339 output.parameter[0] = s0;
340 output.parameter[1] = s1;
341 output.closestPoint[0] = lineOrigin + s0 * lineDirection;
342 output.closestPoint[1] = segCenter + s1 * segDirection;
343 diff = output.closestPoint[0] - output.closestPoint[1];
344 output.sqrDistance = diff.dot( diff );
345 output.distance = std::sqrt( output.sqrDistance );
346 return output;
347}
348
349inline RA_CORE_API LineToTriangleOutput lineToTriSq( const Vector3& lineOrigin,
350 Vector3 lineDirection,
351 const Vector3 v[3] ) {
352 // Reference : GeometricTools,
353 // https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/GteDistLine3Triangle3.h
354
355 LineToTriangleOutput output;
356
357 lineDirection.normalize();
358
359 Vector3 edge0 = v[1] - v[0];
360 Vector3 edge1 = v[2] - v[0];
361 Vector3 normal = edge0.cross( edge1 );
362 normal.normalize();
363 Scalar nd = normal.dot( lineDirection );
364 if ( std::abs( nd ) > (Scalar)0 ) {
365 // The line intersects the plane of the triangle or the triangle itself
366 // (if the intersection point isn't needed, it possible to use the boolean function
367 // vsTriangle of RayCast.hpp)
368 Vector3 diff = lineOrigin - v[0];
369 Vector3 basis[3]; // {D, U, V}
370 basis[0] = lineDirection;
371 if ( std::abs( basis[0][0] ) > std::abs( basis[0][1] ) ) {
372 basis[1] = { -basis[0][2], (Scalar)0, basis[0][0] };
373 }
374 else { basis[1] = { (Scalar)0, basis[0][2], -basis[0][1] }; }
375 basis[2] = basis[0].cross( basis[1] );
376 // Orthonormalize basis
377 // Normalize basis[0]
378 basis[0] /= std::sqrt( basis[0].dot( basis[0] ) );
379 basis[1] -= basis[0] * ( basis[1].dot( basis[0] ) );
380 // Normalize basis[1]
381 basis[1] /= std::sqrt( basis[1].dot( basis[1] ) );
382 basis[2] -= basis[0] * ( basis[2].dot( basis[0] ) );
383 basis[2] -= basis[1] * ( basis[2].dot( basis[1] ) );
384 // Normalize basis[2]
385 basis[2] /= std::sqrt( basis[2].dot( basis[2] ) );
386
387 Scalar UdE0 = basis[1].dot( edge0 );
388 Scalar UdE1 = basis[1].dot( edge1 );
389 Scalar UdDiff = basis[1].dot( diff );
390 Scalar VdE0 = basis[2].dot( edge0 );
391 Scalar VdE1 = basis[2].dot( edge1 );
392 Scalar VdDiff = basis[2].dot( diff );
393 Scalar invDet = (Scalar)1 / ( UdE0 * VdE1 - UdE1 * VdE0 );
394
395 // Barycentric coordinates for the point of intersection
396 Scalar b1 = ( VdE1 * UdDiff - UdE1 * VdDiff ) * invDet;
397 Scalar b2 = ( UdE0 * VdDiff - VdE0 * UdDiff ) * invDet;
398 Scalar b0 = (Scalar)1 - b1 - b2;
399
400 if ( b0 >= (Scalar)0 && b1 >= (Scalar)0 && b2 >= (Scalar)0 ) {
401 // The line intersects the triangle itself
402 // Distance between the origin of the line and the intersection point with the triangle
403 Scalar DdE0 = lineDirection.dot( edge0 );
404 Scalar DdE1 = lineDirection.dot( edge1 );
405 Scalar DdDiff = lineDirection.dot( diff );
406 output.lineParameter = b1 * DdE0 + b2 * DdE1 - DdDiff;
407
408 // Barycentric coordinates for the point of intersection
409 output.triangleParameter[0] = b0;
410 output.triangleParameter[1] = b1;
411 output.triangleParameter[2] = b2;
412
413 // The intersection point is inside or on the triangle
414 output.closestPoint[0] = lineOrigin + output.lineParameter * lineDirection;
415 output.closestPoint[1] = v[0] + b1 * edge0 + b2 * edge1;
416
417 output.distance = (Scalar)0;
418 output.sqrDistance = (Scalar)0;
419 return output;
420 }
421 }
422
423 // Either the line intersects the plane of the triangle but not the triangle itself, or the line
424 // is parallel to the triangle. Regardless, the closest point on the triangle is on an edge of
425 // the triangle, so we compare the line to all 3 edges of the triangle.
426 output.distance = std::numeric_limits<Scalar>::max();
427 output.sqrDistance = std::numeric_limits<Scalar>::max();
428 for ( int i0 = 2, i1 = 0; i1 < 3; i0 = i1++ ) {
429 Vector3 segCenter = ( (Scalar)0.5 ) * ( v[i0] + v[i1] );
430 Vector3 segDirection = v[i1] - v[i0];
431 Scalar segExtent = ( (Scalar)0.5 ) * std::sqrt( segDirection.dot( segDirection ) );
432
433 // Distance line-segment is computed
434 LineToSegmentOutput lsOutput =
435 lineToSegSq( lineOrigin, lineDirection, segCenter, segDirection, segExtent );
436 if ( lsOutput.sqrDistance < output.sqrDistance ) {
437 output.sqrDistance = lsOutput.sqrDistance;
438 output.distance = lsOutput.distance;
439 output.lineParameter = lsOutput.parameter[0];
440 output.triangleParameter[i0] =
441 ( (Scalar)0.5 ) * ( (Scalar)1 - lsOutput.parameter[0] / segExtent );
442 output.triangleParameter[i1] = (Scalar)1 - output.triangleParameter[i0];
443 output.triangleParameter[3 - i0 - i1] = (Scalar)0;
444 output.closestPoint[0] = lsOutput.closestPoint[0];
445 output.closestPoint[1] = lsOutput.closestPoint[1];
446 }
447 }
448 return output;
449}
450
451inline RA_CORE_API SegmentToTriangleOutput segmentToTriSq( const Vector3& segCenter,
452 Vector3 segDirection,
453 Scalar segExtent,
454 const Vector3 v[3] ) {
455 // Reference : Geometric Tools,
456 // https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/GteDistSegment3Triangle3.h
457
458 SegmentToTriangleOutput output;
459
460 segDirection.normalize();
461
462 // Distance line-triangle is computed
463 LineToTriangleOutput ltOutput = lineToTriSq( segCenter, segDirection, v );
464
465 if ( ltOutput.lineParameter >= -segExtent ) {
466 // The closest point on the line is on the segment or at its right
467 if ( ltOutput.lineParameter <= segExtent ) {
468 // The closest point on the line is on the segment
469 // The segment intersects the triangle
470 output.distance = ltOutput.distance;
471 output.sqrDistance = ltOutput.sqrDistance;
472 output.segmentParameter = ltOutput.lineParameter;
473 output.triangleParameter[0] = ltOutput.triangleParameter[0];
474 output.triangleParameter[1] = ltOutput.triangleParameter[1];
475 output.triangleParameter[2] = ltOutput.triangleParameter[2];
476 output.closestPoint[0] = ltOutput.closestPoint[0];
477 output.closestPoint[1] = ltOutput.closestPoint[1];
478 }
479 else {
480 // The closest point on the line is at the segment's right
481 // We compute the distance between the right endpoint of the segment and the triangle
482 Vector3 point = segCenter + segExtent * segDirection;
483 PointToTriangleOutput ptOutput = pointToTriSq( point, v[0], v[1], v[2] );
484 output.sqrDistance = ptOutput.distanceSquared;
485 output.distance = std::sqrt( output.sqrDistance );
486 output.segmentParameter = segExtent;
487 output.closestPoint[0] = point;
488 output.closestPoint[1] = ptOutput.meshPoint;
489 }
490 }
491 else {
492 // The closest point on the line is at the segment's left
493 // We compute the distance between the left endpoint of the segment and the triangle
494 Vector3 point = segCenter - segExtent * segDirection;
495 PointToTriangleOutput ptOutput = pointToTriSq( point, v[0], v[1], v[2] );
496 output.sqrDistance = ptOutput.distanceSquared;
497 output.distance = std::sqrt( output.sqrDistance );
498 output.segmentParameter = segExtent;
499 output.closestPoint[0] = point;
500 output.closestPoint[1] = ptOutput.meshPoint;
501 }
502 return output;
503}
504
505inline RA_CORE_API TriangleToTriangleOutput triangleToTriSq( const Vector3 v1[3],
506 const Vector3 v2[3] ) {
507 TriangleToTriangleOutput output;
508
509 SegmentToTriangleOutput stOutput;
510 output.sqrDistance = std::numeric_limits<Scalar>::max();
511
512 // We compute the closest distance between v1's edges and v2.
513 for ( int i0 = 2, i1 = 0; i1 < 3; i0 = i1++ ) {
514 Vector3 segCenter = ( (Scalar)0.5 ) * ( v1[i0] + v1[i1] );
515 Vector3 segDirection = v1[i1] - v1[i0];
516 Scalar segExtent = ( (Scalar)0.5 ) * std::sqrt( segDirection.dot( segDirection ) );
517
518 stOutput = segmentToTriSq( segCenter, segDirection, segExtent, v2 );
519 if ( stOutput.sqrDistance < output.sqrDistance ) {
520 output.sqrDistance = stOutput.sqrDistance;
521 output.distance = stOutput.distance;
522 output.triangleParameter1[i0] =
523 ( (Scalar)0.5 ) * ( (Scalar)1 - stOutput.segmentParameter / segExtent );
524 output.triangleParameter1[i1] = (Scalar)1 - output.triangleParameter1[i0];
525 output.triangleParameter1[3 - i0 - i1] = (Scalar)0;
526 output.triangleParameter2[0] = stOutput.triangleParameter[0];
527 output.triangleParameter2[1] = stOutput.triangleParameter[1];
528 output.triangleParameter2[2] = stOutput.triangleParameter[2];
529 output.closestPoint[0] = stOutput.closestPoint[0];
530 output.closestPoint[1] = stOutput.closestPoint[1];
531 }
532 }
533
534 // We compute the closest distance between v2's edges and v1.
535 for ( int i0 = 2, i1 = 0; i1 < 3; i0 = i1++ ) {
536 Vector3 segCenter = ( (Scalar)0.5 ) * ( v2[i0] + v2[i1] );
537 Vector3 segDirection = v2[i1] - v2[i0];
538 Scalar segExtent = ( (Scalar)0.5 ) * std::sqrt( segDirection.dot( segDirection ) );
539
540 stOutput = segmentToTriSq( segCenter, segDirection, segExtent, v1 );
541 if ( stOutput.sqrDistance < output.sqrDistance ) {
542 output.sqrDistance = stOutput.sqrDistance;
543 output.distance = stOutput.distance;
544 output.triangleParameter1[0] = stOutput.triangleParameter[0];
545 output.triangleParameter1[1] = stOutput.triangleParameter[1];
546 output.triangleParameter1[2] = stOutput.triangleParameter[2];
547 output.triangleParameter2[i0] =
548 ( (Scalar)0.5 ) * ( (Scalar)1 - stOutput.segmentParameter / segExtent );
549 output.triangleParameter2[i1] = (Scalar)1 - output.triangleParameter2[i0];
550 output.triangleParameter2[3 - i0 - i1] = (Scalar)0;
551 output.closestPoint[0] = stOutput.closestPoint[0];
552 output.closestPoint[1] = stOutput.closestPoint[1];
553 }
554 }
555 return output;
556}
557
558} // namespace Geometry
559} // namespace Core
560} // namespace Ra
T max(T... args)
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T sqrt(T... args)
Structure holding the result of a line-to-segment distance query.
Structure holding the result of a line-to-triangle distance query.
Structure holding the result of a point-to triangle distance query.
Scalar distanceSquared
the point hit on the mesh
Flags
Flags denoting the primitive where the closest point is.
uchar flags
distance squared to the point
Flags getHitPrimitive() const
Return if the hit is a face, a vertex or an edge.
Structure holding the result of a segment-to-triangle distance query.
Structure holding the result of a triangle-to-triangle distance query.