Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
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 
12 namespace Ra {
13 namespace Core {
14 namespace Geometry {
15 //
16 // Point-to-line distance
17 //
18 
20 inline RA_CORE_API Scalar pointToLineSq( const Vector3& q, const Vector3& a, const Vector3& dir );
21 
24 inline RA_CORE_API Scalar projectOnSegment( const Vector3& q, const Vector3& a, const Vector3& ab );
25 
28 inline 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 } ),
41  distanceSquared( std::numeric_limits<Scalar>::max() ),
42  flags( 0 ) {}
43 
44  Vector3 meshPoint;
45  Scalar distanceSquared;
46  uchar flags;
49 
51  inline Flags getHitPrimitive() const { return Flags( flags & 0x3u ); }
54  uint getHitIndex() const { return ( flags & 0xcu ) >> 2; }
55 };
56 
58 inline 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 
76 inline 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 
96 inline 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 
114 inline 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 
132 inline RA_CORE_API TriangleToTriangleOutput triangleToTriSq( const Vector3 v1[3],
133  const Vector3 v2[3] );
134 
135 // Line funcs
136 
137 inline 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 
142 inline 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 
150 inline 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
158 enum 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 
170 inline 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 
289 inline 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 
349 inline 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 
451 inline 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 
505 inline 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
Definition: Cage.cpp:3
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.