GCC Code Coverage Report


Directory: ./
File: lib/geogram/mesh/mesh_halfedges.h
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 33 41 80.5%
Functions: 11 12 91.7%
Branches: 10 36 27.8%

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_HALFEDGES
41 #define GEOGRAM_MESH_MESH_HALFEDGES
42
43 #include <geogram/basic/common.h>
44 #include <geogram/mesh/mesh.h>
45 #include <geogram/mesh/mesh_geometry.h>
46 #include <iostream>
47
48 /**
49 * \file geogram/mesh/mesh_halfedges.h
50 * \brief Classes and function for virtually seeing a mesh as a set of halfedges
51 */
52
53 namespace GEO {
54
55 /**
56 * \brief Exposes a half-edge like API for
57 * traversing a Mesh.
58 */
59 class GEOGRAM_API MeshHalfedges {
60 public:
61 /**
62 * \brief Stores a reference to a mesh corner and facet, and
63 * provides a halfedge-like API.
64 */
65 struct Halfedge {
66
67 static constexpr index_t NO_FACET = NO_INDEX;
68 static constexpr index_t NO_CORNER = NO_INDEX;
69
70 /**
71 * \brief Constructs a new uninitialized Halfedge.
72 */
73 144 Halfedge() :
74 144 facet(NO_FACET),
75 144 corner(NO_CORNER) {
76 144 }
77
78 /**
79 * \brief Constructs a new Halfedge from a facet and corner index.
80 * \param[in] f the facet index
81 * \param[in] c the corner index
82 */
83 1008 Halfedge(index_t f, index_t c) :
84 1008 facet(f),
85 1008 corner(c) {
86 1008 }
87
88 /**
89 * \brief Clears this Halfedge.
90 */
91 void clear() {
92 facet = NO_FACET;
93 corner = NO_CORNER;
94 }
95
96 /**
97 * \brief Tests whether this Halfedge is initialized.
98 * \return true if this Halfedge is uninitialized, false otherwise
99 */
100 bool is_nil() const {
101 return (facet == NO_FACET) && (corner == NO_CORNER);
102 }
103
104 /**
105 * \brief Tests whether this Halfedge is the same as another one
106 * \param[in] rhs the comparand
107 * \return true if this Halfedge and \p rhs refer to the same
108 * facet and corner, false otherwise.
109 */
110 75779 bool operator== (const Halfedge& rhs) const {
111
3/4
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 75349 times.
✓ Branch 2 taken 430 times.
✗ Branch 3 not taken.
75779 return facet == rhs.facet && corner == rhs.corner;
112 }
113
114 /**
115 * \brief Tests whether this Halfedge is different from another one
116 * \param[in] rhs the comparand
117 * \return true if this Halfedge and \p rhs refer to a different
118 * facet or corner, false otherwise.
119 */
120 75779 bool operator!= (const Halfedge& rhs) const {
121 75779 return !(rhs == *this);
122 }
123
124 index_t facet;
125 index_t corner;
126
127 };
128
129 /**
130 * \brief Creates a new MeshHalfedges
131 * \param[in] mesh the Mesh
132 */
133 32 MeshHalfedges(Mesh& mesh) : mesh_(mesh) {
134 32 }
135
136 /**
137 * \brief Gets the mesh.
138 * \return a reference to the mesh.
139 */
140 417 Mesh& mesh() {
141 417 return mesh_;
142 }
143
144 /**
145 * \brief Gets the mesh.
146 * \return a const reference to the mesh.
147 */
148 271408 const Mesh& mesh() const {
149 271408 return mesh_;
150 }
151
152 /**
153 * \brief Sets whether facet regions determine borders.
154 * \param[in] x if set, then an halfedge incident to two facets
155 * with different facet regions is considered to be a
156 * border
157 */
158 void set_use_facet_region(bool x) {
159 if(x) {
160 if(!facet_region_.is_bound()) {
161 facet_region_.bind(mesh_.facets.attributes(),"region");
162 }
163 } else {
164 if(facet_region_.is_bound()) {
165 facet_region_.unbind();
166 }
167 }
168 }
169
170 /**
171 * \brief Sets a facet attribute name that determines borders.
172 * \param[in] attribute_name the name of the facet attribute to
173 * be used to determine borders.
174 */
175 void set_use_facet_region(const std::string& attribute_name) {
176 if(facet_region_.is_bound()) {
177 facet_region_.unbind();
178 }
179 facet_region_.bind(mesh_.facets.attributes(),attribute_name);
180 }
181
182 /**
183 * \brief Sets a facet attribute name that determines borders.
184 * \param[in] attribute_name the name of the facet attribute to
185 * be used to determine borders.
186 * \details Needed to have this overload, because const char*
187 * is implicitly converted to bool instead of std::string.
188 */
189 void set_use_facet_region(const char* attribute_name) {
190 set_use_facet_region(std::string(attribute_name));
191 }
192
193
194 /**
195 * \brief Tests whether a Halfedge is valid.
196 * \param[in] H the Halfedge to be tested
197 * \return true if \p H refers to a halfedge that
198 * exists in the mesh, false otherwise
199 * \note It only tests whether H.corner and H.facet are valid
200 * indices in the mesh, but does not test whether H.corner exists
201 * in H.facet.
202 */
203 254872 bool halfedge_is_valid(const Halfedge& H) const {
204 return
205 509744 H.facet != Halfedge::NO_FACET &&
206
2/4
✓ Branch 0 taken 254872 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 254872 times.
✗ Branch 3 not taken.
509744 H.corner != Halfedge::NO_CORNER &&
207
2/4
✓ Branch 0 taken 254872 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 254872 times.
✗ Branch 4 not taken.
764616 H.facet < mesh_.facets.nb() &&
208 509744 H.corner < mesh_.facet_corners.nb()
209 ;
210 }
211
212 /**
213 * \brief Tests whether a Halfedge is on the boder.
214 * \details If set_use_facet_region() is set, then
215 * Halfedges incident to two different facet regions are
216 * considered as borders.
217 * \param[in] H the Halfedge
218 * \return true if \p H is on the border, false otherwise
219 */
220 2256 bool halfedge_is_border(const Halfedge& H) const {
221
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 2256 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2256 geo_debug_assert(halfedge_is_valid(H));
222
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2256 times.
2256 if(facet_region_.is_bound()) {
223 index_t f = H.facet;
224 index_t adj_f =
225 mesh_.facet_corners.adjacent_facet(H.corner);
226 return
227 adj_f == NO_FACET ||
228 facet_region_[f] != facet_region_[adj_f]
229 ;
230 }
231 2256 return mesh_.facet_corners.adjacent_facet(H.corner) == NO_FACET;
232 }
233
234 /**
235 * \brief Replaces a Halfedge with the next one around the facet.
236 * \param[in,out] H the Halfedge
237 */
238 2256 void move_to_next_around_facet(Halfedge& H) const {
239
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 2256 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2256 geo_debug_assert(halfedge_is_valid(H));
240 2256 H.corner = mesh_.facets.next_corner_around_facet(H.facet, H.corner);
241 2256 }
242
243 /**
244 * \brief Replaces a Halfedge with the previous one around the facet.
245 * \param[in,out] H the Halfedge
246 */
247 void move_to_prev_around_facet(Halfedge& H) const {
248 geo_debug_assert(halfedge_is_valid(H));
249 H.corner = mesh_.facets.prev_corner_around_facet(H.facet, H.corner);
250 }
251
252 /**
253 * \brief Replaces a Halfedge with the next one around the vertex.
254 * \param[in,out] H the Halfedge
255 * \return true if the move was successful, false otherwise. On borders,
256 * the next halfedge around a vertex may not exist.
257 */
258 bool move_to_next_around_vertex(Halfedge& H) const;
259
260 /**
261 * \brief Replaces a Halfedge with the previous one around the vertex.
262 * \param[in,out] H the Halfedge
263 * \return true if the move was successful, false otherwise. On borders,
264 * the previous halfedge around a vertex may not exist.
265 */
266 bool move_to_prev_around_vertex(Halfedge& H) const;
267
268 /**
269 * \brief Replaces a Halfedge with the next one around the border.
270 * \details If set_use_facet_region() is set, then
271 * Halfedges incident to two different facet regions are
272 * considered as borders.
273 * \param[in,out] H the Halfedge
274 */
275 void move_to_next_around_border(Halfedge& H) const;
276
277 /**
278 * \brief Replaces a Halfedge with the previous one around the border.
279 * \details If set_use_facet_region() is set, then
280 * Halfedges incident to two different facet regions are
281 * considered as borders.
282 * \param[in,out] H the Halfedge
283 */
284 void move_to_prev_around_border(Halfedge& H) const;
285
286 /**
287 * \brief Replaces a Halfedge with the opposite one in the
288 * adjacent facet.
289 * \param[in,out] H the Halfedge
290 * \pre !is_on_border(H)
291 */
292 void move_to_opposite(Halfedge& H) const;
293
294 private:
295 Mesh& mesh_;
296 Attribute<index_t> facet_region_;
297 };
298
299 /**
300 * \brief Displays a Halfedge.
301 * \param[out] out the stream where to print the Halfedge
302 * \param[in] H the Halfedge
303 * \return a reference to the stream \p out
304 */
305 inline std::ostream& operator<< (
306 std::ostream& out, const MeshHalfedges::Halfedge& H
307 ) {
308 return out << '(' << H.facet << ',' << H.corner << ')';
309 }
310
311 namespace Geom {
312
313 /**
314 * \brief Gets the origin point of a Halfedge
315 * \param[in] M the mesh
316 * \param[in] H the Halfedge
317 * \return a const reference to the origin of \p H
318 */
319 102006 inline const vec3& halfedge_vertex_from(
320 const Mesh& M, const MeshHalfedges::Halfedge& H
321 ) {
322 102006 return M.facet_corners.point(H.corner);
323 }
324
325 /**
326 * \brief Gets the arrow extremity point of a Halfedge
327 * \param[in] M the mesh
328 * \param[in] H the Halfedge
329 * \return a const reference to the arrow extremity of \p H
330 */
331 inline const vec3& halfedge_vertex_to(
332 const Mesh& M, const MeshHalfedges::Halfedge& H
333 ) {
334 index_t c = M.facets.next_corner_around_facet(H.facet, H.corner);
335 return M.facet_corners.point(c);
336 }
337
338 /**
339 * \brief Gets a 3d vector that connects the origin with the arrow
340 * extremity of a Halfedge.
341 * \param[in] M the Mesh
342 * \param[in] H the Halfedge
343 * \return a 3d vector that connects the origin with the arrow
344 * extremity of \p H
345 */
346 inline vec3 halfedge_vector(
347 const Mesh& M, const MeshHalfedges::Halfedge& H
348 ) {
349 return halfedge_vertex_to(M, H) - halfedge_vertex_from(M, H);
350 }
351
352 /**
353 * \brief Gets the length of a Halfedge
354 * \param[in] M the Mesh
355 * \param[in] H the Halfedge
356 * \return the 3d length of \p H
357 */
358 inline double edge_length(
359 const Mesh& M, const MeshHalfedges::Halfedge& H
360 ) {
361 return length(halfedge_vector(M, H));
362 }
363 }
364 }
365
366 #endif
367