RINGMesh  Version 5.0.0
A programming library for geological model meshes
aabb.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 
36 #include <ringmesh/mesh/aabb.h>
37 
38 #include <algorithm>
39 #include <numeric>
40 
42 
43 #include <ringmesh/mesh/mesh.h>
44 
46 
47 namespace
48 {
49  using namespace RINGMesh;
50 
51  typedef const std::vector< index_t >::iterator const_vector_itr;
52 
53  template < index_t DIMENSION >
54  class Morton_cmp
55  {
56  public:
57  Morton_cmp(
58  const std::vector< Box< DIMENSION > >& bboxes, index_t coord )
59  : bboxes_( bboxes ), coord_( coord )
60  {
61  }
62 
63  bool operator()( index_t box1, index_t box2 )
64  {
65  return bboxes_[box1].center()[coord_]
66  < bboxes_[box2].center()[coord_];
67  }
68 
69  private:
70  const std::vector< Box< DIMENSION > >& bboxes_;
71  index_t coord_;
72  };
73 
74  ALIAS_2D_AND_3D( Morton_cmp );
75 
88  template < class CMP >
89  const_vector_itr split(
90  const_vector_itr& begin, const_vector_itr& end, CMP cmp )
91  {
92  if( begin >= end )
93  {
94  return begin;
95  }
96  const_vector_itr middle = begin + ( end - begin ) / 2;
97  std::nth_element( begin, middle, end, cmp );
98  return middle;
99  }
100 
108  template < index_t DIMENSION >
109  struct MortonSort
110  {
111  template < index_t COORDX >
112  void sort( const std::vector< Box< DIMENSION > >& bboxes,
113  const_vector_itr& begin,
114  const_vector_itr& end );
115 
116  MortonSort( const std::vector< Box< DIMENSION > >& bboxes,
117  std::vector< index_t >& mapping_morton )
118  {
119  sort< 0 >( bboxes, mapping_morton.begin(), mapping_morton.end() );
120  }
121  };
122 
123  template <>
124  template < index_t COORDX >
125  void MortonSort< 3 >::sort( const std::vector< Box3D >& bboxes,
126  const_vector_itr& begin,
127  const_vector_itr& end )
128  {
129  if( end - begin <= 1 )
130  {
131  return;
132  }
133  const index_t COORDY = ( COORDX + 1 ) % 3, COORDZ = ( COORDY + 1 ) % 3;
134 
135  const_vector_itr m0 = begin, m8 = end;
136  const_vector_itr m4 = split( m0, m8, Morton_cmp3D( bboxes, COORDX ) );
137  const_vector_itr m2 = split( m0, m4, Morton_cmp3D( bboxes, COORDY ) );
138  const_vector_itr m1 = split( m0, m2, Morton_cmp3D( bboxes, COORDZ ) );
139  const_vector_itr m3 = split( m2, m4, Morton_cmp3D( bboxes, COORDZ ) );
140  const_vector_itr m6 = split( m4, m8, Morton_cmp3D( bboxes, COORDY ) );
141  const_vector_itr m5 = split( m4, m6, Morton_cmp3D( bboxes, COORDZ ) );
142  const_vector_itr m7 = split( m6, m8, Morton_cmp3D( bboxes, COORDZ ) );
143  sort< COORDZ >( bboxes, m0, m1 );
144  sort< COORDY >( bboxes, m1, m2 );
145  sort< COORDY >( bboxes, m2, m3 );
146  sort< COORDX >( bboxes, m3, m4 );
147  sort< COORDX >( bboxes, m4, m5 );
148  sort< COORDY >( bboxes, m5, m6 );
149  sort< COORDY >( bboxes, m6, m7 );
150  sort< COORDZ >( bboxes, m7, m8 );
151  }
152 
153  template <>
154  template < index_t COORDX >
155  void MortonSort< 2 >::sort( const std::vector< Box2D >& bboxes,
156  const_vector_itr& begin,
157  const_vector_itr& end )
158  {
159  if( end - begin <= 1 )
160  {
161  return;
162  }
163  const index_t COORDY = ( COORDX + 1 ) % 2;
164 
165  const_vector_itr m0 = begin, m4 = end;
166  const_vector_itr m2 = split( m0, m4, Morton_cmp2D( bboxes, COORDX ) );
167  const_vector_itr m1 = split( m0, m2, Morton_cmp2D( bboxes, COORDY ) );
168  const_vector_itr m3 = split( m2, m4, Morton_cmp2D( bboxes, COORDY ) );
169  sort< COORDY >( bboxes, m0, m1 );
170  sort< COORDX >( bboxes, m1, m2 );
171  sort< COORDX >( bboxes, m2, m3 );
172  sort< COORDY >( bboxes, m3, m4 );
173  }
174 
175  template < index_t DIMENSION >
176  void morton_sort( const std::vector< Box< DIMENSION > >& bboxes,
177  std::vector< index_t >& mapping_morton )
178  {
179  mapping_morton.resize( bboxes.size() );
180  std::iota( mapping_morton.begin(), mapping_morton.end(), 0 );
181  MortonSort< DIMENSION >( bboxes, mapping_morton );
182  }
183 
184  template < index_t DIMENSION >
185  bool mesh_cell_contains_point( const VolumeMesh< DIMENSION >& M,
186  index_t cell,
187  const vecn< DIMENSION >& p )
188  {
189  switch( M.cell_type( cell ) )
190  {
192  {
193  const auto& p0 = M.vertex( M.cell_vertex( { cell, 0 } ) );
194  const auto& p1 = M.vertex( M.cell_vertex( { cell, 1 } ) );
195  const auto& p2 = M.vertex( M.cell_vertex( { cell, 2 } ) );
196  const auto& p3 = M.vertex( M.cell_vertex( { cell, 3 } ) );
197  return Position::point_inside_tetra( p, { p0, p1, p2, p3 } );
198  }
199  default:
201  return false;
202  }
203  }
204 }
205 
206 /****************************************************************************/
207 
208 namespace RINGMesh
209 {
210  template < index_t DIMENSION >
212  const std::vector< Box< DIMENSION > >& bboxes )
213  {
214  morton_sort( bboxes, mapping_morton_ );
215  index_t nb_bboxes = static_cast< index_t >( bboxes.size() );
216  tree_.resize( max_node_index( ROOT_INDEX, 0, nb_bboxes ) + ROOT_INDEX );
217  initialize_tree_recursive( bboxes, ROOT_INDEX, 0, nb_bboxes );
218  }
219 
220  template < index_t DIMENSION >
222  index_t node_index, index_t box_begin, index_t box_end )
223  {
224  ringmesh_assert( box_end > box_begin );
225  if( is_leaf( box_begin, box_end ) )
226  {
227  return node_index;
228  }
229  index_t element_middle, child_left, child_right;
230  get_recursive_iterators( node_index, box_begin, box_end, element_middle,
231  child_left, child_right );
232  return std::max(
233  max_node_index( child_left, box_begin, element_middle ),
234  max_node_index( child_right, element_middle, box_end ) );
235  }
236 
245  template < index_t DIMENSION >
247  const std::vector< Box< DIMENSION > >& bboxes,
248  index_t node_index,
249  index_t box_begin,
250  index_t box_end )
251  {
252  ringmesh_assert( node_index < tree_.size() );
253  ringmesh_assert( box_begin != box_end );
254  if( is_leaf( box_begin, box_end ) )
255  {
256  node( node_index ) = bboxes[mapping_morton_[box_begin]];
257  return;
258  }
259  index_t element_middle, child_left, child_right;
260  get_recursive_iterators( node_index, box_begin, box_end, element_middle,
261  child_left, child_right );
262  ringmesh_assert( child_left < tree_.size() );
263  ringmesh_assert( child_right < tree_.size() );
264  initialize_tree_recursive(
265  bboxes, child_left, box_begin, element_middle );
266  initialize_tree_recursive(
267  bboxes, child_right, element_middle, box_end );
268  node( node_index ) =
269  node( child_left ).bbox_union( node( child_right ) );
270  }
271 
272  template < index_t DIMENSION >
273  std::tuple< index_t, vecn< DIMENSION >, double >
275  const vecn< DIMENSION >& query ) const
276  {
277  index_t box_begin = 0;
278  index_t box_end = nb_bboxes();
279  index_t node_index = ROOT_INDEX;
280  while( !is_leaf( box_begin, box_end ) )
281  {
282  index_t box_middle, child_left, child_right;
283  get_recursive_iterators( node_index, box_begin, box_end, box_middle,
284  child_left, child_right );
285  if( length2( node( child_left ).center() - query )
286  < length2( node( child_right ).center() - query ) )
287  {
288  box_end = box_middle;
289  node_index = child_left;
290  }
291  else
292  {
293  box_begin = box_middle;
294  node_index = child_right;
295  }
296  }
297 
298  index_t nearest_box = mapping_morton_[box_begin];
299  vecn< DIMENSION > nearest_point =
300  get_point_hint_from_box( tree_[box_begin], nearest_box );
301  double distance = length( query - nearest_point );
302  return std::make_tuple( nearest_box, nearest_point, distance );
303  }
304 
305  /****************************************************************************/
306 
307  template < index_t DIMENSION >
309  const std::vector< Box< DIMENSION > >& bboxes )
310  {
311  this->initialize_tree( bboxes );
312  }
313 
314  template < index_t DIMENSION >
316  const Box< DIMENSION >& box, index_t element_id ) const
317  {
318  ringmesh_unused( element_id );
319  return box.center();
320  }
321 
322  /****************************************************************************/
323 
324  template < index_t DIMENSION >
326  : mesh_( mesh )
327  {
328  std::vector< Box< DIMENSION > > bboxes;
329  bboxes.resize( mesh.nb_edges() );
330  for( auto i : range( mesh.nb_edges() ) )
331  {
332  for( auto v : range( 2 ) )
333  {
334  bboxes[i].add_point( mesh.vertex(
335  mesh.edge_vertex( ElementLocalVertex( i, v ) ) ) );
336  }
337  }
338  this->initialize_tree( bboxes );
339  }
340 
341  template < index_t DIMENSION >
342  std::tuple< index_t, vecn< DIMENSION >, double >
344  const vecn< DIMENSION >& query ) const
345  {
346  DistanceToEdge action( mesh_ );
347  return this->closest_element_box( query, action );
348  }
349 
350  template < index_t DIMENSION >
351  std::tuple< double, vecn< DIMENSION > >
353  const vecn< DIMENSION >& query, index_t cur_box ) const
354  {
355  const auto& v0 = mesh_.vertex( mesh_.edge_vertex( { cur_box, 0 } ) );
356  const auto& v1 = mesh_.vertex( mesh_.edge_vertex( { cur_box, 1 } ) );
358  query, Geometry::Segment< DIMENSION >{ v0, v1 } );
359  }
360 
361  template < index_t DIMENSION >
363  const Box< DIMENSION >& box, index_t element_id ) const
364  {
365  ringmesh_unused( box );
366  return mesh_.vertex(
367  mesh_.edge_vertex( ElementLocalVertex( element_id, 0 ) ) );
368  }
369 
370  /****************************************************************************/
371 
372  template < index_t DIMENSION >
374  const SurfaceMeshBase< DIMENSION >& mesh )
375  : mesh_( mesh )
376  {
377  std::vector< Box< DIMENSION > > bboxes;
378  bboxes.resize( mesh.nb_polygons() );
379  for( auto i : range( mesh.nb_polygons() ) )
380  {
381  for( auto v : range( mesh.nb_polygon_vertices( i ) ) )
382  {
383  bboxes[i].add_point( mesh.vertex(
384  mesh.polygon_vertex( ElementLocalVertex( i, v ) ) ) );
385  }
386  }
387  this->initialize_tree( bboxes );
388  }
389 
390  template < index_t DIMENSION >
391  std::tuple< index_t, vecn< DIMENSION >, double >
393  const vecn< DIMENSION >& query ) const
394  {
395  DistanceToTriangle action( mesh_ );
396  return this->closest_element_box( query, action );
397  }
398 
399  template < index_t DIMENSION >
400  std::tuple< double, vecn< DIMENSION > >
402  const vecn< DIMENSION >& query, index_t cur_box ) const
403  {
404  const auto& v0 = mesh_.vertex( mesh_.polygon_vertex( { cur_box, 0 } ) );
405  const auto& v1 = mesh_.vertex( mesh_.polygon_vertex( { cur_box, 1 } ) );
406  const auto& v2 = mesh_.vertex( mesh_.polygon_vertex( { cur_box, 2 } ) );
408  query, Geometry::Triangle< DIMENSION >{ v0, v1, v2 } );
409  }
410 
411  template < index_t DIMENSION >
413  const Box< DIMENSION >& box, index_t element_id ) const
414  {
415  ringmesh_unused( box );
416  return mesh_.vertex(
417  mesh_.polygon_vertex( ElementLocalVertex( element_id, 0 ) ) );
418  }
419 
420  /****************************************************************************/
421 
422  template < index_t DIMENSION >
424  const VolumeMesh< DIMENSION >& mesh )
425  : mesh_( mesh )
426  {
427  std::vector< Box< DIMENSION > > bboxes;
428  bboxes.resize( mesh.nb_cells() );
429  for( auto i : range( mesh.nb_cells() ) )
430  {
431  for( auto v : range( mesh.nb_cell_vertices( i ) ) )
432  {
433  bboxes[i].add_point( mesh.vertex(
434  mesh.cell_vertex( ElementLocalVertex( i, v ) ) ) );
435  }
436  }
437  this->initialize_tree( bboxes );
438  }
439 
440  template < index_t DIMENSION >
442  const Box< DIMENSION >& box, index_t element_id ) const
443  {
444  ringmesh_unused( box );
445  return mesh_.vertex(
446  mesh_.cell_vertex( ElementLocalVertex( element_id, 0 ) ) );
447  }
448 
449  template < index_t DIMENSION >
451  const vecn< DIMENSION >& query ) const
452  {
454  query, AABBTree< DIMENSION >::ROOT_INDEX, 0, this->nb_bboxes() );
455  }
456 
457  template < index_t DIMENSION >
459  const vecn< DIMENSION >& query,
460  index_t node_index,
461  index_t box_begin,
462  index_t box_end ) const
463  {
464  if( !this->node( node_index ).contains( query ) )
465  {
466  return NO_ID;
467  }
468  if( box_end == box_begin + 1 )
469  {
470  index_t cell_id = this->mapping_morton_[box_begin];
471  if( mesh_cell_contains_point( mesh_, cell_id, query ) )
472  {
473  return cell_id;
474  }
475  else
476  {
477  return NO_ID;
478  }
479  }
480 
481  index_t box_middle, child_left, child_right;
482  this->get_recursive_iterators( node_index, box_begin, box_end,
483  box_middle, child_left, child_right );
484 
485  index_t result = containing_cell_recursive(
486  query, child_left, box_begin, box_middle );
487  if( result == NO_ID )
488  {
489  result = containing_cell_recursive(
490  query, child_right, box_middle, box_end );
491  }
492  return result;
493  }
494  template < index_t DIMENSION >
496  const vecn< DIMENSION >& p, const Box< DIMENSION >& B )
497  {
498  ringmesh_assert( B.contains( p ) );
499  double result = std::abs( p[0] - B.min()[0] );
500  result = std::min( result, std::abs( p[0] - B.max()[0] ) );
501  for( auto c : range( 1, DIMENSION ) )
502  {
503  result = std::min( result, std::abs( p[c] - B.min()[c] ) );
504  result = std::min( result, std::abs( p[c] - B.max()[c] ) );
505  }
506  return result;
507  }
508  template < index_t DIMENSION >
510  const vecn< DIMENSION >& p, const Box< DIMENSION >& B )
511  {
512  bool inside = true;
513  vecn< DIMENSION > result;
514  for( auto c : range( DIMENSION ) )
515  {
516  if( p[c] < B.min()[c] )
517  {
518  inside = false;
519  result[c] = p[c] - B.min()[c];
520  }
521  else if( p[c] > B.max()[c] )
522  {
523  inside = false;
524  result[c] = p[c] - B.max()[c];
525  }
526  }
527  if( inside )
528  {
529  return -inner_point_box_distance( p, B );
530  }
531  else
532  {
533  return result.length();
534  }
535  }
536  template class RINGMESH_API AABBTree< 2 >;
537  template class RINGMESH_API BoxAABBTree< 2 >;
538  template class RINGMESH_API LineAABBTree< 2 >;
539  template class RINGMESH_API SurfaceAABBTree< 2 >;
540 
541  template class RINGMESH_API AABBTree< 3 >;
542  template class RINGMESH_API BoxAABBTree< 3 >;
543  template class RINGMESH_API LineAABBTree< 3 >;
544  template class RINGMESH_API SurfaceAABBTree< 3 >;
545  template class RINGMESH_API VolumeAABBTree< 3 >;
546 } // namespace RINGMesh
AABB tree structure.
Definition: aabb.h:63
GEO::vecng< DIMENSION, double > vecn
Definition: types.h:74
std::tuple< index_t, vecn< DIMENSION >, double > closest_edge(const vecn< DIMENSION > &query) const
Gets the closest edge to a given point.
Definition: aabb.cpp:343
virtual index_t nb_cell_vertices(index_t cell_id) const =0
Gets the number of vertices of a cell.
virtual const vecn< DIMENSION > & vertex(index_t v_id) const =0
Gets a point.
double point_box_signed_distance(const vecn< DIMENSION > &p, const Box< DIMENSION > &B)
Definition: aabb.cpp:509
void get_recursive_iterators(index_t node_index, index_t box_begin, index_t box_end, index_t &middle_box, index_t &child_left, index_t &child_right) const
Definition: aabb.h:165
std::tuple< double, vecn< DIMENSION > > point_to_triangle(const Geometry::Point< DIMENSION > &point, const Geometry::Triangle< DIMENSION > &triangle)
bool contains(const vecn< DIMENSION > &b) const
Definition: box.h:143
std::vector< index_t > mapping_morton_
Definition: aabb.h:254
SurfaceAABBTree(const SurfaceMeshBase< DIMENSION > &mesh)
Definition: aabb.cpp:373
void ringmesh_unused(const T &)
Definition: common.h:105
virtual index_t cell_vertex(const ElementLocalVertex &cell_local_vertex) const =0
Gets a vertex index by cell and local vertex index.
const LineMesh< DIMENSION > & mesh_
Definition: aabb.h:318
ALIAS_2D_AND_3D(Box)
index_t containing_cell_recursive(const vecn< DIMENSION > &query, index_t node_index, index_t box_begin, index_t box_end) const
Definition: aabb.cpp:458
const SurfaceMeshBase< DIMENSION > & mesh_
Definition: aabb.h:369
std::tuple< double, vecn< DIMENSION > > operator()(const vecn< DIMENSION > &query, index_t cur_box) const
Definition: aabb.cpp:352
virtual index_t nb_edges() const =0
Gets the number of all the edges in the whole Mesh.
vecn< DIMENSION > center() const
Definition: box.h:71
vecn< DIMENSION > get_point_hint_from_box(const Box< DIMENSION > &box, index_t element_id) const override
Gets an element point from its box.
Definition: aabb.cpp:315
virtual index_t polygon_vertex(const ElementLocalVertex &polygon_local_vertex) const =0
Gets the vertex index by polygon index and local vertex index.
std::tuple< index_t, vecn< DIMENSION >, double > get_nearest_element_box_hint(const vecn< DIMENSION > &query) const
Gets an hint of the result.
Definition: aabb.cpp:274
BoxAABBTree(const std::vector< Box< DIMENSION > > &boxes)
Definition: aabb.cpp:308
vecn< DIMENSION > get_point_hint_from_box(const Box< DIMENSION > &box, index_t element_id) const override
Gets an element point from its box.
Definition: aabb.cpp:362
vecn< DIMENSION > get_point_hint_from_box(const Box< DIMENSION > &box, index_t element_id) const override
Gets an element point from its box.
Definition: aabb.cpp:412
std::tuple< double, vecn< DIMENSION > > operator()(const vecn< DIMENSION > &query, index_t cur_box) const
Definition: aabb.cpp:401
double inner_point_box_distance(const vecn< DIMENSION > &p, const Box< DIMENSION > &B)
Definition: aabb.cpp:495
void initialize_tree_recursive(const std::vector< Box< DIMENSION > > &bboxes, index_t node_index, index_t element_begin, index_t element_end)
The recursive instruction used in initialize_tree()
Definition: aabb.cpp:246
const vecn< DIMENSION > & max() const
Definition: box.h:66
virtual CellType cell_type(index_t cell_id) const =0
Gets the type of a cell.
LineAABBTree(const LineMesh< DIMENSION > &mesh)
Definition: aabb.cpp:325
const VolumeMesh< DIMENSION > & mesh_
Definition: aabb.h:403
index_t max_node_index(index_t node_index, index_t box_begin, index_t box_end)
Gets the number of nodes in the tree subset.
Definition: aabb.cpp:221
virtual index_t nb_polygon_vertices(index_t polygon_id) const =0
Gets the number of vertices in the polygon.
VolumeAABBTree(const VolumeMesh< DIMENSION > &mesh)
Definition: aabb.cpp:423
const vecn< DIMENSION > & min() const
Definition: box.h:61
#define ringmesh_assert(x)
index_t containing_cell(const vecn< DIMENSION > &query) const
Gets the cell contining a point.
Definition: aabb.cpp:450
bool contains(const container &in, const T &value)
Definition: algorithm.h:87
index_t nb_bboxes() const
Definition: aabb.h:73
virtual index_t nb_cells() const =0
Gets the number of cells in the Mesh.
virtual index_t nb_polygons() const =0
Gets the number of all polygons in the whole Mesh.
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
vecn< DIMENSION > get_point_hint_from_box(const Box< DIMENSION > &box, index_t element_id) const override
Gets an element point from its box.
Definition: aabb.cpp:441
virtual index_t edge_vertex(const ElementLocalVertex &edge_local_vertex) const =0
std::tuple< index_t, vecn< DIMENSION >, double > closest_triangle(const vecn< DIMENSION > &query) const
Gets the closest triangle to a given point.
Definition: aabb.cpp:392
void initialize_tree(const std::vector< Box< DIMENSION > > &bboxes)
Builds the tree.
Definition: aabb.cpp:211
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)
std::tuple< index_t, vecn< DIMENSION >, double > closest_element_box(const vecn< DIMENSION > &query, const EvalDistance &action) const
Gets the closest element box to a point.
Definition: aabb.h:100
const Box< DIMENSION > & node(index_t i) const
Definition: aabb.h:177
#define ringmesh_assert_not_reached