GCC Code Coverage Report


Directory: ./
File: tests/test_mesh_syntaxic_sugar/main.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 87 99 87.9%
Functions: 6 6 100.0%
Branches: 98 194 50.5%

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
41 #include <geogram/basic/common.h>
42 #include <geogram/basic/logger.h>
43 #include <geogram/basic/command_line.h>
44 #include <geogram/basic/command_line_args.h>
45 #include <geogram/mesh/mesh.h>
46 #include <geogram/mesh/mesh_io.h>
47 #include <geogram/mesh/mesh_subdivision.h>
48 #include <geogram/basic/stopwatch.h>
49 #include <stack>
50
51 // Tests "syntaxic sugar" for mesh traversal
52
53 namespace {
54 using namespace GEO;
55
56 2 void create_geodesic_sphere(Mesh& M, index_t nb_subdivisions, bool quads) {
57 static vec3 points[] = {
58 {0, 0.0, 1.175571},
59 {1.051462, 0.0, 0.5257311},
60 {0.3249197, 1.0, 0.5257311},
61 {-0.8506508, 0.618034, 0.5257311},
62 {-0.8506508, -0.618034, 0.5257311},
63 {0.3249197, -1.0, 0.5257311},
64 {0.8506508, 0.618034, -0.5257311},
65 {0.8506508, -0.618034, -0.5257311},
66 {-0.3249197, 1.0, -0.5257311},
67 {-1.051462, 0.0, -0.5257311},
68 {-0.3249197, -1.0, -0.5257311},
69 {0.0, 0.0, -1.175571}
70
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
6 };
71
72 static index_t facets[][3] = {
73 {0,1,2},
74 {0,2,3},
75 {0,3,4},
76 {0,4,5},
77 {0,5,1},
78 {1,5,7},
79 {1,7,6},
80 {1,6,2},
81 {2,6,8},
82 {2,8,3},
83 {3,8,9},
84 {3,9,4},
85 {4,9,10},
86 {4,10,5},
87 {5,10,7},
88 {6,7,11},
89 {6,11,8},
90 {7,10,11},
91 {8,11,9},
92 {9,11,10},
93 };
94
95 2 M.clear();
96 2 M.vertices.set_dimension(3);
97
98 M.vertices.create_vertices(12);
99
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
26 for(index_t v=0; v<12; ++v) {
100 24 M.vertices.point(v) = points[v];
101 }
102
103
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 2 times.
42 for(index_t f=0; f<20; ++f) {
104 40 M.facets.create_triangle(
105 facets[f][0],
106 facets[f][1],
107 facets[f][2]
108 );
109 }
110
111 2 M.facets.connect();
112
113 {
114
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 Stopwatch W("splitting");
115
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 for(index_t k=0; k<nb_subdivisions; ++k) {
116
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if(quads) {
117
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 mesh_split_quads(M);
118 } else {
119
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 mesh_split_triangles(M);
120 }
121 }
122 2 }
123
124 {
125
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 Stopwatch W("normalizing");
126
2/2
✓ Branch 0 taken 25604 times.
✓ Branch 1 taken 2 times.
25606 for(vec3& p:M.vertices.points()) {
127 25604 p = normalize(p);
128 }
129 2 }
130 2 }
131
132 2 double area_plain(const Mesh& M) {
133 double result = 0.0;
134
2/2
✓ Branch 0 taken 35840 times.
✓ Branch 1 taken 2 times.
35842 for(index_t f: M.facets) {
135 index_t v0 = M.facets.vertex(f,0);
136 const vec3& p0 = M.vertices.point(v0);
137
4/4
✓ Branch 0 taken 61440 times.
✓ Branch 1 taken 61440 times.
✓ Branch 2 taken 87040 times.
✓ Branch 3 taken 35840 times.
184320 for(index_t le=0; le+1<M.facets.nb_vertices(f); ++le) {
138 index_t v1 = M.facets.vertex(f,le);
139 const vec3& p1 = M.vertices.point(v1);
140 index_t v2 = M.facets.vertex(f,le+1);
141 const vec3& p2 = M.vertices.point(v2);
142 87040 result += Geom::triangle_area(p0,p1,p2);
143 }
144 }
145 2 return result;
146 }
147
148 2 double area_sugar(const Mesh& M) {
149 double result = 0.0;
150
2/2
✓ Branch 0 taken 35840 times.
✓ Branch 1 taken 2 times.
35842 for(index_t f: M.facets) {
151
2/2
✓ Branch 1 taken 51200 times.
✓ Branch 2 taken 35840 times.
87040 for(auto [p1, p2, p3] : M.facets.triangle_points(f)) {
152 51200 result += Geom::triangle_area(p1,p2,p3);
153 }
154 }
155 2 return result;
156 }
157
158 2 index_t nb_connected_components_plain(const Mesh& M) {
159 index_t result = 0;
160 vector<index_t> facet_comp(M.facets.nb(), NO_INDEX);
161 std::stack<index_t> S;
162
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35838 times.
✓ Branch 2 taken 35840 times.
✓ Branch 3 taken 2 times.
35842 for(index_t f: M.facets) {
163
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35838 times.
35840 if(facet_comp[f] == NO_INDEX) {
164
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 facet_comp[f] = result;
165 S.push(f);
166
2/2
✓ Branch 0 taken 35840 times.
✓ Branch 1 taken 2 times.
35842 while(!S.empty()) {
167
2/2
✓ Branch 0 taken 35559 times.
✓ Branch 1 taken 281 times.
35840 index_t g = S.top();
168 S.pop();
169
2/2
✓ Branch 0 taken 122880 times.
✓ Branch 1 taken 35840 times.
281600 for(index_t le=0; le<M.facets.nb_vertices(g); ++le) {
170 122880 index_t h = M.facets.adjacent(g,le);
171
3/4
✓ Branch 0 taken 122880 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35838 times.
✓ Branch 3 taken 87042 times.
122880 if(h != NO_INDEX && facet_comp[h] == NO_INDEX) {
172
2/2
✓ Branch 0 taken 35557 times.
✓ Branch 1 taken 281 times.
35838 facet_comp[h] = result;
173 S.push(h);
174 }
175 }
176 }
177 2 ++result;
178 }
179 }
180 2 return result;
181 }
182
183 2 index_t nb_connected_components_sugar(const Mesh& M) {
184 index_t result = 0;
185 vector<index_t> facet_comp(M.facets.nb(), NO_INDEX);
186 std::stack<index_t> S;
187
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35838 times.
✓ Branch 2 taken 35840 times.
✓ Branch 3 taken 2 times.
35842 for(index_t f: M.facets) {
188
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35838 times.
35840 if(facet_comp[f] == NO_INDEX) {
189
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 facet_comp[f] = result;
190 S.push(f);
191
2/2
✓ Branch 0 taken 35840 times.
✓ Branch 1 taken 2 times.
35842 while(!S.empty()) {
192
2/2
✓ Branch 0 taken 35559 times.
✓ Branch 1 taken 281 times.
35840 index_t g = S.top();
193 S.pop();
194
2/2
✓ Branch 0 taken 122880 times.
✓ Branch 1 taken 35840 times.
158720 for(index_t h: M.facets.adjacent(g)) {
195
3/4
✓ Branch 0 taken 122880 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35838 times.
✓ Branch 3 taken 87042 times.
122880 if(h != NO_INDEX && facet_comp[h] == NO_INDEX) {
196
2/2
✓ Branch 0 taken 35557 times.
✓ Branch 1 taken 281 times.
35838 facet_comp[h] = result;
197 S.push(h);
198 }
199 }
200 }
201 2 ++result;
202 }
203 }
204 2 return result;
205 }
206
207 }
208
209
210 2 int main(int argc, char** argv) {
211 using namespace GEO;
212
213 2 GEO::initialize(GEO::GEOGRAM_INSTALL_ALL);
214
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 CmdLine::import_arg_group("standard");
215
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 CmdLine::declare_arg(
216
2/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
4 "nb_subdivisions",4,
217 2 "number of subdivisions from icosahedron"
218 );
219
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 CmdLine::declare_arg(
220
3/8
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
6 "quads", false, "use quads instead of triangles"
221 );
222 std::vector<std::string> filenames;
223
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
4 if(!CmdLine::parse(argc, argv, filenames, "<inputfile> <outputfile>")) {
224 return 1;
225 }
226 try {
227
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 Mesh M;
228
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(filenames.size() >= 1) {
230 mesh_load(filenames[0], M);
231 index_t nb_subdivisions = CmdLine::get_arg_uint("nb_subdivisions");
232 bool quads = CmdLine::get_arg_bool("quads");
233 Stopwatch W("splitting");
234 for(index_t k=0; k<nb_subdivisions; ++k) {
235 if(quads) {
236 mesh_split_quads(M);
237 } else {
238 mesh_split_triangles(M);
239 }
240 }
241 } else {
242
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 create_geodesic_sphere(
243 M,
244
2/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
4 CmdLine::get_arg_uint("nb_subdivisions"),
245
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 CmdLine::get_arg_bool("quads")
246 );
247 }
248
249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(filenames.size() == 2) {
250 mesh_save(M, filenames[1]);
251 }
252
253 double area_p = 0.0;
254 double area_s = 0.0;
255 {
256
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Stopwatch W("area (plain)");
257 2 area_p = area_plain(M);
258 2 }
259 {
260
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Stopwatch W("area (sugar)");
261 2 area_s = area_sugar(M);
262 2 }
263
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Logger::out("Area") << area_p << " " << area_s << std::endl;
264
265
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
2 geo_assert(
266 ::fabs(area_p - area_s) / ::fabs(0.5*(area_p + area_s)) < 1e-6
267 );
268
269 index_t nb_cnx_p = 0;
270 index_t nb_cnx_s = 0;
271 {
272
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Stopwatch W("cnx (plain)");
273
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 nb_cnx_p = nb_connected_components_plain(M);
274 2 }
275 {
276
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Stopwatch W("cnx (sugar)");
277
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 nb_cnx_s = nb_connected_components_sugar(M);
278 2 }
279
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Logger::out("Cnx comps") << nb_cnx_p << " " << nb_cnx_s << std::endl;
280
1/8
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
2 geo_assert(nb_cnx_p == nb_cnx_s);
281
282
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 } catch(const std::exception& e) {
283 std::cerr << "Received an exception: " << e.what() << std::endl;
284 return 1;
285 }
286
287 2 return 0;
288 2 }
289