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