RINGMesh  Version 5.0.0
A programming library for geological model meshes
geometry_distance.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012-2017, Association Scientifique pour la Geologie et ses
3  * Applications (ASGA). All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of ASGA nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ASGA BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * http://www.ring-team.org
28  *
29  * RING Project
30  * Ecole Nationale Superieure de Geologie - GeoRessources
31  * 2 Rue du Doyen Marcel Roubault - TSA 70605
32  * 54518 VANDOEUVRE-LES-NANCY
33  * FRANCE
34  */
35 
37 
38 #include <array>
39 
40 #include <geogram/mesh/mesh.h>
41 
49 namespace RINGMesh
50 {
51  namespace Distance
52  {
53  template <>
54  std::tuple< double, vec3 > point_to_triangle(
55  const Geometry::Point3D& point,
56  const Geometry::Triangle3D& triangle )
57  {
58  const vec3& V0 = triangle.p0;
59  const vec3& V1 = triangle.p1;
60  const vec3& V2 = triangle.p2;
61  vec3 diff{ V0 - point };
62  vec3 edge0{ V1 - V0 };
63  vec3 edge1{ V2 - V0 };
64  double a00{ length2( edge0 ) };
65  double a01{ dot( edge0, edge1 ) };
66  double a11{ length2( edge1 ) };
67  double b0{ dot( diff, edge0 ) };
68  double b1{ dot( diff, edge1 ) };
69  double c{ length2( diff ) };
70  double det{ std::fabs( a00 * a11 - a01 * a01 ) };
71  double s{ a01 * b1 - a11 * b0 };
72  double t{ a01 * b0 - a00 * b1 };
73  double sqrDistance;
74 
75  if( s + t <= det )
76  {
77  if( s < 0.0 )
78  {
79  if( t < 0.0 )
80  { // region 4
81  if( b0 < 0.0 )
82  {
83  t = 0.0;
84  if( -b0 >= a00 )
85  {
86  s = 1.0;
87  sqrDistance = a00 + 2.0 * b0 + c;
88  }
89  else
90  {
91  s = -b0 / a00;
92  sqrDistance = b0 * s + c;
93  }
94  }
95  else
96  {
97  s = 0.0;
98  if( b1 >= 0.0 )
99  {
100  t = 0.0;
101  sqrDistance = c;
102  }
103  else if( -b1 >= a11 )
104  {
105  t = 1.0;
106  sqrDistance = a11 + 2.0 * b1 + c;
107  }
108  else
109  {
110  t = -b1 / a11;
111  sqrDistance = b1 * t + c;
112  }
113  }
114  }
115  else
116  { // region 3
117  s = 0.0;
118  if( b1 >= 0.0 )
119  {
120  t = 0.0;
121  sqrDistance = c;
122  }
123  else if( -b1 >= a11 )
124  {
125  t = 1.0;
126  sqrDistance = a11 + 2.0 * b1 + c;
127  }
128  else
129  {
130  t = -b1 / a11;
131  sqrDistance = b1 * t + c;
132  }
133  }
134  }
135  else if( t < 0.0 )
136  { // region 5
137  t = 0.0;
138  if( b0 >= 0.0 )
139  {
140  s = 0.0;
141  sqrDistance = c;
142  }
143  else if( -b0 >= a00 )
144  {
145  s = 1.0;
146  sqrDistance = a00 + 2.0 * b0 + c;
147  }
148  else
149  {
150  s = -b0 / a00;
151  sqrDistance = b0 * s + c;
152  }
153  }
154  else
155  { // region 0
156  // minimum at interior point
157  double invDet = double( 1.0 ) / det;
158  s *= invDet;
159  t *= invDet;
160  sqrDistance = s * ( a00 * s + a01 * t + 2.0 * b0 )
161  + t * ( a01 * s + a11 * t + 2.0 * b1 ) + c;
162  }
163  }
164  else
165  {
166  double tmp0, tmp1, numer, denom;
167 
168  if( s < 0.0 )
169  { // region 2
170  tmp0 = a01 + b0;
171  tmp1 = a11 + b1;
172  if( tmp1 > tmp0 )
173  {
174  numer = tmp1 - tmp0;
175  denom = a00 - 2.0 * a01 + a11;
176  if( numer >= denom )
177  {
178  s = 1.0;
179  t = 0.0;
180  sqrDistance = a00 + 2.0 * b0 + c;
181  }
182  else
183  {
184  s = numer / denom;
185  t = 1.0 - s;
186  sqrDistance = s * ( a00 * s + a01 * t + 2.0 * b0 )
187  + t * ( a01 * s + a11 * t + 2.0 * b1 )
188  + c;
189  }
190  }
191  else
192  {
193  s = 0.0;
194  if( tmp1 <= 0.0 )
195  {
196  t = 1.0;
197  sqrDistance = a11 + 2.0 * b1 + c;
198  }
199  else if( b1 >= 0.0 )
200  {
201  t = 0.0;
202  sqrDistance = c;
203  }
204  else
205  {
206  t = -b1 / a11;
207  sqrDistance = b1 * t + c;
208  }
209  }
210  }
211  else if( t < 0.0 )
212  { // region 6
213  tmp0 = a01 + b1;
214  tmp1 = a00 + b0;
215  if( tmp1 > tmp0 )
216  {
217  numer = tmp1 - tmp0;
218  denom = a00 - 2.0 * a01 + a11;
219  if( numer >= denom )
220  {
221  t = 1.0;
222  s = 0.0;
223  sqrDistance = a11 + 2.0 * b1 + c;
224  }
225  else
226  {
227  t = numer / denom;
228  s = 1.0 - t;
229  sqrDistance = s * ( a00 * s + a01 * t + 2.0 * b0 )
230  + t * ( a01 * s + a11 * t + 2.0 * b1 )
231  + c;
232  }
233  }
234  else
235  {
236  t = 0.0;
237  if( tmp1 <= 0.0 )
238  {
239  s = 1.0;
240  sqrDistance = a00 + 2.0 * b0 + c;
241  }
242  else if( b0 >= 0.0 )
243  {
244  s = 0.0;
245  sqrDistance = c;
246  }
247  else
248  {
249  s = -b0 / a00;
250  sqrDistance = b0 * s + c;
251  }
252  }
253  }
254  else
255  { // region 1
256  numer = a11 + b1 - a01 - b0;
257  if( numer <= 0.0 )
258  {
259  s = 0.0;
260  t = 1.0;
261  sqrDistance = a11 + 2.0 * b1 + c;
262  }
263  else
264  {
265  denom = a00 - 2.0 * a01 + a11;
266  if( numer >= denom )
267  {
268  s = 1.0;
269  t = 0.0;
270  sqrDistance = a00 + 2.0 * b0 + c;
271  }
272  else
273  {
274  s = numer / denom;
275  t = 1.0 - s;
276  sqrDistance = s * ( a00 * s + a01 * t + 2.0 * b0 )
277  + t * ( a01 * s + a11 * t + 2.0 * b1 )
278  + c;
279  }
280  }
281  }
282  }
283 
284  // Account for numerical round-off error.
285  if( sqrDistance < 0.0 )
286  {
287  sqrDistance = 0.0;
288  }
289 
290  vec3 closest_point{ V0 + s * edge0 + t * edge1 };
291  return std::make_tuple( std::sqrt( sqrDistance ), closest_point );
292  }
293 
294  template <>
295  std::tuple< double, vec2 > point_to_segment(
296  const Geometry::Point2D& point, const Geometry::Segment2D& segment )
297  {
298  // The direction vector is not unit length. The normalization is
299  // deferred
300  // until it is needed.
301  vec2 direction = segment.p1 - segment.p0;
302  vec2 diff = point - segment.p1;
303  double t = dot( direction, diff );
304  vec2 nearest_p;
305  if( t >= global_epsilon )
306  {
307  nearest_p = segment.p1;
308  }
309  else
310  {
311  diff = point - segment.p0;
312  t = dot( direction, diff );
313  if( t <= global_epsilon )
314  {
315  nearest_p = segment.p0;
316  }
317  else
318  {
319  double sqrLength = dot( direction, direction );
320  if( sqrLength > global_epsilon )
321  {
322  t /= sqrLength;
323  nearest_p = segment.p0 + t * direction;
324  }
325  else
326  {
327  nearest_p = segment.p0;
328  }
329  }
330  }
331 
332  diff = point - nearest_p;
333  return std::make_tuple( std::sqrt( dot( diff, diff ) ), nearest_p );
334  }
335 
336  template <>
337  std::tuple< double, vec2 > point_to_triangle(
338  const Geometry::Point2D& point,
339  const Geometry::Triangle2D& triangle )
340  {
341  double result;
342  vec2 closest_point;
343  if( Position::point_inside_triangle( point, triangle ) )
344  {
345  closest_point = point;
346  result = 0.0;
347  }
348  else
349  {
350  std::array< vec2, 3 > closest;
351  std::array< double, 3 > distance;
352  std::tie( distance[0], closest[0] ) = point_to_segment(
353  point, Geometry::Segment2D{ triangle.p0, triangle.p1 } );
354  std::tie( distance[1], closest[1] ) = point_to_segment(
355  point, Geometry::Segment2D{ triangle.p1, triangle.p2 } );
356  std::tie( distance[2], closest[2] ) = point_to_segment(
357  point, Geometry::Segment2D{ triangle.p2, triangle.p0 } );
358  if( distance[0] < distance[1] )
359  {
360  if( distance[0] < distance[2] )
361  {
362  result = distance[0];
363  closest_point = closest[0];
364  }
365  else
366  {
367  result = distance[2];
368  closest_point = closest[2];
369  }
370  }
371  else
372  {
373  if( distance[1] < distance[2] )
374  {
375  result = distance[1];
376  closest_point = closest[1];
377  }
378  else
379  {
380  result = distance[2];
381  closest_point = closest[2];
382  }
383  }
384  }
385  return std::make_tuple( result, closest_point );
386  }
387 
388  std::tuple< double, vec3 > point_to_tetra(
389  const Geometry::Point3D& point, const Geometry::Tetra& tetra )
390  {
391  std::array< vec3, 4 > vertices{ { tetra.p0, tetra.p1, tetra.p2,
392  tetra.p3 } };
393  double dist{ max_float64() };
394  vec3 nearest_p;
395  for( auto f :
396  range( GEO::MeshCellDescriptors::tet_descriptor.nb_facets ) )
397  {
398  double distance{ max_float64() };
399  vec3 cur_p;
400  std::tie( distance, cur_p ) = point_to_triangle(
401  point, Geometry::Triangle3D{
402  vertices[GEO::MeshCellDescriptors::tet_descriptor
403  .facet_vertex[f][0]],
404  vertices[GEO::MeshCellDescriptors::tet_descriptor
405  .facet_vertex[f][1]],
406  vertices[GEO::MeshCellDescriptors::tet_descriptor
407  .facet_vertex[f][2]] } );
408  if( distance < dist )
409  {
410  dist = distance;
411  nearest_p = cur_p;
412  }
413  }
414  return std::make_tuple( dist, nearest_p );
415  }
416 
417  template <>
418  std::tuple< double, vec3 > point_to_segment(
419  const Geometry::Point3D& point, const Geometry::Segment3D& segment )
420  {
421  bool can_point_be_projected;
422  vec3 nearest_p;
423  std::tie( can_point_be_projected, nearest_p ) =
424  point_segment_projection( point, segment.p0, segment.p1 );
425  if( can_point_be_projected )
426  {
427  return std::make_tuple(
428  length( nearest_p - point ), nearest_p );
429  }
430  double p0_sq{ length2( segment.p0 - point ) };
431  double p1_sq{ length2( segment.p1 - point ) };
432  if( p0_sq < p1_sq )
433  {
434  return std::make_tuple( std::sqrt( p0_sq ), segment.p0 );
435  }
436  return std::make_tuple( std::sqrt( p1_sq ), segment.p1 );
437  }
438 
439  std::tuple< double, vec3 > point_to_plane(
440  const Geometry::Point3D& point, const Geometry::Plane& plane )
441  {
442  vec3 v{ point - plane.origin };
443  double distance{ dot( v, plane.normal ) };
444  vec3 projected_p{ point - distance * plane.normal };
445  return std::make_tuple( distance, projected_p );
446  }
447 
448  template std::tuple< double, vecn< 2 > > RINGMESH_API point_to_segment(
450  template std::tuple< double, vecn< 2 > > RINGMESH_API point_to_triangle(
452 
453  template std::tuple< double, vecn< 3 > > RINGMESH_API point_to_segment(
455  template std::tuple< double, vecn< 3 > > RINGMESH_API point_to_triangle(
457  } // namespace Distance
458 } // namespace RINGMesh
std::tuple< bool, vecn< DIMENSION > > point_segment_projection(const vecn< DIMENSION > &p, const vecn< DIMENSION > &p0, const vecn< DIMENSION > &p1)
Definition: geometry.cpp:161
std::tuple< double, vec3 > RINGMESH_API point_to_tetra(const Geometry::Point3D &point, const Geometry::Tetra &tetra)
std::tuple< double, vecn< DIMENSION > > point_to_triangle(const Geometry::Point< DIMENSION > &point, const Geometry::Triangle< DIMENSION > &triangle)
vecn< 3 > vec3
Definition: types.h:76
vecn< 2 > vec2
Definition: types.h:78
std::tuple< double, vec3 > RINGMESH_API point_to_plane(const Geometry::Point3D &point, const Geometry::Plane &plane)
bool point_inside_triangle(const Geometry::Point< DIMENSION > &point, const Geometry::Triangle< DIMENSION > &triangle)
Tests if a point is inside a triangle.
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
vecn< DIMENSION > Point
Definition: geometry.h:93
std::tuple< double, vecn< DIMENSION > > point_to_segment(const Geometry::Point< DIMENSION > &point, const Geometry::Segment< DIMENSION > &segment)