1 // Copyright (c) 2020 GeometryFactory (France) and Telecom Paris (France).
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org)
5 //
6 // $URL: https://github.com/CGAL/cgal/blob/v5.3/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h $
7 // $Id: split_long_edges.h 0fdfebd 2020-12-17T17:30:17+01:00 Jane Tournois
8 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 //
11 // Author(s)     : Jane Tournois, Noura Faraj, Jean-Marc Thiery, Tamy Boubekeur
12 
13 #ifndef CGAL_INTERNAL_SPLIT_LONG_EDGES_H
14 #define CGAL_INTERNAL_SPLIT_LONG_EDGES_H
15 
16 #include <CGAL/license/Tetrahedral_remeshing.h>
17 
18 #include <boost/bimap.hpp>
19 #include <boost/bimap/set_of.hpp>
20 #include <boost/bimap/multiset_of.hpp>
21 #include <boost/unordered_map.hpp>
22 #include <boost/container/small_vector.hpp>
23 
24 #include <CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h>
25 
26 #include <functional>
27 #include <utility>
28 
29 namespace CGAL
30 {
31 namespace Tetrahedral_remeshing
32 {
33 namespace internal
34 {
35 template<typename C3t3>
split_edge(const typename C3t3::Edge & e,C3t3 & c3t3)36 typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e,
37                                         C3t3& c3t3)
38 {
39   typedef typename C3t3::Triangulation       Tr;
40   typedef typename C3t3::Subdomain_index     Subdomain_index;
41   typedef typename C3t3::Surface_patch_index Surface_patch_index;
42   typedef typename Tr::Geom_traits::Point_3 Point;
43   typedef typename Tr::Facet                Facet;
44   typedef typename Tr::Vertex_handle        Vertex_handle;
45   typedef typename Tr::Cell_handle          Cell_handle;
46   typedef typename Tr::Cell_circulator      Cell_circulator;
47 
48   Tr& tr = c3t3.triangulation();
49   const Vertex_handle v1 = e.first->vertex(e.second);
50   const Vertex_handle v2 = e.first->vertex(e.third);
51 
52   const Point m = tr.geom_traits().construct_midpoint_3_object()
53     (point(v1->point()), point(v2->point()));
54 
55   //backup subdomain info of incident cells before making changes
56   short dimension = 0;
57   if(c3t3.is_in_complex(e))
58     dimension = 1;
59   else
60   {
61     const std::size_t nb_patches = nb_incident_surface_patches(e, c3t3);
62     if(nb_patches == 1)
63       dimension = 2;
64     else if(nb_patches == 0)
65       dimension = 3;
66     else
67       CGAL_assertion(false);//e should be in complex
68   }
69   CGAL_assertion(dimension > 0);
70 
71   boost::unordered_map<Facet, Subdomain_index> cells_info;
72   boost::unordered_map<Facet, std::pair<Vertex_handle, Surface_patch_index> > facets_info;
73 
74   // check orientation and collect incident cells to avoid circulating twice
75   boost::container::small_vector<Cell_handle, 30> inc_cells;
76   Cell_circulator circ = tr.incident_cells(e);
77   Cell_circulator end = circ;
78   do
79   {
80     inc_cells.push_back(circ);
81     if (tr.is_infinite(circ))
82     {
83       ++circ;
84       continue;
85     }
86 
87     //1st half-cell
88     std::array<Point, 4> pts = { point(circ->vertex(0)->point()),
89                                  point(circ->vertex(1)->point()),
90                                  point(circ->vertex(2)->point()),
91                                  point(circ->vertex(3)->point()) };
92     const int i1 = circ->index(v1);
93     const Point p1 = pts[i1];
94     pts[i1] = m;
95     if(CGAL::orientation(pts[0], pts[1], pts[2], pts[3]) != CGAL::POSITIVE)
96       return Vertex_handle();
97 
98     //2nd half-cell
99     pts[i1] = p1;
100     pts[circ->index(v2)] = m;
101     if (CGAL::orientation(pts[0], pts[1], pts[2], pts[3]) != CGAL::POSITIVE)
102       return Vertex_handle();
103 
104     ++circ;
105   }
106   while (circ != end);
107 
108   for(Cell_handle c : inc_cells)
109   {
110     const int index_v1 = c->index(v1);
111     const int index_v2 = c->index(v2);
112 
113     //keys are the opposite facets to the ones not containing e,
114     //because they will not be modified
115     const Subdomain_index subdomain = c3t3.subdomain_index(c);
116     const Facet opp_facet1 = tr.mirror_facet(Facet(c, index_v1));
117     const Facet opp_facet2 = tr.mirror_facet(Facet(c, index_v2));
118 
119     // volume data
120     cells_info.insert(std::make_pair(opp_facet1, subdomain));
121     cells_info.insert(std::make_pair(opp_facet2, subdomain));
122     if (c3t3.is_in_complex(c))
123       c3t3.remove_from_complex(c);
124 
125     // surface data for facets of the cells to be split
126     const int findex = CGAL::Triangulation_utils_3::next_around_edge(index_v1, index_v2);
127     Surface_patch_index patch = c3t3.surface_patch_index(c, findex);
128     Vertex_handle opp_vertex = c->vertex(findex);
129     facets_info.insert(std::make_pair(opp_facet1,
130                                       std::make_pair(opp_vertex, patch)));
131     facets_info.insert(std::make_pair(opp_facet2,
132                                       std::make_pair(opp_vertex, patch)));
133 
134     if(c3t3.is_in_complex(c, findex))
135       c3t3.remove_from_complex(c, findex);
136   }
137 
138   // insert midpoint
139   Vertex_handle new_v = tr.tds().insert_in_edge(e);
140   new_v->set_point(typename Tr::Point(m));
141   new_v->set_dimension(dimension);
142 
143   // update c3t3 with subdomain and surface patch indices
144   std::vector<Cell_handle> new_cells;
145   tr.incident_cells(new_v, std::back_inserter(new_cells));
146   for (Cell_handle new_cell : new_cells)
147   {
148     const Facet fi(new_cell, new_cell->index(new_v));
149     const Facet mfi = tr.mirror_facet(fi);
150 
151     //get subdomain info back
152     CGAL_assertion(cells_info.find(mfi) != cells_info.end());
153     Subdomain_index n_index = cells_info.at(mfi);
154     if (Subdomain_index() != n_index)
155       c3t3.add_to_complex(new_cell, n_index);
156     else
157       new_cell->set_subdomain_index(Subdomain_index());
158 
159     // get surface info back
160     CGAL_assertion(facets_info.find(mfi) != facets_info.end());
161     const std::pair<Vertex_handle, Surface_patch_index> v_and_opp_patch = facets_info.at(mfi);
162 
163     // facet opposite to new_v (status wrt c3t3 is unchanged)
164     new_cell->set_surface_patch_index(new_cell->index(new_v),
165                                       mfi.first->surface_patch_index(mfi.second));
166 
167     // new half-facet (added or not to c3t3 depending on the stored surface patch index)
168     if (Surface_patch_index() == v_and_opp_patch.second)
169       new_cell->set_surface_patch_index(new_cell->index(v_and_opp_patch.first),
170                                         Surface_patch_index());
171     else
172       c3t3.add_to_complex(new_cell,
173                           new_cell->index(v_and_opp_patch.first),
174                           v_and_opp_patch.second);
175 
176     // newly created internal facet
177     for (int i = 0; i < 4; ++i)
178     {
179       const Vertex_handle vi = new_cell->vertex(i);
180       if (vi == v1 || vi == v2)
181       {
182         new_cell->set_surface_patch_index(i, Surface_patch_index());
183         break;
184       }
185     }
186 
187     //the 4th facet (new_v, v_and_opp_patch.first, v1 or v2)
188     // will have its patch tagged from the other side, if needed
189   }
190 
191   set_index(new_v, c3t3);
192 
193   return new_v;
194 }
195 
196 template<typename C3T3, typename CellSelector>
can_be_split(const typename C3T3::Edge & e,const C3T3 & c3t3,const bool protect_boundaries,CellSelector cell_selector)197 bool can_be_split(const typename C3T3::Edge& e,
198                   const C3T3& c3t3,
199                   const bool protect_boundaries,
200                   CellSelector cell_selector)
201 {
202   if (is_outside(e, c3t3, cell_selector))
203     return false;
204 
205   if (protect_boundaries)
206   {
207     if (c3t3.is_in_complex(e))
208       return false;
209     else if (is_boundary(c3t3, e, cell_selector))
210       return false;
211 
212 #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG
213     if (!is_internal(e, c3t3, cell_selector))
214     {
215       std::cerr << "e is not inside!?" << std::endl;
216       typename C3T3::Vertex_handle v1 = e.first->vertex(e.second);
217       typename C3T3::Vertex_handle v2 = e.first->vertex(e.third);
218       std::cerr << v1->point() << " " << v2->point() << std::endl;
219     }
220 #endif
221 
222     CGAL_assertion(is_internal(e, c3t3, cell_selector));
223     return true;
224   }
225   else
226   {
227     return is_selected(e, c3t3, cell_selector);
228   }
229 }
230 
231 template<typename C3T3, typename CellSelector, typename Visitor>
split_long_edges(C3T3 & c3t3,const typename C3T3::Triangulation::Geom_traits::FT & high,const bool protect_boundaries,CellSelector cell_selector,Visitor & visitor)232 void split_long_edges(C3T3& c3t3,
233                       const typename C3T3::Triangulation::Geom_traits::FT& high,
234                       const bool protect_boundaries,
235                       CellSelector cell_selector,
236                       Visitor& visitor)
237 {
238   typedef typename C3T3::Triangulation       T3;
239   typedef typename T3::Cell_handle           Cell_handle;
240   typedef typename T3::Edge                  Edge;
241   typedef typename T3::Finite_edges_iterator Finite_edges_iterator;
242   typedef typename T3::Vertex_handle         Vertex_handle;
243   typedef typename std::pair<Vertex_handle, Vertex_handle> Edge_vv;
244 
245   typedef typename T3::Geom_traits     Gt;
246   typedef typename T3::Geom_traits::FT FT;
247   typedef boost::bimap<
248   boost::bimaps::set_of<Edge_vv>,
249         boost::bimaps::multiset_of<FT, std::greater<FT> > >  Boost_bimap;
250   typedef typename Boost_bimap::value_type               long_edge;
251 
252 #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE
253   std::cout << "Split long edges (" << high << ")...";
254   std::cout.flush();
255   std::size_t nb_splits = 0;
256 #endif
257   const FT sq_high = high*high;
258 
259   //collect long edges
260   T3& tr = c3t3.triangulation();
261   Boost_bimap long_edges;
262   for (Finite_edges_iterator eit = tr.finite_edges_begin();
263        eit != tr.finite_edges_end(); ++eit)
264   {
265     Edge e = *eit;
266     if (!can_be_split(e, c3t3, protect_boundaries, cell_selector))
267       continue;
268 
269     typename Gt::Compute_squared_length_3 sql
270       = tr.geom_traits().compute_squared_length_3_object();
271     FT sqlen = sql(tr.segment(e));
272     if (sqlen > sq_high)
273       long_edges.insert(long_edge(make_vertex_pair<T3>(e), sqlen));
274   }
275 
276 #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG
277   debug::dump_edges(long_edges, "long_edges.polylines.txt");
278 
279   std::ofstream ofs("midpoints.off");
280   ofs << "OFF" << std::endl;
281   ofs << long_edges.size() << " 0 0" << std::endl;
282 #endif
283   while(!long_edges.empty())
284   {
285     //the edge with longest length
286     typename Boost_bimap::right_map::iterator eit = long_edges.right.begin();
287     Edge_vv e = eit->second;
288 #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE_PROGRESS
289     const double sqlen = eit->first;
290 #endif
291     long_edges.right.erase(eit);
292 
293     Cell_handle cell;
294     int i1, i2;
295     if ( tr.tds().is_edge(e.first, e.second, cell, i1, i2))
296     {
297       Edge edge(cell, i1, i2);
298 
299       //check that splittability has not changed
300       if (!can_be_split(edge, c3t3, protect_boundaries, cell_selector))
301         continue;
302 
303       visitor.before_split(tr, edge);
304       Vertex_handle vh = split_edge(edge, c3t3);
305 
306       if(vh != Vertex_handle())
307         visitor.after_split(tr, vh);
308 
309 #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG
310       if (vh != Vertex_handle())
311         ofs << vh->point() << std::endl;
312 #endif
313 
314 #if  defined(CGAL_TETRAHEDRAL_REMESHING_VERBOSE_PROGRESS) \
315 || defined(CGAL_TETRAHEDRAL_REMESHING_VERBOSE)
316       if (vh != Vertex_handle())
317         ++nb_splits;
318 #endif
319 
320 #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE_PROGRESS
321       std::cout << "\rSplit (" << high << ")... ("
322                 << long_edges.left.size() << " long edges, "
323                 << "length  = " << std::sqrt(sqlen) << ", "
324                 << nb_splits << " splits)";
325       std::cout.flush();
326 #endif
327     }
328   }//end loop on long_edges
329 
330 #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG
331   if(ofs.is_open())
332     ofs.close();
333 #endif
334 
335 #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE
336   std::cout << " done (" << nb_splits << " splits)." << std::endl;
337 #endif
338 }
339 
340 } // internal
341 } // Tetrahedral_remeshing
342 } // CGAL
343 
344 #endif // CGAL_INTERNAL_SPLIT_LONG_EDGES_H
345