RINGMesh  Version 5.0.0
A programming library for geological model meshes
geometry_position.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 <geogram/mesh/mesh.h>
39 
40 #include <geogram/numerics/predicates.h>
41 
49 namespace
50 {
51  using namespace RINGMesh;
52 
53  bool is_almost_zero( double value )
54  {
55  return value < global_epsilon && value > -global_epsilon;
56  }
57 
58  bool point_inside_segment_exact(
59  const Geometry::Point3D& point, const Geometry::Segment3D& segment )
60  {
62  point, { segment.direction(), segment.p0 } ) };
64  point, { segment.direction(), segment.p1 } ) };
65  return s1 == ZERO || s2 == ZERO || s1 != s2;
66  }
67 
68  bool point_inside_segment_approx(
69  const Geometry::Point3D& point, const Geometry::Segment3D& segment )
70  {
71  double distance;
72  std::tie( distance, std::ignore ) =
73  Distance::point_to_segment( point, segment );
74  if( distance > global_epsilon )
75  {
76  return false;
77  }
78  double half_length{ segment.length() / 2. };
79  vec3 segment_center{ segment.barycenter() };
80  double point_distance{ length( point - segment_center ) };
81  if( point_distance < half_length - global_epsilon )
82  {
83  return true;
84  }
85  if( point_distance > half_length + global_epsilon )
86  {
87  return false;
88  }
89  return point_inside_segment_exact( point, segment );
90  }
91 
92  bool point_inside_triangle_exact(
93  const Geometry::Point2D& point, const Geometry::Triangle2D& triangle )
94  {
96  point, { triangle.p0, triangle.p1 } ) };
98  point, { triangle.p1, triangle.p2 } ) };
100  point, { triangle.p2, triangle.p0 } ) };
101 
102  if( s1 == ZERO )
103  {
104  if( s2 == ZERO || s3 == ZERO )
105  {
106  // Case where p is exactly equal to one triangle vertex
107  return true;
108  }
109  return s2 == s3;
110  }
111  if( s2 == ZERO )
112  {
113  if( s1 == ZERO || s3 == ZERO )
114  {
115  return true;
116  }
117  return s1 == s3;
118  }
119  if( s3 == ZERO )
120  {
121  if( s1 == ZERO || s2 == ZERO )
122  {
123  return true;
124  }
125  return s1 == s2;
126  }
127  return s1 == s2 && s2 == s3;
128  }
129 
130  bool point_inside_triangle_approx(
131  const Geometry::Point2D& point, const Geometry::Triangle2D& triangle )
132  {
133  double area1{ GEO::Geom::triangle_signed_area(
134  point, triangle.p0, triangle.p1 ) };
135  if( is_almost_zero( area1 ) )
136  {
137  return point_inside_triangle_exact( point, triangle );
138  }
139  Sign s1{ sign( area1 ) };
140  double area2{ GEO::Geom::triangle_signed_area(
141  point, triangle.p1, triangle.p2 ) };
142  if( is_almost_zero( area2 ) )
143  {
144  return point_inside_triangle_exact( point, triangle );
145  }
146  Sign s2{ sign( area2 ) };
147  double area3{ GEO::Geom::triangle_signed_area(
148  point, triangle.p2, triangle.p0 ) };
149  if( is_almost_zero( area3 ) )
150  {
151  return point_inside_triangle_exact( point, triangle );
152  }
153  Sign s3{ sign( area3 ) };
154  return s1 == s2 && s2 == s3;
155  }
156 
157  Geometry::Plane plane_from_triangle_normal_and_edge(
158  const vec3& normal, const vec3& e0, const vec3& e1 )
159  {
160  return { cross( normal, normalize( e1 - e0 ) ), e0 };
161  }
162 
163  bool point_inside_triangle_exact(
164  const Geometry::Point3D& point, const Geometry::Triangle3D& triangle )
165  {
166  vec3 triangle_normal{ triangle.plane().normal };
168  point, plane_from_triangle_normal_and_edge(
169  triangle_normal, triangle.p0, triangle.p1 ) ) };
171  point, plane_from_triangle_normal_and_edge(
172  triangle_normal, triangle.p1, triangle.p2 ) ) };
174  point, plane_from_triangle_normal_and_edge(
175  triangle_normal, triangle.p2, triangle.p0 ) ) };
176 
177  if( s1 == ZERO )
178  {
179  if( s2 == ZERO || s3 == ZERO )
180  {
181  // Case where p is exactly equal to one triangle vertex
182  return true;
183  }
184  return s2 == s3;
185  }
186  if( s2 == ZERO )
187  {
188  if( s1 == ZERO || s3 == ZERO )
189  {
190  return true;
191  }
192  return s1 == s3;
193  }
194  if( s3 == ZERO )
195  {
196  if( s1 == ZERO || s2 == ZERO )
197  {
198  return true;
199  }
200  return s1 == s2;
201  }
202  return s1 == s2 && s2 == s3;
203  }
204 
205  bool point_inside_triangle_approx(
206  const Geometry::Point3D& point, const Geometry::Triangle3D& triangle )
207  {
208  // Get another point not in the triangle plane (using its normal)
209  vec3 translated_point{ point + triangle.plane().normal };
210 
211  double vol1{ GEO::Geom::tetra_signed_volume(
212  point, translated_point, triangle.p0, triangle.p1 ) };
213  if( is_almost_zero( vol1 ) )
214  {
215  return point_inside_triangle_exact( point, triangle );
216  }
217  Sign s1{ sign( vol1 ) };
218  double vol2{ GEO::Geom::tetra_signed_volume(
219  point, translated_point, triangle.p1, triangle.p2 ) };
220  if( is_almost_zero( vol2 ) )
221  {
222  return point_inside_triangle_exact( point, triangle );
223  }
224  Sign s2{ sign( vol2 ) };
225  double vol3{ GEO::Geom::tetra_signed_volume(
226  point, translated_point, triangle.p2, triangle.p0 ) };
227  if( is_almost_zero( vol3 ) )
228  {
229  return point_inside_triangle_exact( point, triangle );
230  }
231  Sign s3{ sign( vol3 ) };
232  return s1 == s2 && s2 == s3;
233  }
234 
235  bool point_inside_tetra_exact(
236  const vec3& p, std::array< vec3, 4 >& vertices )
237  {
238  std::array< Sign, 4 > signs;
239  for( auto f : range( 4 ) )
240  {
241  signs[f] = sign( GEO::PCK::orient_3d( p.data(),
242  vertices[GEO::MeshCellDescriptors::tet_descriptor
243  .facet_vertex[f][0]]
244  .data(),
245  vertices[GEO::MeshCellDescriptors::tet_descriptor
246  .facet_vertex[f][1]]
247  .data(),
248  vertices[GEO::MeshCellDescriptors::tet_descriptor
249  .facet_vertex[f][2]]
250  .data() ) );
251  }
252  return ( signs[0] >= 0 && signs[1] >= 0 && signs[2] >= 0
253  && signs[3] >= 0 )
254  || ( signs[0] <= 0 && signs[1] <= 0 && signs[2] <= 0
255  && signs[3] <= 0 );
256  }
257 
258  bool point_inside_tetra_approx(
259  const vec3& p, std::array< vec3, 4 >& vertices )
260  {
261  std::array< Sign, 4 > signs;
262  for( const index_t f : range( 4 ) )
263  {
264  double volume{ GEO::Geom::tetra_signed_volume( p,
265  vertices[GEO::MeshCellDescriptors::tet_descriptor
266  .facet_vertex[f][0]],
267  vertices[GEO::MeshCellDescriptors::tet_descriptor
268  .facet_vertex[f][1]],
269  vertices[GEO::MeshCellDescriptors::tet_descriptor
270  .facet_vertex[f][2]] ) };
271  if( is_almost_zero( volume ) )
272  {
273  return point_inside_tetra_exact( p, vertices );
274  }
275  signs[f] = sign( volume );
276  }
277  return ( signs[0] >= 0 && signs[1] >= 0 && signs[2] >= 0
278  && signs[3] >= 0 )
279  || ( signs[0] <= 0 && signs[1] <= 0 && signs[2] <= 0
280  && signs[3] <= 0 );
281  }
282 } // namespace
283 
284 namespace RINGMesh
285 {
286  namespace Position
287  {
289  const Geometry::Point3D& point, const Geometry::Tetra& tetra )
290  {
291  std::array< vec3, 4 > vertices{ { tetra.p0, tetra.p1, tetra.p2,
292  tetra.p3 } };
293  return point_inside_tetra_approx( point, vertices );
294  }
295 
296  template < index_t DIMENSION >
298  const Geometry::Triangle< DIMENSION >& triangle )
299  {
300  return point_inside_triangle_approx( point, triangle );
301  }
302 
304  const Geometry::Point3D& point, const Geometry::Segment3D& segment )
305  {
306  return point_inside_segment_approx( point, segment );
307  }
308 
310  const Geometry::Point2D& point, const Geometry::Segment2D& segment )
311  {
312  return sign( GEO::PCK::orient_2d( point, segment.p0, segment.p1 ) );
313  }
314 
316  const Geometry::Point3D& point, const Geometry::Plane& plane )
317  {
318  double distance;
319  vec3 projected_point;
320  std::tie( distance, projected_point ) =
321  Distance::point_to_plane( point, plane );
322 
323  vec3 point_on_plane{ projected_point };
324  double translation{ std::max( 1.0, distance ) };
325  for( auto d : range( 3 ) )
326  {
327  if( std::fabs( plane.normal[d] ) > global_epsilon )
328  {
329  index_t d1{ ( d + 1 ) % 3 };
330  index_t d2{ ( d + 2 ) % 3 };
331  point_on_plane[d1] += translation;
332  point_on_plane[d2] += translation;
333 
334  point_on_plane[d] =
335  -( plane.plane_constant()
336  + plane.normal[d1] * point_on_plane[d1]
337  + plane.normal[d2] * point_on_plane[d2] )
338  / plane.normal[d];
339  break;
340  }
341  }
342  ringmesh_assert( point_on_plane != projected_point );
343 
344  vec3 u{ normalize( point_on_plane ) };
345  vec3 v{ cross( plane.normal, u ) };
346 
347  vec3 p0{ projected_point + distance * u };
348  vec3 p1{ projected_point
349  + distance * ( std::cos( 2 * M_PI / 3 ) * u
350  - std::sin( 2 * M_PI / 3 ) * v ) };
351  vec3 p2{ projected_point
352  + distance * ( std::cos( 2 * M_PI / 3 ) * u
353  + std::sin( 2 * M_PI / 3 ) * v ) };
354 
355  return sign( GEO::PCK::orient_3d(
356  point.data(), p0.data(), p1.data(), p2.data() ) );
357  }
358 
359  template bool RINGMESH_API point_inside_triangle(
361 
362  template bool RINGMESH_API point_inside_triangle(
364  } // namespace Position
365 } // namespace RINGMesh
vecn< 3 > vec3
Definition: types.h:76
double RINGMESH_API triangle_signed_area(const vec3 &p0, const vec3 &p1, const vec3 &p2, const vec3 &triangle_normal)
Definition: geometry.cpp:66
bool RINGMESH_API point_inside_segment(const Geometry::Point3D &point, const Geometry::Segment3D &segment)
Tests if a point is on a segment.
Sign RINGMESH_API point_side_to_segment(const Geometry::Point2D &point, const Geometry::Segment2D &segment)
std::tuple< double, vec3 > RINGMESH_API point_to_plane(const Geometry::Point3D &point, const Geometry::Plane &plane)
Sign RINGMESH_API point_side_to_plane(const Geometry::Point3D &point, const Geometry::Plane &plane)
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
#define ringmesh_assert(x)
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
vecn< DIMENSION > Point
Definition: geometry.h:93
bool RINGMESH_API point_inside_tetra(const Geometry::Point3D &point, const Geometry::Tetra &tetra)
std::tuple< double, vecn< DIMENSION > > point_to_segment(const Geometry::Point< DIMENSION > &point, const Geometry::Segment< DIMENSION > &segment)