RINGMesh  Version 5.0.0
A programming library for geological model meshes
geomodel_builder.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 <stack>
39 
42 
49 namespace
50 {
51  using namespace RINGMesh;
52 
53  template < index_t DIMENSION >
54  gmme_id find_corner(
55  const GeoModel< DIMENSION >& geomodel, index_t geomodel_point_id )
56  {
57  const auto& geomodel_vertices = geomodel.mesh.vertices;
58  const auto& vertices =
59  geomodel_vertices.gme_vertices( geomodel_point_id );
60  for( const auto& vertex : vertices )
61  {
62  if( vertex.gmme.type() == Corner< DIMENSION >::type_name_static() )
63  {
64  return vertex.gmme;
65  }
66  }
67  return gmme_id{};
68  }
69 
75  template < index_t DIMENSION >
76  bool line_equal( const Line< DIMENSION >& line,
77  const std::vector< index_t >& rhs_vertices )
78  {
79  if( line.nb_vertices() != rhs_vertices.size() )
80  {
81  return false;
82  }
83  const auto& geomodel_vertices = line.geomodel().mesh.vertices;
84  bool equal{ true };
85  for( auto i : range( line.nb_vertices() ) )
86  {
87  if( rhs_vertices[i]
88  != geomodel_vertices.geomodel_vertex_id( line.gmme(), i ) )
89  {
90  equal = false;
91  break;
92  }
93  }
94  if( equal )
95  {
96  return true;
97  }
98  // If the order is the other one
99  equal = true;
100  for( auto i : range( line.nb_vertices() ) )
101  {
102  if( rhs_vertices[i]
103  != geomodel_vertices.geomodel_vertex_id(
104  line.gmme(), line.nb_vertices() - i - 1 ) )
105  {
106  equal = false;
107  break;
108  }
109  }
110  return equal;
111  }
112 
117  template < index_t DIMENSION >
118  void reorder_closed_line_vertices_to_start_at_corner(
119  const GeoModel< DIMENSION >& geomodel,
120  std::vector< index_t >& line_vertices )
121  {
122  if( geomodel.nb_corners() == 0 || line_vertices.empty() )
123  {
124  return;
125  }
126  for( auto i : range( 1, line_vertices.size() - 1 ) )
127  {
128  auto corner = find_corner( geomodel, line_vertices[i] );
129  if( corner.is_defined() )
130  {
131  line_vertices.pop_back();
132  std::rotate( line_vertices.begin(), line_vertices.begin() + i,
133  line_vertices.end() );
134  line_vertices.push_back( line_vertices.front() );
135  break;
136  }
137  }
138  }
139 
144  struct BorderPolygon
145  {
146  BorderPolygon(
147  index_t surface, index_t polygon, index_t v0, index_t v1 )
148  : v0_( v0 ), v1_( v1 ), surface_( surface ), polygon_( polygon )
149  {
150  }
151 
156  bool operator<( const BorderPolygon& rhs ) const
157  {
158  if( std::min( v0_, v1_ ) != std::min( rhs.v0_, rhs.v1_ ) )
159  {
160  return std::min( v0_, v1_ ) < std::min( rhs.v0_, rhs.v1_ );
161  }
162  if( std::max( v0_, v1_ ) != std::max( rhs.v0_, rhs.v1_ ) )
163  {
164  return std::max( v0_, v1_ ) < std::max( rhs.v0_, rhs.v1_ );
165  }
166  if( surface_ != rhs.surface_ )
167  {
168  return surface_ < rhs.surface_;
169  }
170  return polygon_ < rhs.polygon_;
171  }
172 
173  bool same_edge( const BorderPolygon& rhs ) const
174  {
175  return std::min( v0_, v1_ ) == std::min( rhs.v0_, rhs.v1_ )
176  && std::max( v0_, v1_ ) == std::max( rhs.v0_, rhs.v1_ );
177  }
178 
181  index_t v0_;
182  index_t v1_;
183  // Index of the surface containing the polygon
184  index_t surface_;
185  // Index of the polygon in the surface
186  index_t polygon_;
187  };
188 
189  template < index_t DIMENSION >
190  class CommonDataFromGeoModelSurfaces
191  {
192  ringmesh_disable_copy_and_move( CommonDataFromGeoModelSurfaces );
194 
195  protected:
196  explicit CommonDataFromGeoModelSurfaces(
197  const GeoModel< DIMENSION >& geomodel )
198  : geomodel_( geomodel )
199  {
200  const auto& geomodel_vertices = geomodel_.mesh.vertices;
201  for( const auto& surface : geomodel_.surfaces() )
202  {
203  const auto& mesh = surface.mesh();
204  auto S_id = surface.gmme();
205  for( auto p : range( surface.nb_mesh_elements() ) )
206  {
207  for( auto v :
208  range( surface.nb_mesh_element_vertices( p ) ) )
209  {
210  if( mesh.is_edge_on_border( { p, v } ) )
211  {
212  auto vertex = geomodel_vertices.geomodel_vertex_id(
213  S_id, { p, v } );
214  auto next_vertex =
215  geomodel_vertices.geomodel_vertex_id( S_id,
216  mesh.next_polygon_vertex( { p, v } ) );
217  border_polygons_.emplace_back(
218  surface.index(), p, vertex, next_vertex );
219  }
220  }
221  }
222  }
223  std::sort( border_polygons_.begin(), border_polygons_.end() );
224  }
225  ~CommonDataFromGeoModelSurfaces() = default;
226 
227  bool have_border_polygons_same_boundary_edge(
228  index_t t0, index_t t1 ) const
229  {
230  return this->border_polygons_[t0].same_edge(
231  this->border_polygons_[t1] );
232  }
233 
234  protected:
235  const GeoModel< DIMENSION >& geomodel_;
236  // All the polygons on a boundary of all the Surfaces of the GeoModel
237  std::vector< BorderPolygon > border_polygons_;
238  };
239 
240  ALIAS_2D_AND_3D( CommonDataFromGeoModelSurfaces );
241 
246  class GeoModelRegionFromSurfaces
247  {
248  public:
255  struct PolygonToSort
256  {
264  PolygonToSort( index_t index,
265  index_t surface_index,
266  const vec3& normal,
267  const vec3& p0,
268  const vec3& p1 )
269  : index_( index ), surface_index_( surface_index ), N_( normal )
270  {
271  ringmesh_assert( p0 != p1 );
272  auto e1 = normalize( p1 - p0 );
273  B_A_ = normalize( cross( e1, N_ ) );
274  }
275 
276  bool operator<( const PolygonToSort& r ) const
277  {
278  return angle_ < r.angle_;
279  }
280 
282  index_t index_;
283 
285  index_t surface_index_;
286 
288  vec3 N_;
289 
292  vec3 B_A_;
293 
294  // Values filled by sorting function in GeoModelRegionFromSurfaces
295  double angle_{ -max_float64() };
296  bool side_{ false };
297  };
298 
299  void add_polygon_edge( index_t surface_index,
300  const vec3& normal,
301  const vec3& p0,
302  const vec3& p1 )
303  {
304  auto polygon_id = static_cast< index_t >( polygons_.size() );
305  polygons_.emplace_back( polygon_id, surface_index, normal, p0, p1 );
306  }
307 
315  static vec3 rotate( const vec3& axis, double angle, const vec3& V )
316  {
317  auto q = normalize( axis ) * std::sin( 0.5 * angle );
318  vecn< 4 > quat( q[0], q[1], q[2], std::cos( 0.5 * angle ) );
319 
320  GEO::Matrix< 4, double > mat;
321  mat( 0, 0 ) = 1 - 2.0 * ( quat[1] * quat[1] + quat[2] * quat[2] );
322  mat( 1, 0 ) = 2.0 * ( quat[0] * quat[1] + quat[2] * quat[3] );
323  mat( 2, 0 ) = 2.0 * ( quat[2] * quat[0] - quat[1] * quat[3] );
324  mat( 3, 0 ) = 0.0;
325 
326  mat( 0, 1 ) = 2.0 * ( quat[0] * quat[1] - quat[2] * quat[3] );
327  mat( 1, 1 ) = 1 - 2.0 * ( quat[2] * quat[2] + quat[0] * quat[0] );
328  mat( 2, 1 ) = 2.0 * ( quat[1] * quat[2] + quat[0] * quat[3] );
329  mat( 3, 1 ) = 0.0;
330 
331  mat( 0, 2 ) = 2.0 * ( quat[2] * quat[0] + quat[1] * quat[3] );
332  mat( 1, 2 ) = 2.0 * ( quat[1] * quat[2] - quat[0] * quat[3] );
333  mat( 2, 2 ) = 1 - 2.0 * ( quat[1] * quat[1] + quat[0] * quat[0] );
334  mat( 3, 2 ) = 0.0;
335 
336  mat( 0, 3 ) = 0.0;
337  mat( 1, 3 ) = 0.0;
338  mat( 2, 3 ) = 0.0;
339  mat( 3, 3 ) = 1.0;
340 
341  vecn< 4 > normalizedV( V.x, V.y, V.z, 1.0 );
342  vecn< 4 > result;
343  GEO::mult( mat, normalizedV.data(), result.data() );
344 
345  ringmesh_assert( std::fabs( result.w ) > global_epsilon );
346  double inv_w{ 1.0 / result.w };
347  return { result.x * inv_w, result.y * inv_w, result.z * inv_w };
348  }
349 
350  void sort()
351  {
352  ringmesh_assert( !polygons_.empty() );
353 
354  std::pair< index_t, bool > default_pair( index_t( -1 ), false );
355  sorted_polygons_.resize( 2 * polygons_.size(), default_pair );
356 
357  // If there is only one Polygon to sort - nothing to do
358  if( polygons_.size() == 1 )
359  {
360  sorted_polygons_[0] = std::pair< index_t, bool >(
361  polygons_[0].surface_index_, true );
362  sorted_polygons_[1] = std::pair< index_t, bool >(
363  polygons_[0].surface_index_, false );
364  return;
365  }
366 
367  // Initialization
368  // We start on the plus (true) side of the first Polygon
369  sorted_polygons_[0] =
370  std::pair< index_t, bool >( polygons_[0].surface_index_, true );
371 
372  // Reference vectors with wich angles will be computed
373  vec3 N_ref{ polygons_[0].N_ };
374  vec3 B_A_ref{ polygons_[0].B_A_ };
375  vec3 Ax_ref{ normalize( cross( B_A_ref, N_ref ) ) };
376 
377  // The minus (false) side of the start polygon will the last one
378  // encountered
379  polygons_[0].angle_ = 2 * M_PI;
380  polygons_[0].side_ = false;
381 
382  for( auto i : range( 1, polygons_.size() ) )
383  {
384  auto& cur = polygons_[i];
385  // Computes the angle RADIANS between the reference and the
386  // current
387  // polygon
388  double cos = dot( B_A_ref, cur.B_A_ );
389  // Remove invalid values
390  if( cos < -1 )
391  {
392  cos = -1;
393  }
394  else if( cos > 1 )
395  {
396  cos = 1;
397  }
398  cur.angle_ = std::acos( cos );
399  // Put the angle between PI and 2PI if necessary
400  if( dot( cross( B_A_ref, cur.B_A_ ), Ax_ref ) < 0. )
401  {
402  cur.angle_ = 2 * M_PI - cur.angle_;
403  }
404 
405  // Get the side of the surface first encountered
406  // when rotating in the N_ref direction
407  auto N_rotate = rotate( Ax_ref, -cur.angle_, cur.N_ );
408  cur.side_ = dot( N_rotate, N_ref ) < 0;
409  }
410 
411  // Sorts the Surfaces according to the angle
412  std::sort( polygons_.begin(), polygons_.end() );
413 
414  // Fills the sorted surfaces adding the side
415  index_t it{ 1 };
416  for( auto& cur : polygons_ )
417  {
418  if( cur.index_ == 0 )
419  { // The last to add
420  sorted_polygons_[it].first = cur.surface_index_;
421  sorted_polygons_[it].second = cur.side_;
422  }
423  else
424  {
425  sorted_polygons_[it].first = cur.surface_index_;
426  sorted_polygons_[it].second = cur.side_;
427  sorted_polygons_[it + 1].first = cur.surface_index_;
428  sorted_polygons_[it + 1].second = !cur.side_;
429  it += 2;
430  }
431  }
432  // All the surfaces must have been sorted
433  ringmesh_assert( std::count( sorted_polygons_.begin(),
434  sorted_polygons_.end(), default_pair )
435  == 0 );
436  }
437 
438  void clear()
439  {
440  polygons_.clear();
441  sorted_polygons_.clear();
442  }
443 
446  const std::pair< index_t, bool >& next(
447  const std::pair< index_t, bool >& in ) const
448  {
449  for( auto i : range( sorted_polygons_.size() ) )
450  {
451  if( sorted_polygons_[i] == in )
452  {
453  if( i == sorted_polygons_.size() - 1 )
454  {
455  return sorted_polygons_[sorted_polygons_.size() - 2];
456  }
457  if( i == 0 )
458  {
459  return sorted_polygons_[1];
460  }
461 
462  if( sorted_polygons_[i + 1].first
463  == sorted_polygons_[i].first )
464  {
465  // The next has the same surface id, check its sign
466  if( sorted_polygons_[i + 1].second
467  != sorted_polygons_[i].second )
468  {
469  return sorted_polygons_[i - 1];
470  }
471  // Sign is the same
472  return sorted_polygons_[i + 1];
473  }
474  ringmesh_assert( sorted_polygons_[i - 1].first
475  == sorted_polygons_[i].first );
476  if( sorted_polygons_[i - 1].second
477  != sorted_polygons_[i].second )
478  {
479  return sorted_polygons_[i + 1];
480  }
481  return sorted_polygons_[i - 1];
482  }
483  }
485  return sorted_polygons_.front();
486  }
487 
488  private:
489  std::vector< PolygonToSort > polygons_;
490  // Pairs global polygon identifier (Surface index) and side reached
491  std::vector< std::pair< index_t, bool > > sorted_polygons_;
492  };
493 
494  class RegionTopologyFromGeoModelSurfaces
495  : public CommonDataFromGeoModelSurfaces< 3 >
496  {
497  public:
498  explicit RegionTopologyFromGeoModelSurfaces(
499  const GeoModel3D& geomodel )
500  : CommonDataFromGeoModelSurfaces3D( geomodel ),
501  region_info_( geomodel.nb_lines() )
502  {
503  }
504 
505  void compute_region_info()
506  {
507  const auto& vertices = this->geomodel_.mesh.vertices;
508  for( const auto& line : geomodel_.lines() )
509  {
510  BorderPolygon line_border{ NO_ID, NO_ID,
511  vertices.geomodel_vertex_id( line.gmme(), 0 ),
512  vertices.geomodel_vertex_id( line.gmme(), 1 ) };
513  for( const auto& border : this->border_polygons_ )
514  {
515  if( line_border.same_edge( border ) )
516  {
517  auto surface_id = border.surface_;
518  region_info_[line.index()].add_polygon_edge( surface_id,
519  this->geomodel_.surface( surface_id )
520  .mesh()
521  .polygon_normal( border.polygon_ ),
522  vertices.vertex( border.v0_ ),
523  vertices.vertex( border.v1_ ) );
524  }
525  }
526  region_info_[line.index()].sort();
527  }
528  }
529 
530  const std::vector< GeoModelRegionFromSurfaces >& region_info() const
531  {
532  return region_info_;
533  }
534 
535  private:
536  std::vector< GeoModelRegionFromSurfaces > region_info_;
537  };
538 
539  struct LineDefinition
540  {
541  bool is_closed()
542  {
543  return vertices_.front() == vertices_.back();
544  }
545 
546  template < index_t DIMENSION >
547  void reoder_closed_line_vertices(
548  const GeoModel< DIMENSION >& geomodel )
549  {
550  // Vertices can begin and end at any vertex
551  reorder_closed_line_vertices_to_start_at_corner(
552  geomodel, vertices_ );
553  }
554 
555  void clear()
556  {
557  vertices_.clear();
558  adjacent_surfaces_.clear();
559  }
560 
561  std::vector< index_t > vertices_;
562  std::vector< index_t > adjacent_surfaces_;
563  };
564 
574  template < index_t DIMENSION >
575  class LineGeometryFromGeoModelSurfaces
576  : public CommonDataFromGeoModelSurfaces< DIMENSION >
577  {
578  public:
585  explicit LineGeometryFromGeoModelSurfaces(
586  const GeoModel< DIMENSION >& geomodel )
587  : CommonDataFromGeoModelSurfaces< DIMENSION >( geomodel ),
588  cur_border_polygon_( 0 )
589  {
590  visited_.resize( this->border_polygons_.size(), false );
591  }
592 
599  bool compute_next_line_geometry()
600  {
601  for( ; cur_border_polygon_ < this->border_polygons_.size();
602  cur_border_polygon_++ )
603  {
604  if( is_visited( cur_border_polygon_ ) )
605  {
606  continue;
607  }
608  cur_line_.clear();
609  compute_line_geometry();
610  return true;
611  }
612  return false;
613  }
614 
615  LineDefinition& current_line()
616  {
617  return cur_line_;
618  }
619 
620  private:
621  void compute_line_geometry()
622  {
623  visit_border_polygons_on_same_edge( cur_border_polygon_ );
624  auto p0 = this->border_polygons_[cur_border_polygon_].v0_;
625  auto p1 = this->border_polygons_[cur_border_polygon_].v1_;
626  cur_line_.vertices_.push_back( p0 );
627  cur_line_.vertices_.push_back( p1 );
628 
629  cur_line_.adjacent_surfaces_ =
630  get_adjacent_surfaces( cur_border_polygon_ );
631 
632  bool backward{ false };
633  get_one_line_vertices( backward );
634  backward = true;
635  get_one_line_vertices( backward );
636  }
643  void get_one_line_vertices( bool backward )
644  {
646  cur_border_polygon_ < this->border_polygons_.size() );
647  ringmesh_assert( cur_border_polygon_ != NO_ID );
648 
649  auto t = get_next_border_polygon( cur_border_polygon_, backward );
650  ringmesh_assert( t != NO_ID );
651  while( propagate( t ) )
652  {
653  visit_border_polygons_on_same_edge( t );
654  add_border_polygon_vertices_to_line( t, backward );
655  t = get_next_border_polygon( t, backward );
656  ringmesh_assert( t != NO_ID );
657  }
658  }
659 
660  bool propagate( index_t t ) const
661  {
662  return t != cur_border_polygon_ && !is_visited( t )
663  && equal_to_line_adjacent_surfaces( t );
664  }
665 
666  bool is_visited( index_t i ) const
667  {
668  return visited_[i];
669  }
670 
671  bool equal_to_line_adjacent_surfaces( index_t t ) const
672  {
673  auto input = get_adjacent_surfaces( t );
674  if( input.size() != cur_line_.adjacent_surfaces_.size() )
675  {
676  return false;
677  }
678  return std::equal( input.begin(), input.end(),
679  cur_line_.adjacent_surfaces_.begin() );
680  }
681 
682  void add_border_polygon_vertices_to_line(
683  index_t polygon_index, bool backward )
684  {
685  const auto& border_polygon = this->border_polygons_[polygon_index];
686  add_vertices_to_line(
687  border_polygon.v0_, border_polygon.v1_, !backward );
688  }
689 
690  void add_vertices_to_line( index_t v0, index_t v1, bool at_the_end )
691  {
692  auto to_add = v1;
693  if( at_the_end )
694  {
695  auto end_vertex = cur_line_.vertices_.back();
696  if( v1 == end_vertex )
697  {
698  to_add = v0;
699  }
700  else
701  {
702  ringmesh_assert( v0 == end_vertex );
703  }
704  cur_line_.vertices_.push_back( to_add );
705  }
706  else
707  {
708  auto first_vertex = cur_line_.vertices_.front();
709  if( v1 == first_vertex )
710  {
711  to_add = v0;
712  }
713  else
714  {
715  ringmesh_assert( v0 == first_vertex );
716  }
717  cur_line_.vertices_.insert(
718  cur_line_.vertices_.begin(), to_add );
719  }
720  }
721 
725  index_t get_next_border_polygon( index_t from, bool backward ) const
726  {
727  const auto& border_polygon = this->border_polygons_[from];
728  const auto& S = this->geomodel_.surface( border_polygon.surface_ );
729  const auto& mesh = S.mesh();
730  auto surface_id = S.gmme();
731  const auto& geomodel_vertices = this->geomodel_.mesh.vertices;
732 
733  // Gets the next edge on border in the Surface
734  auto p = border_polygon.polygon_;
735  auto possible_v0_id = geomodel_vertices.mesh_entity_vertex_id(
736  surface_id, border_polygon.v0_ );
737  ringmesh_assert( !possible_v0_id.empty() );
738  index_t v0_id{ NO_ID };
739  for( auto id : possible_v0_id )
740  {
741  if( mesh.vertex_index_in_polygon( p, id ) != NO_ID )
742  {
743  v0_id = id;
744  }
745  }
746  ringmesh_assert( v0_id != NO_ID );
747  auto v0_id_in_polygon = mesh.vertex_index_in_polygon( p, v0_id );
748  ringmesh_assert( v0_id_in_polygon != NO_ID );
749 
750  const PolygonLocalEdge cur_polygon_local_edge{ p,
751  v0_id_in_polygon };
752  PolygonLocalEdge next_polygon_local_edge0_on_border =
753  backward ? mesh.prev_on_border( cur_polygon_local_edge )
754  : mesh.next_on_border( cur_polygon_local_edge );
756  next_polygon_local_edge0_on_border.polygon_id_ != NO_ID );
758  next_polygon_local_edge0_on_border.local_edge_id_ != NO_ID );
759 
760  PolygonLocalEdge next_polygon_local_edge1_on_border =
761  mesh.next_polygon_vertex( next_polygon_local_edge0_on_border );
763  next_polygon_local_edge1_on_border.polygon_id_ != NO_ID );
765  next_polygon_local_edge1_on_border.local_edge_id_ != NO_ID );
766 
767  // Finds the BorderPolygon that is corresponding to this
768  // It must exist and there is only one
769  BorderPolygon bait{ border_polygon.surface_,
770  next_polygon_local_edge0_on_border.polygon_id_,
771  geomodel_vertices.geomodel_vertex_id(
772  surface_id, next_polygon_local_edge0_on_border ),
773  geomodel_vertices.geomodel_vertex_id(
774  surface_id, next_polygon_local_edge1_on_border ) };
775  auto result = find_sorted( this->border_polygons_, bait );
776 
777  ringmesh_assert( result != NO_ID );
778  ringmesh_assert( this->border_polygons_[result].same_edge( bait ) );
779  return result;
780  }
781 
786  void visit_border_polygons_on_same_edge( index_t border_id )
787  {
788  visited_[border_id] = true;
789  for( auto next_border_id = border_id + 1;
790  next_border_id < this->border_polygons_.size()
791  && this->have_border_polygons_same_boundary_edge(
792  border_id, next_border_id );
793  next_border_id++ )
794  {
795  visited_[next_border_id] = true;
796  }
797  for( auto prev_border_id = border_id - 1;
798  prev_border_id != NO_ID
799  && this->have_border_polygons_same_boundary_edge(
800  border_id, prev_border_id );
801  prev_border_id-- )
802  {
803  visited_[prev_border_id] = true;
804  }
805  }
806 
813  std::vector< index_t > get_adjacent_surfaces( index_t border_id ) const
814  {
815  std::vector< index_t > adjacent_surfaces;
816  adjacent_surfaces.reserve( 10 );
817  adjacent_surfaces.push_back(
818  this->border_polygons_[border_id].surface_ );
819 
820  for( auto next_border_id = border_id + 1;
821  next_border_id < this->border_polygons_.size()
822  && this->have_border_polygons_same_boundary_edge(
823  border_id, next_border_id );
824  next_border_id++ )
825  {
826  adjacent_surfaces.push_back(
827  this->border_polygons_[next_border_id].surface_ );
828  }
829 
830  for( auto prev_border_id = border_id - 1;
831  prev_border_id != NO_ID
832  && this->have_border_polygons_same_boundary_edge(
833  border_id, prev_border_id );
834  prev_border_id-- )
835  {
836  adjacent_surfaces.push_back(
837  this->border_polygons_[prev_border_id].surface_ );
838  }
839  std::sort( adjacent_surfaces.begin(), adjacent_surfaces.end() );
840  return adjacent_surfaces;
841  }
842 
843  private:
844  // Internal use to flag the visited border_polygons when computing the
845  // Lines
846  std::vector< bool > visited_;
847 
848  // Currently computed line information
849  index_t cur_border_polygon_;
850  LineDefinition cur_line_;
851  };
852 
853 } // namespace
854 
855 namespace RINGMesh
856 {
857  template < index_t DIMENSION >
860  : builder_( builder ),
861  geomodel_( geomodel ),
862  geomodel_access_( geomodel )
863  {
864  }
865 
866  template < index_t DIMENSION >
868  const GeoModel< DIMENSION >& from )
869  {
870  builder_.topology.copy_topology( from );
871  builder_.geometry.copy_meshes( from );
872  builder_.geology.copy_geology( from );
873  }
874 
875  template < index_t DIMENSION >
878  : builder_( builder ),
879  geomodel_( geomodel ),
880  geomodel_access_( geomodel )
881  {
882  }
883 
884  template < index_t DIMENSION >
887  : topology( builder, geomodel ),
888  geometry( builder, geomodel ),
889  geology( builder, geomodel ),
890  removal( builder, geomodel ),
891  repair( builder, geomodel ),
892  copy( builder, geomodel ),
893  info( builder, geomodel ),
894  geomodel_( geomodel ),
895  geomodel_access_( geomodel )
896  {
897  }
898 
899  template < index_t DIMENSION >
901  {
902  std::vector< vecn< DIMENSION > > point_extremities;
903  point_extremities.reserve( geomodel_.nb_lines() * DIMENSION );
904  for( const auto& line : geomodel_.lines() )
905  {
906  point_extremities.push_back( line.vertex( 0 ) );
907  point_extremities.push_back(
908  line.vertex( line.nb_vertices() - 1 ) );
909  }
910 
911  NNSearch< DIMENSION > nn_search( point_extremities );
912  std::vector< index_t > index_map;
913  std::vector< vecn< DIMENSION > > unique_points;
914  std::tie( std::ignore, index_map, unique_points ) =
916  geomodel_.epsilon() );
917 
918  topology.create_mesh_entities( Corner< DIMENSION >::type_name_static(),
919  static_cast< index_t >( unique_points.size() ) );
920  for( index_t c : range( geomodel_.nb_corners() ) )
921  {
922  geometry.set_corner( c, unique_points[c] );
923  }
924  index_t index = 0;
925  for( const auto& line : geomodel_.lines() )
926  {
927  gmme_id line_id = line.gmme();
928  index_t point0 = index_map[index++];
929  gmme_id corner0( Corner< DIMENSION >::type_name_static(), point0 );
930  index_t point1 = index_map[index++];
931  gmme_id corner1( Corner< DIMENSION >::type_name_static(), point1 );
932  topology.add_mesh_entity_boundary_relation( line_id, corner0 );
933  topology.add_mesh_entity_boundary_relation( line_id, corner1 );
934 
935  // Update line vertex extremities with corner coordinates
936  geometry.set_mesh_entity_vertex(
937  line_id, 0, unique_points[point0], false );
938  geometry.set_mesh_entity_vertex(
939  line_id, line.nb_vertices() - 1, unique_points[point1], false );
940  }
941  }
942 
943  template < index_t DIMENSION >
946  {
947  LineGeometryFromGeoModelSurfaces< DIMENSION > line_computer(
948  geomodel_ );
949 
950  while( line_computer.compute_next_line_geometry() )
951  {
952  auto line = line_computer.current_line();
953 
954  if( line.is_closed() )
955  {
956  line.reoder_closed_line_vertices( geomodel_ );
957  }
958 
959  auto& vertices = line.vertices_;
960  auto first_corner =
961  topology.find_or_create_corner( vertices.front() );
962  auto second_corner =
963  topology.find_or_create_corner( vertices.back() );
964 
965  const auto& adjacent_surfaces = line.adjacent_surfaces_;
966  auto backup_nb_lines = geomodel_.nb_lines();
967  auto line_index = topology.find_or_create_line(
968  adjacent_surfaces, first_corner, second_corner );
969 
970  bool created_line = geomodel_.nb_lines() != backup_nb_lines;
971  if( created_line )
972  {
973  geometry.set_line( line_index.index(), vertices );
974 
975  for( auto j : adjacent_surfaces )
976  {
977  gmme_id surface_index{
979  };
980  topology.add_mesh_entity_boundary_relation(
981  surface_index, line_index );
982  }
983  topology.add_mesh_entity_boundary_relation(
984  line_index, first_corner );
985  topology.add_mesh_entity_boundary_relation(
986  line_index, second_corner );
987  }
988  else
989  {
990  bool same_geometry = line_equal(
991  geomodel_.line( line_index.index() ), vertices );
992  if( !same_geometry )
993  {
994  geometry.set_line( line_index.index(), vertices );
995  }
996  }
997  }
998  geometry.clear_geomodel_mesh();
999  }
1000 
1001  template < index_t DIMENSION >
1003  {
1004  if( geomodel_.name().empty() )
1005  {
1006  info.set_geomodel_name( "model_default_name" );
1007  }
1008 
1009  cut_geomodel_on_internal_boundaries();
1010  print_geomodel( geomodel_ );
1011  }
1012 
1014  : GeoModelBuilderBase< 2 >( *this, geomodel )
1015  {
1016  }
1017 
1018  template <>
1020  {
1021  geometry.cut_surfaces_by_internal_lines();
1022  }
1023 
1025  : GeoModelBuilderBase< 3 >( *this, geomodel )
1026  {
1027  }
1028 
1029  template <>
1031  {
1032  geometry.cut_surfaces_by_internal_lines();
1033  geometry.cut_regions_by_internal_surfaces();
1034  }
1035 
1037  {
1038  RegionTopologyFromGeoModelSurfaces region_computer{ geomodel_ };
1039  region_computer.compute_region_info();
1040 
1041  const auto& region_info = region_computer.region_info();
1042 
1043  if( geomodel_.nb_surfaces() < 2 || geomodel_.nb_lines() == 0 )
1044  {
1045  throw RINGMeshException( "GeoModel",
1046  "You need at least 1 line and 2 surfaces to use "
1047  "GeoModelBuilder::build_regions_from_lines_and_surfaces" );
1048  }
1049 
1050  // Each side of each Surface is in one Region( +side is first )
1051  std::vector< index_t > surf_2_region(
1052  2 * geomodel_.nb_surfaces(), NO_ID );
1053 
1054  // Start with the first Surface on its + side
1055  std::stack< std::pair< index_t, bool > > S;
1056  S.emplace( 0, true );
1057 
1058  while( !S.empty() )
1059  {
1060  auto cur = S.top();
1061  S.pop();
1062  // This side is already assigned
1063  if( surf_2_region[cur.second ? 2 * cur.first : 2 * cur.first + 1]
1064  != NO_ID )
1065  {
1066  continue;
1067  }
1068  // Create a new region
1069  gmme_id cur_region_id{ Region3D::type_name_static(),
1070  geomodel_.nb_regions() };
1071  topology.create_mesh_entities( Region3D::type_name_static(), 1 );
1072  // Get all oriented surfaces defining this region
1073  std::stack< std::pair< index_t, bool > > SR;
1074  SR.push( cur );
1075  while( !SR.empty() )
1076  {
1077  auto s = SR.top();
1078  SR.pop();
1079  index_t s_id = s.second ? 2 * s.first : 2 * s.first + 1;
1080  // This oriented surface has already been visited
1081  if( surf_2_region[s_id] != NO_ID )
1082  {
1083  continue;
1084  }
1085  // Add the surface to the current region
1086  topology.add_mesh_entity_boundary_relation( cur_region_id,
1087  gmme_id{ Surface3D::type_name_static(), s.first },
1088  s.second );
1089  surf_2_region[s_id] = cur_region_id.index();
1090 
1091  // Check the other side of the surface and push it in S
1092  index_t s_id_opp = !s.second ? 2 * s.first : 2 * s.first + 1;
1093  if( surf_2_region[s_id_opp] == NO_ID )
1094  {
1095  S.emplace( s.first, !s.second );
1096  }
1097  // For each contact, push the next oriented surface that is in
1098  // the same region
1099  const auto& surface = geomodel_.surface( s.first );
1100  for( auto i : range( surface.nb_boundaries() ) )
1101  {
1102  const auto& n =
1103  region_info[surface.boundary_gmme( i ).index()].next(
1104  s );
1105  index_t n_id = n.second ? 2 * n.first : 2 * n.first + 1;
1106 
1107  if( surf_2_region[n_id] == NO_ID )
1108  {
1109  SR.push( n );
1110  }
1111  }
1112  }
1113  }
1114 
1115  // Check if all the surfaces were visited
1116  // If not, this means that there are additionnal regions included in
1117  // those built
1118  if( std::count( surf_2_region.begin(), surf_2_region.end(), NO_ID )
1119  != 0 )
1120  {
1121  Logger::err( "GeoModel",
1122  "Small bubble regions were skipped at geomodel building " );
1123  // Or, most probably, we have a problem before
1126  }
1127 
1128  // We need to remove from the regions_ the one corresponding
1129  // to the "universe", i.e., the one with the biggest volume.
1130  double max_volume{ -1. };
1131  index_t universe_id{ NO_ID };
1132  for( const auto& region : geomodel_.regions() )
1133  {
1134  double cur_volume = region.size();
1135  if( cur_volume > max_volume )
1136  {
1137  max_volume = cur_volume;
1138  universe_id = region.index();
1139  }
1140  }
1141  const auto& cur_region = geomodel_.region( universe_id );
1142  std::set< gmme_id > to_erase;
1143  to_erase.insert( cur_region.gmme() );
1144  removal.remove_mesh_entities( to_erase );
1145  }
1146 
1147  template class RINGMESH_API GeoModelBuilderBase< 2 >;
1148  template class RINGMESH_API GeoModelBuilderInfo< 2 >;
1149  template class RINGMESH_API GeoModelBuilderCopy< 2 >;
1150 
1151  template class RINGMESH_API GeoModelBuilderBase< 3 >;
1152  template class RINGMESH_API GeoModelBuilderInfo< 3 >;
1153  template class RINGMESH_API GeoModelBuilderCopy< 3 >;
1154 
1155 } // namespace RINGMesh
#define ringmesh_disable_copy_and_move(Class)
Definition: common.h:76
const GeoModel< DIMENSION > & geomodel() const
GeoModelMesh< DIMENSION > mesh
Definition: geomodel.h:241
GEO::vecng< DIMENSION, double > vecn
Definition: types.h:74
GeoModel< DIMENSION > & geomodel_
GeoModelBuilderGeometry< DIMENSION > geometry
const vecn< DIMENSION > & vertex(index_t vertex_index) const
Coordinates of the vertex_index.
GeoModelBuilderBase(GeoModelBuilder< DIMENSION > &builder, GeoModel< DIMENSION > &geomodel)
Base class to build or edit a GeoModel.
vecn< 3 > vec3
Definition: types.h:76
void RINGMESH_API rotate(GeoModel3D &geomodel, const vec3 &origin, const vec3 &axis, double angle, bool degrees=false)
Rotate the boundary geomodel.
GeoModelBuilderTopology< DIMENSION > topology
index_t find_sorted(const container &in, const T &value)
Returns the position of the first entity matching.
Definition: algorithm.h:73
GeoModel< DIMENSION > & geomodel_
GeoModel< DIMENSION > & geomodel_
#define ringmesh_template_assert_2d_or_3d(type)
Definition: common.h:80
A GeoModelEntity of type CORNER.
GeoModelBuilderInfo(GeoModelBuilder< DIMENSION > &builder, GeoModel< DIMENSION > &geomodel)
ALIAS_2D_AND_3D(Box)
bool is_closed() const
A Line is closed if its two extremities are identitical.
void print_geomodel(const GeoModel< DIMENSION > &geomodel)
Print in the console the geomodel statistics.
GeoModelBuilder< DIMENSION > & builder_
static void err(const std::string &feature, const Args &... args)
Definition: logger.h:68
static MeshEntityType type_name_static()
GeoModelBuilderRemoval< DIMENSION > removal
GeoModelAccess< DIMENSION > geomodel_access_
GeoModelBuilderCopy(GeoModelBuilder< DIMENSION > &builder, GeoModel< DIMENSION > &geomodel)
index_t nb_corners() const
Definition: geomodel.h:201
#define ringmesh_assert(x)
GeoModelAccess< DIMENSION > geomodel_access_
Classes to build GeoModel from various inputs.
Definition: algorithm.h:48
A GeoModelEntity of type LINE.
std::tuple< index_t, std::vector< index_t >, std::vector< vecn< DIMENSION > > > get_colocated_index_mapping_and_unique_points(double epsilon) const
Gets the index_map that link all the points to a no duplicated list of index in the list of unique_po...
Definition: nn_search.cpp:177
This template is a specialization of a gme_id to the GeoModelMeshEntity.
Definition: entity_type.h:285
void copy_geomodel(const GeoModel< DIMENSION > &from)
#define ringmesh_assert_not_reached