RINGMesh  Version 5.0.0
A programming library for geological model meshes
geometry_intersection.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 
45 namespace RINGMesh
46 {
47  namespace Intersection
48  {
49  std::tuple< bool, std::vector< vec3 > > circle_plane(
50  const Geometry::Circle& circle, const Geometry::Plane& plane )
51  {
52  bool does_plane_intersect_plane;
53  Geometry::Line3D inter;
54  std::tie( does_plane_intersect_plane, inter ) =
55  plane_plane( plane, circle.plane );
56  if( !does_plane_intersect_plane )
57  {
58  return std::make_tuple( false, std::vector< vec3 >() );
59  }
60 
61  // http://www.geometrictools.com/LibMathematics/Intersection/Intersection.html
62  // Locate one or two points that are on the circle and line. If the
63  // line is t*D+P, the circle center is C, and the circle radius is
64  // r,
65  // then r^2 = |t*D+P-C|^2 = |D|^2*t^2 + 2*Dot(D,P-C)*t + |P-C|^2.
66  // This
67  // is a quadratic equation of the form: a2*t^2 + 2*a1*t + a0 = 0.
68  // In our case, a2 = 1 because direction plane is normalized
69  vec3 diff{ inter.origin - circle.plane.origin };
70  double a1{ dot( diff, inter.direction ) };
71  double a0{ diff.length2() - circle.radius * circle.radius };
72 
73  double discr{ a1 * a1 - a0 };
74  if( discr < 0.0 )
75  {
76  return std::make_tuple( false, std::vector< vec3 >() );
77  }
78 
79  std::vector< vec3 > result;
80  if( discr < global_epsilon )
81  {
82  result.emplace_back( inter.origin - a1 * inter.direction );
83  }
84  else
85  {
86  double root{ std::sqrt( discr ) };
87  result.emplace_back(
88  inter.origin - ( a1 + root ) * inter.direction );
89  result.emplace_back(
90  inter.origin - ( a1 - root ) * inter.direction );
91  }
92  return std::make_tuple( true, result );
93  }
94 
95  std::tuple< bool, Geometry::Line3D > plane_plane(
96  const Geometry::Plane& plane0, const Geometry::Plane& plane1 )
97  {
98  // http://www.geometrictools.com/LibMathematics/Intersection/Intersection.html
99  // If N0 and N1 are parallel, either the planes are parallel and
100  // separated
101  // or the same plane. In both cases, 'false' is returned.
102  // Otherwise,
103  // the intersection line is
104  // L(t) = t*Cross(N0,N1)/|Cross(N0,N1)| + c0*N0 + c1*N1
105  // for some coefficients c0 and c1 and for t any real number (the
106  // line
107  // parameter). Taking dot products with the normals,
108  // d0 = Dot(N0,L) = c0*Dot(N0,N0) + c1*Dot(N0,N1) = c0 + c1*d
109  // d1 = Dot(N1,L) = c0*Dot(N0,N1) + c1*Dot(N1,N1) = c0*d + c1
110  // where d = Dot(N0,N1). These are two equations in two unknowns.
111  // The
112  // solution is
113  // c0 = (d0 - d*d1)/det
114  // c1 = (d1 - d*d0)/det
115  // where det = 1 - d^2.
116 
117  double norm_d{ dot( plane0.normal, plane1.normal ) };
118 
119  // Planes are parallel
120  if( std::fabs( std::fabs( norm_d ) - 1 ) < global_epsilon )
121  {
122  return std::make_tuple( false, Geometry::Line3D{} );
123  }
124 
125  double invDet{ 1.0 / ( 1.0 - norm_d * norm_d ) };
126  double const_P0{ dot( plane0.normal, plane0.origin ) };
127  double const_P1{ dot( plane1.normal, plane1.origin ) };
128  double c0{ ( const_P0 - norm_d * const_P1 ) * invDet };
129  double c1{ ( const_P1 - norm_d * const_P0 ) * invDet };
130  vec3 O_inter{ c0 * plane0.normal + c1 * plane1.normal };
131  vec3 D_inter{ cross( plane0.normal, plane1.normal ) };
132  return std::make_tuple(
133  true, Geometry::Line3D{ D_inter, O_inter } );
134  }
135 
136  std::tuple< bool, vec2 > line_line(
137  const Geometry::Line2D& line0, const Geometry::Line2D& line1 )
138  {
139  // The intersection of two lines is a solution to P0 + s0*D0 = P1 +
140  // s1*D1.
141  // Rewrite this as s0*D0 - s1*D1 = P1 - P0 = Q. If DotPerp(D0, D1))
142  // = 0,
143  // the lines are parallel. Additionally, if DotPerp(Q, D1)) = 0,
144  // the
145  // lines are the same. If Dotperp(D0, D1)) is not zero, then
146  // s0 = DotPerp(Q, D1))/DotPerp(D0, D1))
147  // produces the point of intersection. Also,
148  // s1 = DotPerp(Q, D0))/DotPerp(D0, D1))
149 
150  vec2 diff{ line1.origin - line0.origin };
151  double D0DotPerpD1{ dot_perp( line0.direction, line1.direction ) };
152  if( std::fabs( D0DotPerpD1 ) < global_epsilon )
153  {
154  // The lines are parallel.
155  return std::make_tuple( false, vec2() );
156  }
157 
158  double invD0DotPerpD1{ 1.0 / D0DotPerpD1 };
159  double diffDotPerpD1{ dot_perp( diff, line1.direction ) };
160  double s0{ diffDotPerpD1 * invD0DotPerpD1 };
161  vec2 result{ line0.origin + s0 * line0.direction };
162  return std::make_tuple( true, result );
163  }
164 
165  std::tuple< bool, vec2 > segment_segment(
166  const Geometry::Segment2D& segment0,
167  const Geometry::Segment2D& segment1 )
168  {
169  bool does_segment_intersect_segment;
170  vec2 line_intersection_result;
171  std::tie(
172  does_segment_intersect_segment, line_intersection_result ) =
173  line_line( Geometry::Line2D{ segment0 },
174  Geometry::Line2D{ segment1 } );
175  if( does_segment_intersect_segment )
176  {
177  // Test whether the line-line intersection is on the segments.
179  segment0.p0, segment1 ) };
181  segment0.p1, segment1 ) };
182  if( s0_seg0 != ZERO && ( s0_seg0 == s1_seg0 ) )
183  {
184  return std::make_tuple( false, vec2() );
185  }
187  segment1.p0, segment0 ) };
189  segment1.p1, segment0 ) };
190  if( s0_seg1 != ZERO && ( s0_seg1 == s1_seg1 ) )
191  {
192  return std::make_tuple( false, vec2() );
193  }
194 
195  if( s0_seg0 == ZERO || s1_seg0 == ZERO
196  || ( s0_seg0 != s1_seg0 ) )
197  {
198  if( s0_seg1 == ZERO || s1_seg1 == ZERO
199  || ( s0_seg1 != s1_seg1 ) )
200  {
201  return std::make_tuple(
202  true, line_intersection_result );
203  }
204  }
205  }
206  return std::make_tuple( false, vec2() );
207  }
208 
209  std::tuple< bool, vec2 > segment_line(
210  const Geometry::Segment2D& segment, const Geometry::Line2D& line )
211  {
212  bool does_segment_intersect_line;
213  vec2 line_intersection_result;
214  std::tie( does_segment_intersect_line, line_intersection_result ) =
215  line_line( Geometry::Line2D{ segment }, line );
216  if( does_segment_intersect_line )
217  {
218  // Test whether the line-line intersection is on the segment.
219  Geometry::Segment2D line_segment{ { line_intersection_result
220  - line.direction },
221  { line_intersection_result + line.direction } };
223  segment.p0, line_segment ) };
225  segment.p1, line_segment ) };
226  if( s0 == ZERO || s1 == ZERO || ( s0 != s1 ) )
227  {
228  return std::make_tuple( true, line_intersection_result );
229  }
230  }
231  return std::make_tuple( false, vec2() );
232  }
233 
234  std::tuple< bool, vec3 > line_plane(
235  const Geometry::Line3D& line, const Geometry::Plane& plane )
236  {
237  double dot_directions{ dot( line.direction, plane.normal ) };
238  if( std::fabs( dot_directions ) > global_epsilon )
239  {
240  double signed_distance{ dot( plane.normal, line.origin )
241  + plane.plane_constant() };
242  vec3 result{ line.origin
243  - signed_distance * line.direction
244  / dot_directions };
245  return std::make_tuple( true, result );
246  }
247  // line is parallel to the plane
248  return std::make_tuple( false, vec3() );
249  }
250 
251  std::tuple< bool, vec3 > segment_plane(
252  const Geometry::Segment3D& segment, const Geometry::Plane& plane )
253  {
254  bool does_line_intersect_plane;
255  vec3 line_plane_result;
256  std::tie( does_line_intersect_plane, line_plane_result ) =
257  line_plane( Geometry::Line3D{ segment }, plane );
258  if( does_line_intersect_plane )
259  {
261  line_plane_result, segment ) )
262  {
263  // result inside the segment
264  return std::make_tuple( true, line_plane_result );
265  }
266  return std::make_tuple( false, vec3() );
267  }
268  return std::make_tuple( false, vec3() );
269  }
270 
271  std::tuple< bool, vec3 > segment_disk(
272  const Geometry::Segment3D& segment, const Geometry::Disk& disk )
273  {
274  bool does_segment_intersect_plane{ false };
275  vec3 segment_plane_result;
276  std::tie( does_segment_intersect_plane, segment_plane_result ) =
277  segment_plane( segment, disk.plane );
278  if( does_segment_intersect_plane )
279  {
280  if( ( segment_plane_result - disk.plane.origin ).length()
281  <= disk.radius )
282  {
283  return std::make_tuple( true, segment_plane_result );
284  }
285  }
286  return std::make_tuple( false, vec3() );
287  }
288 
289  std::tuple< bool, std::vector< vec3 > > triangle_circle(
290  const Geometry::Triangle3D& triangle,
291  const Geometry::Circle& circle )
292  {
293  bool does_circle_intersect_plane{ false };
294  std::vector< vec3 > inter_circle_plane;
295  std::tie( does_circle_intersect_plane, inter_circle_plane ) =
296  circle_plane( circle, triangle.plane() );
297  std::vector< vec3 > result;
298  if( does_circle_intersect_plane )
299  {
300  for( const vec3& p : inter_circle_plane )
301  {
302  if( Position::point_inside_triangle( p, triangle ) )
303  {
304  result.push_back( p );
305  }
306  }
307  }
308  return std::make_tuple( !result.empty(), result );
309  }
310 
311  std::tuple< bool, vec3 > segment_triangle(
312  const Geometry::Segment3D& segment,
313  const Geometry::Triangle3D& triangle )
314  {
315  // http://www.geometrictools.com/LibMathematics/Intersection/Intersection.html
316  // Compute the offset origin, edges, and normal.
317  vec3 seg_center{ segment.barycenter() };
318  vec3 diff{ seg_center - triangle.p0 };
319  vec3 edge1{ triangle.p1 - triangle.p0 };
320  vec3 edge2{ triangle.p2 - triangle.p0 };
321  vec3 normal{ cross( edge1, edge2 ) };
322 
323  // Solve Q + t*D = b1*E1 + b2*E2 (Q = diff, D = segment direction,
324  // E1 = edge1, E2 = edge2, N = Cross(E1,E2)) by
325  // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
326  // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
327  // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
328  vec3 D{ segment.direction() };
329  double DdN{ dot( D, normal ) };
330  signed_index_t sign;
331  if( DdN > global_epsilon )
332  {
333  sign = 1;
334  }
335  else if( DdN < -global_epsilon )
336  {
337  sign = -1;
338  DdN = -DdN;
339  }
340  else
341  {
342  // Segment and triangle are parallel, call it a "no
343  // intersection"
344  // even if the segment does intersect.
345  return std::make_tuple( false, vec3() );
346  }
347 
348  double DdQxE2{ sign * dot( D, cross( diff, edge2 ) ) };
349  if( DdQxE2 >= 0 )
350  {
351  double DdE1xQ{ sign * dot( D, cross( edge1, diff ) ) };
352  if( DdE1xQ >= 0 )
353  {
354  if( DdQxE2 + DdE1xQ <= DdN )
355  {
356  // Line intersects triangle, check if segment does.
357  double QdN{ -sign * dot( diff, normal ) };
358  double extDdN{ segment.length() * DdN / 2. };
359  if( -extDdN <= QdN && QdN <= extDdN )
360  {
361  // Segment intersects triangle.
362  double inv{ 1. / DdN };
363  double seg_parameter{ QdN * inv };
364 
365  vec3 result{ seg_center + seg_parameter * D };
366  return std::make_tuple( true, result );
367  }
368  // else: |t| > extent, no intersection
369  }
370  // else: b1+b2 > 1, no intersection
371  }
372  // else: b2 < 0, no intersection
373  }
374  // else: b1 < 0, no intersection
375  return std::make_tuple( false, vec3() );
376  }
377 
378  std::tuple< bool, std::vector< vec3 > > line_sphere(
379  const Geometry::Line3D& line, const Geometry::Sphere& sphere )
380  {
381  // The sphere is (X-C)^T*(X-C)-1 = 0 and the line is X = P+t*D.
382  // Substitute the line equation into the sphere equation to obtain a
383  // quadratic equation Q(t) = t^2 + 2*a1*t + a0 = 0, where a1 =
384  // D^T*(P-C),
385  // and a0 = (P-C)^T*(P-C)-1.
386  vec3 diff{ line.origin - sphere.origin };
387  double a0{ dot( diff, diff ) - sphere.radius * sphere.radius };
388  double a1{ dot( line.direction, diff ) };
389 
390  // Intersection occurs when Q(t) has real roots.
391  double discr{ a1 * a1 - a0 };
392  std::vector< vec3 > results;
393  if( discr > global_epsilon )
394  {
395  double root{ std::sqrt( discr ) };
396  results.reserve( 2 );
397  results.emplace_back(
398  line.origin + ( -a1 - root ) * line.direction );
399  results.emplace_back(
400  line.origin + ( -a1 + root ) * line.direction );
401  }
402  else if( discr > -global_epsilon )
403  {
404  results.reserve( 1 );
405  results.emplace_back( line.origin + -a1 * line.direction );
406  }
407  return std::make_tuple( !results.empty(), results );
408  }
409 
410  std::tuple< bool, std::vector< vec3 > > segment_sphere(
411  const Geometry::Segment3D& segment, const Geometry::Sphere& sphere )
412  {
413  bool line_intersect;
414  std::vector< vec3 > line_intersections;
415  std::tie( line_intersect, line_intersections ) =
416  line_sphere( Geometry::Line3D{ segment }, sphere );
417 
418  std::vector< vec3 > segment_intersections;
419  if( line_intersect )
420  {
421  segment_intersections.reserve( line_intersections.size() );
422  for( auto& point : line_intersections )
423  {
424  if( Position::point_inside_segment( point, segment ) )
425  {
426  segment_intersections.emplace_back( point );
427  }
428  }
429  }
430  return std::make_tuple(
431  !segment_intersections.empty(), segment_intersections );
432  }
433  } // namespace Intersection
434 } // namespace RINGMesh
vecn< 3 > vec3
Definition: types.h:76
double RINGMESH_API dot_perp(const vec2 &v0, const vec2 &v1)
Definition: geometry.cpp:61
bool RINGMESH_API point_inside_segment(const Geometry::Point3D &point, const Geometry::Segment3D &segment)
Tests if a point is on a segment.
std::tuple< bool, vec3 > RINGMESH_API segment_disk(const Geometry::Segment3D &segment, const Geometry::Disk &disk)
std::tuple< bool, vec2 > RINGMESH_API segment_line(const Geometry::Segment2D &segment, const Geometry::Line2D &line)
vecn< 2 > vec2
Definition: types.h:78
std::tuple< bool, Geometry::Line3D > RINGMESH_API plane_plane(const Geometry::Plane &plane0, const Geometry::Plane &plane1)
std::tuple< bool, std::vector< vec3 > > RINGMESH_API line_sphere(const Geometry::Line3D &line, const Geometry::Sphere &sphere)
Sign RINGMESH_API point_side_to_segment(const Geometry::Point2D &point, const Geometry::Segment2D &segment)
std::tuple< bool, std::vector< vec3 > > RINGMESH_API triangle_circle(const Geometry::Triangle3D &triangle, const Geometry::Circle &circle)
std::tuple< bool, vec3 > RINGMESH_API segment_plane(const Geometry::Segment3D &segment, const Geometry::Plane &plane)
std::tuple< bool, std::vector< vec3 > > RINGMESH_API segment_sphere(const Geometry::Segment3D &segment, const Geometry::Sphere &sphere)
std::tuple< bool, vec3 > RINGMESH_API segment_triangle(const Geometry::Segment3D &segment, const Geometry::Triangle3D &triangle)
double plane_constant() const
Definition: geometry.h:150
bool point_inside_triangle(const Geometry::Point< DIMENSION > &point, const Geometry::Triangle< DIMENSION > &triangle)
Tests if a point is inside a triangle.
Sign sign(T x)
Definition: geometry.h:85
std::tuple< bool, vec2 > RINGMESH_API line_line(const Geometry::Line2D &line0, const Geometry::Line2D &line1)
std::tuple< bool, vec2 > RINGMESH_API segment_segment(const Geometry::Segment2D &segment0, const Geometry::Segment2D &segment1)
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
std::tuple< bool, vec3 > RINGMESH_API line_plane(const Geometry::Line3D &line, const Geometry::Plane &plane)
std::tuple< bool, std::vector< vec3 > > RINGMESH_API circle_plane(const Geometry::Circle &circle, const Geometry::Plane &plane)