GCC Code Coverage Report


Directory: ./
File: lib/geogram/mesh/mesh_surface_intersection_internal.h
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 95 107 88.8%
Functions: 13 15 86.7%
Branches: 51 92 55.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * 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 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_MESH_MESH_SURFACE_INTERSECTION_INTERNAL
41 #define GEOGRAM_MESH_MESH_SURFACE_INTERSECTION_INTERNAL
42
43 /**
44 * \file mesh_surface_intersection_internal.h
45 * \brief Classes used by MeshSurfaceIntersection
46 */
47
48 #include <geogram/basic/common.h>
49 #include <geogram/mesh/mesh_surface_intersection.h>
50 #include <geogram/mesh/mesh.h>
51 #include <geogram/mesh/mesh_io.h>
52 #include <geogram/mesh/index.h>
53 #include <geogram/mesh/triangle_intersection.h>
54 #include <geogram/delaunay/CDT_2d.h>
55 #include <geogram/numerics/exact_geometry.h>
56
57 #include <map>
58
59 namespace GEO {
60
61 /**
62 * \brief Meshes a single triangle with the constraints that come from
63 * the intersections with the other triangles.
64 * \details Inherits CDTBase2d (constrained Delaunay triangulation), and
65 * redefines orient2d(), incircle2d() and create_intersection() using
66 * vectors with homogeneous coordinates stored as arithmetic expansions
67 * (vec2HE) or arbitrary-precision floating point numbers (vec2HEx) if
68 * compiled with Tessael's geogramplus extension package.
69 */
70 class MeshInTriangle : public CDTBase2d {
71 public:
72
73 typedef exact::vec3h ExactPoint;
74
75 /***************************************************************/
76
77 /**
78 * \brief An edge of the mesh.
79 * \details It represents the constraints to be used by the
80 * constrained triangulation to remesh the facet. Makes a maximum
81 * use of the combinatorial information to reduce the complexity
82 * (degree) of the constructed coordinates as much as possible.
83 */
84 class Edge {
85 public:
86 Edge(
87 index_t v1_in = NO_INDEX,
88 index_t v2_in = NO_INDEX,
89 index_t f2 = NO_INDEX,
90 TriangleRegion R2 = T2_RGN_T
91 124645 ) : v1(v1_in),
92 124645 v2(v2_in) {
93 124645 sym.f2 = f2;
94 93635 sym.R2 = R2;
95 }
96 index_t v1,v2; // The two extremities of the edge
97 struct { // Symbolic information: this edge = f1 /\ f2.R2
98 index_t f2;
99 TriangleRegion R2;
100 } sym;
101 };
102
103 /***************************************************************/
104
105 /**
106 * \brief A vertex of the triangulation
107 * \details Stores geometric information in exact precision, both
108 * in 3D and in local 2D coordinates. It also stores symbolic
109 * information, that is, facet indices and regions that generated
110 * the vertex.
111 */
112
2/4
✗ Branch 9 not taken.
✓ Branch 10 taken 5684 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 240137 times.
890024 class Vertex {
113 public:
114
115 enum Type {
116 UNINITIALIZED, MESH_VERTEX, PRIMARY_ISECT, SECONDARY_ISECT
117 };
118
119 /**
120 * \brief Constructor for macro-triangle vertices.
121 * \param[in] f facet index, supposed to correspond to
122 * MeshInTriangle's current facet
123 * \param[in] lv local vertex index in \p f
124 */
125
1/2
✓ Branch 1 taken 46515 times.
✗ Branch 2 not taken.
46515 Vertex(MeshInTriangle* M, index_t f, index_t lv) {
126 geo_debug_assert(f == M->f1_);
127 46515 type = MESH_VERTEX;
128 46515 mit = M;
129 init_sym(f, NO_INDEX, TriangleRegion(lv), T2_RGN_T);
130
1/2
✓ Branch 1 taken 46515 times.
✗ Branch 2 not taken.
46515 init_geometry(compute_geometry());
131 46515 }
132
133 /**
134 * \brief Constructor for intersections with other facets.
135 * \param[in] f1 , f2 the two facets. \p f1 is suposed to
136 * correspond to MeshInTriangle's current facet
137 * \param[in] R1 , R2 the two facet regions.
138 */
139 240137 Vertex(
140 MeshInTriangle* M,
141 index_t f1, index_t f2,
142 TriangleRegion R1, TriangleRegion R2
143
1/2
✓ Branch 1 taken 240137 times.
✗ Branch 2 not taken.
240137 ) {
144 geo_debug_assert(f1 == M->f1_);
145 240137 type = PRIMARY_ISECT;
146 240137 mit = M;
147 init_sym(f1,f2,R1,R2);
148
1/2
✓ Branch 1 taken 240137 times.
✗ Branch 2 not taken.
240137 init_geometry(compute_geometry());
149 240137 }
150
151 /**
152 * \brief Constructor for intersections between constraints.
153 * \param[in] point_exact_in exact 3D coordinates
154 * of the intersection
155 */
156 5684 Vertex(
157 MeshInTriangle* M, const ExactPoint& point_exact_in
158
1/2
✓ Branch 1 taken 5684 times.
✗ Branch 2 not taken.
5684 ) {
159 5684 type = SECONDARY_ISECT;
160 5684 mit = M;
161 init_sym(NO_INDEX, NO_INDEX, T1_RGN_T, T2_RGN_T);
162 5684 init_geometry(point_exact_in);
163 5684 }
164
165 /**
166 * \brief Default constructor
167 */
168 Vertex() {
169 type = UNINITIALIZED;
170 mit = nullptr;
171 init_sym(NO_INDEX, NO_INDEX, T1_RGN_T, T2_RGN_T);
172 mesh_vertex_index = NO_INDEX;
173 }
174
175 /**
176 * \brief Gets the mesh
177 * \return a reference to the mesh
178 */
179 const Mesh& mesh() const {
180 68170 return mit->mesh();
181 }
182
183 /**
184 * \brief Prints this vertex
185 * \details Displays the combinatorial information
186 * \param[out] out an optional stream where to print
187 */
188 void print(std::ostream& out=std::cerr) const;
189
190 /**
191 * \brief Gets a string representation of this Vertex
192 * \return a string with the combinatorial information
193 * of this Vertex
194 */
195 std::string to_string() const {
196 std::ostringstream out;
197 print(out);
198 return out.str();
199 }
200
201 vec2 get_UV_approx() const {
202 double u = point_exact[mit->u_].estimate();
203 double v = point_exact[mit->v_].estimate();
204 double w = point_exact.w.estimate();
205 return vec2(u/w,v/w);
206 }
207
208 protected:
209
210 /**
211 * \brief Initializes the symbolic information of this Vertex
212 * \param[in] f1 , f2 the two facets. \p f1 is suposed to
213 * correspond to MeshInTriangle's current facet
214 * \param[in] R1 , R2 the two facet regions.
215 */
216 void init_sym(
217 index_t f1, index_t f2, TriangleRegion R1, TriangleRegion R2
218 ) {
219 292336 sym.f1 = f1;
220 292336 sym.f2 = f2;
221 292336 sym.R1 = R1;
222 292336 sym.R2 = R2;
223
3/6
✓ Branch 1 taken 5684 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 240137 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 46515 times.
✗ Branch 8 not taken.
292336 mesh_vertex_index = NO_INDEX;
224 }
225
226 /**
227 * \brief Gets the geometry of this vertex
228 * \details Computes the exact 3D position of this vertex
229 * based on the mesh and the combinatorial information
230 */
231 ExactPoint compute_geometry();
232
233 /**
234 * \brief Optimizes exact numbers in generated
235 * points and computes approximate coordinates.
236 */
237 void init_geometry(const ExactPoint& P);
238
239 public:
240 MeshInTriangle* mit;
241 ExactPoint point_exact; // Exact homogeneous coords using expansions
242 Type type; // MESH_VERTEX, PRIMARY_ISECT or SECONDARY_ISECT
243 index_t mesh_vertex_index; // Global mesh vertex index once created
244 struct { // Symbolic information - tri-tri isect
245 index_t f1,f2; // global facet indices in mesh
246 TriangleRegion R1,R2; // triangle regions
247 } sym;
248 #ifndef GEOGRAM_USE_EXACT_NT
249 double l; // precomputed approximated (p[u]^2 + p[v]^2) / p.w^2
250 #endif
251 };
252
253 /***************************************************************/
254
255 MeshInTriangle(MeshSurfaceIntersection& EM);
256
257 /**
258 * \brief Gets the readonly initial mesh
259 * \return a const reference to a copy of the initial mesh
260 */
261 const Mesh& mesh() const {
262
2/4
✓ Branch 0 taken 46515 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21655 times.
✗ Branch 3 not taken.
68170 return mesh_;
263 }
264
265 /**
266 * \brief Gets the target mesh
267 * \return a reference to the target mesh
268 */
269 Mesh& target_mesh() {
270 131387 return exact_mesh_.target_mesh();
271 }
272
273 /**
274 * \brief In dry run mode, the computed local triangulations
275 * are not inserted in the global mesh. This is for benchmarking.
276 * Default is off.
277 */
278 void set_dry_run(bool x) {
279
1/2
✓ Branch 1 taken 229 times.
✗ Branch 2 not taken.
229 dry_run_ = x;
280 }
281
282 /**
283 * \brief For debugging, save constraints to a file
284 * \param[in] filename a mesh filename where to solve the constraints
285 * (.obj or .geogram)
286 */
287 void save_constraints(const std::string& filename) {
288 Mesh M;
289 get_constraints(M);
290 mesh_save(M,filename);
291 }
292
293 void begin_facet(index_t f);
294
295 index_t add_vertex(index_t f2, TriangleRegion R1, TriangleRegion R2);
296
297 void add_edge(
298 index_t f2,
299 TriangleRegion AR1, TriangleRegion AR2,
300 TriangleRegion BR1, TriangleRegion BR2
301 );
302
303 /**
304 * \brief Creates new vertices and new triangles in target mesh
305 */
306 void commit();
307
308
309 /**
310 * \see CDT2d::clear()
311 */
312 void clear() override;
313
314 protected:
315 /**
316 * \brief For debugging, copies the constraints to a mesh
317 */
318 void get_constraints(Mesh& M, bool with_edges=true) const;
319
320 vec3 mesh_vertex(index_t v) const {
321 return vec3(mesh().vertices.point_ptr(v));
322 }
323
324
1/2
✓ Branch 0 taken 1500857 times.
✗ Branch 1 not taken.
1500857 vec3 mesh_facet_vertex(index_t f, index_t lv) const {
325 index_t v = mesh().facets.vertex(f,lv);
326 1500857 return mesh_vertex(v);
327 }
328
329 vec2 mesh_vertex_UV(index_t v) const {
330 const double* p = mesh().vertices.point_ptr(v);
331 620972 return vec2(p[u_], p[v_]);
332 }
333
334
1/2
✓ Branch 0 taken 620972 times.
✗ Branch 1 not taken.
620972 vec2 mesh_facet_vertex_UV(index_t f, index_t lv) const {
335 index_t v = mesh().facets.vertex(f,lv);
336 620972 return mesh_vertex_UV(v);
337 }
338
339
340 void log_err() const {
341 std::cerr << "Houston, we got a problem (while remeshing facet "
342 << f1_ << "):" << std::endl;
343 }
344
345 protected:
346
347 /********************** CDTBase2d overrides ***********************/
348
349 /**
350 * \brief Tests the orientation of three vertices
351 * \param[in] v1 , v2 , v3 the three vertices
352 * \retval POSITIVE if they are in the trigonometric order
353 * \retval ZERO if they are aligned
354 * \retval NEGATIVE if they are in the anti-trigonometric order
355 */
356 Sign orient2d(index_t v1,index_t v2,index_t v3) const override;
357
358 /**
359 * \brief Tests the relative position of a point with respect
360 * to the circumscribed circle of a triangle
361 * \param[in] v1 , v2 , v3 the three vertices of the triangle
362 * oriented anticlockwise
363 * \param[in] v4 the point to be tested
364 * \retval POSITIVE if the point is inside the circle
365 * \retval ZERO if the point is on the circle
366 * \retval NEGATIVE if the point is outside the circle
367 */
368 Sign incircle(
369 index_t v1,index_t v2,index_t v3,index_t v4
370 ) const override;
371
372 /**
373 * \brief Given two segments that have an intersection, create the
374 * intersection
375 * \details The intersection is given both as the indices of segment
376 * extremities (i,j) and (k,l), that one can use to retreive the
377 * points in derived classes, and constraint indices E1 and E2, that
378 * derived classes may use to retreive symbolic information attached
379 * to the constraint
380 * \param[in] e1 the index of the first edge, corresponding to the
381 * value of ncnstr() when insert_constraint() was called for
382 * that edge
383 * \param[in] i , j the vertices of the first segment
384 * \param[in] e2 the index of the second edge, corresponding to the
385 * value of ncnstr() when insert_constraint() was called for
386 * that edge
387 * \param[in] k , l the vertices of the second segment
388 * \return the index of a newly created vertex that corresponds to
389 * the intersection between [\p i , \p j] and [\p k , \p l]
390 */
391 index_t create_intersection(
392 index_t e1, index_t i, index_t j,
393 index_t e2, index_t k, index_t l
394 ) override;
395
396 /**
397 * \brief Computes the intersection between two edges
398 * \param[in] e1 , e2 the two edges
399 * \param[out] I the intersection
400 */
401 void get_edge_edge_intersection(
402 index_t e1, index_t e2, ExactPoint& I
403 ) const;
404
405 /**
406 * \brief Auxilliary function used by get_edge_edge_intersection()
407 * for the special case when the two edges are coplanar
408 * \param[in] e1 , e2 the two edges
409 * \param[out] I the intersection
410 */
411 void get_edge_edge_intersection_2D(
412 index_t e1, index_t e2, ExactPoint& I
413 ) const;
414
415 public:
416 void save(const std::string& filename) const override;
417
418 protected:
419 void begin_insert_transaction() override;
420 void commit_insert_transaction() override;
421 void rollback_insert_transaction() override;
422
423 private:
424 MeshSurfaceIntersection& exact_mesh_;
425 const Mesh& mesh_;
426 index_t f1_;
427 index_t latest_f2_;
428 index_t latest_f2_count_;
429 coord_index_t f1_normal_axis_;
430 coord_index_t u_; // = (f1_normal_axis_ + 1)%3
431 coord_index_t v_; // = (f1_normal_axis_ + 2)%3
432 vector<Vertex> vertex_;
433 vector<Edge> edges_;
434 bool has_planar_isect_;
435 bool dry_run_;
436 mutable std::map<trindex, Sign> pred_cache_;
437 bool use_pred_cache_insert_buffer_;
438 mutable std::vector< std::pair<trindex, Sign> >
439 pred_cache_insert_buffer_;
440 };
441
442 /*************************************************************************/
443
444 /**
445 * \brief Stores information about a triangle-triangle intersection.
446 * \details The intersection is a segment A-B. Its extremities A and B
447 * are indicated by the regions in f1 and f2 that created the
448 * intersection. If the intersection is just a point,
449 * then A and B regions are the same.
450 */
451 struct IsectInfo {
452 public:
453
454 /**
455 * Swaps the two facets and updates the combinatorial
456 * information accordingly.
457 */
458 67882 void flip() {
459 std::swap(f1,f2);
460 67882 A_rgn_f1 = swap_T1_T2(A_rgn_f1);
461 67882 A_rgn_f2 = swap_T1_T2(A_rgn_f2);
462 std::swap(A_rgn_f1, A_rgn_f2);
463 67882 B_rgn_f1 = swap_T1_T2(B_rgn_f1);
464 67882 B_rgn_f2 = swap_T1_T2(B_rgn_f2);
465 std::swap(B_rgn_f1, B_rgn_f2);
466 67882 }
467
468 /**
469 * \brief Tests whether intersection is just a point.
470 * \details Points are encoded as segments with the
471 * same symbolic information for both vertices.
472 */
473 bool is_point() const {
474 return
475
2/2
✓ Branch 0 taken 54380 times.
✓ Branch 1 taken 81384 times.
135764 A_rgn_f1 == B_rgn_f1 &&
476
2/2
✓ Branch 0 taken 8606 times.
✓ Branch 1 taken 45774 times.
54380 A_rgn_f2 == B_rgn_f2 ;
477 }
478
479 index_t f1;
480 index_t f2;
481 TriangleRegion A_rgn_f1;
482 TriangleRegion A_rgn_f2;
483 TriangleRegion B_rgn_f1;
484 TriangleRegion B_rgn_f2;
485 };
486
487 /**********************************************************************/
488
489 /**
490 * \brief Detects and retriangulates a set of coplanar facets for
491 * MeshSurfaceIntersection.
492 */
493 class CoplanarFacets {
494 public:
495 static constexpr index_t NON_MANIFOLD = index_t(-2);
496 typedef MeshSurfaceIntersection::ExactPoint ExactPoint;
497
498 /**
499 * \brief Constructs a CoplanarFacets object associated with a
500 * MeshSurfaceIntersection
501 * \details No set of facets is identified. One needs to call get().
502 * \param[in] I a reference to the MeshSurfaceIntersection
503 * \param[in] clear_attributes if set, resets facet_chart and
504 * keep_vertex
505 * \param[in] angle_tolerance angle tolerance for detecting coplanar
506 * facets and colinear edges (in degrees)
507 */
508 CoplanarFacets(
509 MeshSurfaceIntersection& I, bool clear_attributes,
510 double angle_tolerance = 0.0
511 );
512
513 /**
514 * \brief Gets the set of coplanar facets from a given facet and
515 * group id.
516 * \details Uses the "group" facets attribute. If \p f's group is
517 * uninitialized (NO_INDEX), determines the facets of the group
518 * geometrically and initializes the attribute, else gets the
519 * facets based on the attribute.
520 * \param[in] f the facet
521 * \param[in] group_id the facet group id
522 */
523 void get(index_t f, index_t group_id);
524
525 /**
526 * \brief Marks the vertices that need to be kept in the
527 * simplified facets.
528 * \details A vertex is kept if it is incident to at least
529 * two non-colinear
530 * edges on the border. The status of the vertices is stored in the
531 * "keep" vertex attribute.
532 */
533 void mark_vertices_to_keep();
534
535 /**
536 * \brief For debugging purposes, saves border edges to a file.
537 * \param[in] filename the file where to store the borders.
538 */
539 void save_borders(const std::string& filename);
540
541 /**
542 * \brief For debugging purposes, saves all the facets of the group
543 * to a file.
544 * \param[in] filename the file where to store the facets of the group.
545 */
546 void save_facet_group(const std::string& filename);
547
548 /**
549 * \brief Triangulates the kept vertices.
550 * \details One can get the triangle through the (public) CDT member
551 * (ExactCDT2d).
552 */
553 void triangulate();
554
555 protected:
556
557 /**
558 * \brief Finds all the pairs of coplanar facets
559 * \details Initializes c_is_coplanar_[], a vector of booleans indexed
560 * by facet corners.
561 */
562 void find_coplanar_facets();
563
564 /**
565 * \brief Tests whether two triangles are coplanar
566 * \details This is used to determine the facets that can be
567 * merged
568 * \param[in] p1 , p2 , p3 the vertices of the first triangle
569 * \param[in] q1 , q2 , q3 the vertices of the second triangle
570 * \retval true if the two triangles are coplanar
571 * \retval false otherwise
572 * \details uses angle_tolerance specified to the constructor (if set
573 * to zero, uses exact computation)
574 */
575 bool triangles_are_coplanar(
576 const vec3& p1, const vec3& p2, const vec3& p3,
577 const vec3& q1, const vec3& q2, const vec3& q3
578 ) const;
579
580
581 /**
582 * \brief Tests whether two edges are co-linear
583 * \param[in] P1 , P2 , P3 the vertices of the two edges
584 * \retval true if [P1,P2] and [P2,P3] are co-linear, and P2 is between
585 * P1 and p3
586 * \retval false otherwise
587 * \details uses angle_tolerance specified to the constructor (if set
588 * to zero, uses exact computation)
589 */
590 bool edges_are_colinear(
591 const ExactPoint& P1, const ExactPoint& P2, const ExactPoint& P3
592 ) const;
593
594
595
596 public:
597 ExactCDT2d CDT;
598
599 /**
600 * \brief Gets the number of coplanar facets
601 * \return the number of coplanar facets present in the mesh
602 */
603 index_t nb_facets() {
604 return facets_.size();
605 }
606
607 /**
608 * \brief Marks the facets
609 * \param[out] facet_is_marked on exit, set to 1 for facets present
610 * in the list of coplanar facets. Needs to be of size
611 * mesh_.facets.nb().
612 */
613 void mark_facets(vector<index_t>& facet_is_marked) {
614
2/2
✓ Branch 0 taken 58472 times.
✓ Branch 1 taken 8067 times.
66539 for(index_t f: facets_) {
615 58472 facet_is_marked[f] = 1;
616 }
617 }
618
619 private:
620 MeshSurfaceIntersection& I_;
621 Mesh& mesh_;
622 const Mesh& mesh_copy_;
623 double angle_tolerance_;
624 index_t group_id_;
625 Attribute<index_t> facet_group_;
626 Attribute<bool> keep_vertex_;
627 Attribute<bool> c_is_coplanar_;
628 Attribute<bool> f_is_flipped_;
629 vector<bool> f_visited_;
630 vector<bool> h_visited_;
631 vector<bool> v_visited_;
632 vector<index_t> v_idx_;
633 coord_index_t u_;
634 coord_index_t v_;
635
636 /***********************************************************/
637
638 vector<index_t> vertices_;
639 vector<index_t> facets_;
640
641 /**
642 * \brief A 2d Incident Edge Lists data structure
643 */
644 class Halfedges {
645 public:
646
647 /**
648 * \brief Halfedges constructor
649 * \param[in] coplanar_facets a reference to the CoplanarFacets
650 */
651 Halfedges(
652 CoplanarFacets& coplanar_facets
653 285 ) : mesh_(coplanar_facets.mesh_) {
654 }
655
656 /**
657 * \brief Initializes this Halfedges
658 * \details Clears the list of halfedges and incident edge lists
659 */
660 146174 void initialize() {
661 // Use resize rather than assign so that we do not traverse
662 // all the halfedges of the mesh_
663 146174 v_first_halfedge_.resize(mesh_.vertices.nb(), NO_INDEX);
664 146174 h_next_around_v_.resize(mesh_.facet_corners.nb(), NO_INDEX);
665 // We only need to reset the halfedges of this set of coplanar
666 // facets.
667
2/2
✓ Branch 0 taken 491795 times.
✓ Branch 1 taken 146174 times.
637969 for(index_t h: halfedges_) {
668 491795 v_first_halfedge_[vertex(h,0)] = NO_INDEX;
669 491795 h_next_around_v_[h] = NO_INDEX;
670 }
671 146174 halfedges_.resize(0);
672 146174 }
673
674 /**
675 * \brief Gets a vertex of a halfedge
676 * \param[in] h the halfedge
677 * \param[in] dlv 0 for origin, 1 for destination, 2 for opposite
678 * \return the vertex
679 */
680 index_t vertex(index_t h, index_t dlv) const {
681 2168703 index_t f = h/3;
682
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 246918 times.
✗ Branch 5 not taken.
246918 index_t lv = (h+dlv)%3;
683
3/8
✓ Branch 0 taken 51858 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 246918 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 491795 times.
✗ Branch 7 not taken.
790571 return mesh_.facets.vertex(f,lv);
684 }
685
686 /**
687 * \brief Gets the incident facet
688 * \param[in] h a halfedge
689 * \return the facet incident to the halfedge
690 */
691 index_t facet(index_t h) const {
692 return h/3;
693 }
694
695 /**
696 * \brief Gets the opposite halfedge
697 * \param[in] h a halfedge
698 * \return the halfedge opposite to \p h, or NO_INDEX if there
699 * is no such halfedge
700 */
701 index_t alpha2(index_t h) const {
702 index_t t1 = facet(h);
703 index_t t2 = mesh_.facet_corners.adjacent_facet(h);
704 if(t2 == NO_INDEX) {
705 return NO_INDEX;
706 }
707 for(index_t h2: mesh_.facets.corners(t2)) {
708 if(mesh_.facet_corners.adjacent_facet(h2) == t1) {
709 return h2;
710 }
711 }
712 geo_assert_not_reached;
713 }
714
715 /**
716 * \brief Adds a halfedge
717 * \param[in] h the halfedge
718 */
719 493836 void add(index_t h) {
720
2/2
✓ Branch 0 taken 492394 times.
✓ Branch 1 taken 1442 times.
493836 halfedges_.push_back(h);
721
1/2
✓ Branch 0 taken 493836 times.
✗ Branch 1 not taken.
493836 index_t v1 = vertex(h,0);
722 493836 h_next_around_v_[h] = v_first_halfedge_[v1];
723 493836 v_first_halfedge_[v1] = h;
724 493836 }
725
726 /**
727 * \brief used by range-based for
728 * \return an iterator to the first halfedge
729 */
730 vector<index_t>::const_iterator begin() const {
731 return halfedges_.begin();
732 }
733
734 /**
735 * \brief used by range-based for
736 * \return an iterator to one position past the last halfedge
737 */
738 vector<index_t>::const_iterator end() const {
739 return halfedges_.end();
740 }
741
742 /**
743 * \brief Gets the first halfedge starting from a vertex
744 * \param[in] v the vertex
745 * \return the first halfedge starting from \p v
746 */
747 index_t vertex_first_halfedge(index_t v) const {
748 987668 return v_first_halfedge_[v];
749 }
750
751 /**
752 * \brief Gets the next halfedge in the incident edge list
753 * \param[in] h a halfedge
754 * \return the next halfedge around the origin of \p h or
755 * NO_INDEX if there is no such halfedge
756 */
757 index_t next_around_vertex(index_t h) const {
758 987688 return h_next_around_v_[h];
759 }
760
761 /**
762 * \brief Gets the number of halfedges around a vertex
763 * \partam[in] v a vertex
764 * \return the number of halfedges starting from \p v
765 */
766 index_t nb_halfedges_around_vertex(index_t v) const {
767 index_t result = 0;
768 987668 for(
769 index_t h = vertex_first_halfedge(v);
770
4/4
✓ Branch 0 taken 493836 times.
✓ Branch 1 taken 493832 times.
✓ Branch 2 taken 493844 times.
✓ Branch 3 taken 493836 times.
1975348 h != NO_INDEX;
771 h = next_around_vertex(h)
772 ) {
773 987680 ++result;
774 }
775 return result;
776 }
777
778 /**
779 * \brief Gets the next halfedge along a polyline
780 * \param[in] h a halfedge
781 * \return the halfedge on the same polyline as \p h starting
782 * from \p h destination or NO_INDEX if there is no such
783 * halfedge. Polyline stops where it encounters a vertex that does
784 * not have exactly 1 incident halfedge, that is,
785 * where the halfedges graph is non-manifold.
786 */
787
1/2
✓ Branch 0 taken 493836 times.
✗ Branch 1 not taken.
493836 index_t next_along_polyline(index_t h) const {
788 index_t v2 = vertex(h,1);
789
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 493828 times.
493836 if(nb_halfedges_around_vertex(v2) != 1) {
790 8 return NO_INDEX;
791 }
792 return vertex_first_halfedge(v2);
793 }
794
795 private:
796 Mesh& mesh_;
797 vector<index_t> halfedges_;
798 vector<index_t> v_first_halfedge_;
799 vector<index_t> h_next_around_v_;
800 } halfedges_;
801
802
803 /**
804 * \brief Organizes halfedges as a set of chains starting and ending
805 * at non-manifold vertices
806 */
807 class Polylines {
808 public:
809
810 /**
811 * \brief Polylines constructor
812 * \param[in] CF a reference to the CoplanarFacets
813 */
814
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 228 times.
285 Polylines(CoplanarFacets& CF) : CF_(CF) {
815 }
816
817 /**
818 * \brief Initializes this Polylines
819 * \details Resets all the stored polylines
820 */
821 146174 void initialize() {
822 146174 H_.resize(0);
823 146174 polyline_start_.resize(0);
824 146174 polyline_start_.push_back(0);
825 146174 }
826
827 /**
828 * \brief Gets the number of polylines
829 * \return the number of polylines
830 */
831 index_t nb() const {
832 81154 return polyline_start_.size() - 1;
833 }
834
835 /**
836 * \brief used by range-based for
837 * \return a non-iterator corresponding to the first polyline index.
838 */
839 index_as_iterator begin() const {
840 return index_as_iterator(0);
841 }
842
843 /**
844 * \brief used by range-based for
845 * \return a non-iterator to one position past the
846 * last polyline index.
847 */
848 index_as_iterator end() const {
849 81154 return index_as_iterator(nb());
850 }
851
852 /**
853 * \brief Gets the halfedges in a polyline
854 * \param[in] polyline the polyline index
855 * \return an iteratable sequence of halfedges
856 */
857 const_index_ptr_range halfedges(index_t polyline) const {
858 geo_debug_assert(polyline < nb());
859 return const_index_ptr_range(
860 81360 H_, polyline_start_[polyline], polyline_start_[polyline+1]
861 81360 );
862 }
863
864 /**
865 * \brief Creates a new polyline
866 */
867 void begin_polyline() {
868 }
869
870 /**
871 * \brief Finishes a polyline creation
872 */
873 void end_polyline() {
874
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 146372 times.
146380 polyline_start_.push_back(H_.size());
875 146380 }
876
877 /**
878 * \brief Adds a halfedge to the current polyline
879 * \details Needs to be called between begin_polyline() and
880 * end_polyline()
881 * \param[in] h the halfedge to be added to the current polyline
882 */
883 void add_halfedge(index_t h) {
884
3/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 492318 times.
✓ Branch 3 taken 1442 times.
493836 H_.push_back(h);
885 }
886
887 /**
888 * \brief Gets the first vertex of a polyline
889 * \param[in] polyline a polyline index
890 * \return the index of the first vertex of \p polyline
891 */
892
1/2
✓ Branch 0 taken 162720 times.
✗ Branch 1 not taken.
162720 index_t first_vertex(index_t polyline) const {
893 162720 index_t h = H_[polyline_start_[polyline]];
894
1/2
✓ Branch 0 taken 162720 times.
✗ Branch 1 not taken.
162720 return CF_.halfedges_.vertex(h,0);
895 }
896
897 /**
898 * \brief Gets the last vertex of a polyline
899 * \details if the polyline is closed, last_vertex() is the same as
900 * first_vertex()
901 * \param[in] polyline a polyline index
902 * \return the index of the first vertex of \p polyline
903 */
904 154550 index_t last_vertex(index_t polyline) const {
905
1/2
✓ Branch 0 taken 154550 times.
✗ Branch 1 not taken.
154550 index_t h = H_[polyline_start_[polyline+1]-1];
906
1/2
✓ Branch 0 taken 154550 times.
✗ Branch 1 not taken.
154550 return CF_.halfedges_.vertex(h,1);
907 }
908
909 /**
910 * \brief Gets the predecessor of the first vertex
911 * \param[in] polyline a polyline
912 * \return if the polyline is closed, the predecessor
913 * of the first vertex, otherwise NO_INDEX
914 */
915 73190 index_t prev_first_vertex(index_t polyline) const {
916
1/2
✓ Branch 0 taken 73190 times.
✗ Branch 1 not taken.
73190 if(first_vertex(polyline) != last_vertex(polyline)) {
917 return NO_INDEX;
918 }
919
1/2
✓ Branch 0 taken 73190 times.
✗ Branch 1 not taken.
73190 index_t h = H_[polyline_start_[polyline+1]-1];
920
1/2
✓ Branch 0 taken 73190 times.
✗ Branch 1 not taken.
146380 return CF_.halfedges_.vertex(h,0);
921 }
922
923 private:
924 CoplanarFacets& CF_;
925 vector<index_t> H_;
926 vector<index_t> polyline_start_;
927 } polylines_;
928
929 };
930
931 /**********************************************************************/
932
933 }
934
935 #endif
936