GCC Code Coverage Report


Directory: ./
File: lib/geogram/mesh/mesh_CSG_utils.h
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 12 14 85.7%
Functions: 5 6 83.3%
Branches: 4 20 20.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2025 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 H_GEOGRAM_MESH_MESH_CSG_UTILS_H
41 #define H_GEOGRAM_MESH_MESH_CSG_UTILS_H
42
43 #include <geogram/basic/common.h>
44 #include <geogram/mesh/mesh.h>
45 #include <string>
46 #include <filesystem>
47 #include <memory>
48
49 namespace GEOCSG {
50
51 using namespace GEO;
52
53 /**********************************************************************/
54 /**** Values, ArgList ****/
55 /**********************************************************************/
56
57 /**
58 * \brief A parsed value in a .csg file
59 * \details Can be a number, a boolean, a 1d array, 2d array, a string
60 * or a file path.
61 */
62 struct Value {
63 enum Type {NONE, NUMBER, BOOLEAN, ARRAY1D, ARRAY2D, STRING, PATH};
64
65 Value();
66 Value(double x);
67 Value(int x);
68 Value(bool x);
69 Value(const std::string& x);
70 Value(const std::filesystem::path& x);
71 std::string to_string() const;
72
73 Type type;
74 bool boolean_val;
75 double number_val;
76 vector<vector<double> > array_val;
77 std::string string_val;
78 };
79
80 /**
81 * \brief A parsed argument list in a .csg file.
82 * \details Stores name-value pairs.
83 */
84 class ArgList {
85 public:
86 typedef std::pair<std::string, Value> Arg;
87
88 /**
89 * \brief Constructs an empty ArgList
90 */
91 ArgList();
92
93 /**
94 * \brief Removes all the arguments in this arglist
95 */
96 void clear() {
97 args_.clear();
98 }
99
100 /**
101 * \brief Gets the number of arguments
102 * \return the number of arguments in this arglist
103 */
104 399 index_t size() const {
105 399 return args_.size();
106 }
107
108 /**
109 * \brief Gets the name of an argument
110 * \param[in] i the index of the argument, in 0..size()-1
111 * \return the name of the \p i th argument
112 */
113 63 const std::string& ith_arg_name(index_t i) const {
114
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
63 geo_debug_assert(i < size());
115 63 return args_[i].first;
116 }
117
118 /**
119 * \brief Gets the value of an argument
120 * \param[in] i the index of the argument, in 0..size()-1
121 * \return the value of the argument as a const reference to a Value
122 */
123 189 const Value& ith_arg_val(index_t i) const {
124
1/6
✗ Branch 1 not taken.
✓ Branch 2 taken 189 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
189 geo_debug_assert(i < size());
125 189 return args_[i].second;
126 }
127
128 /**
129 * \brief Tests whether this arglist has an argument
130 * \param[in] name the name of the argument
131 * \param[in] pos_fallback a position fallback to be used if the
132 * arglist has no argument with \p name, or NO_INDEX if no positional
133 * fallback is specified
134 * \param[in] type required type for the argument, or NONE if any type
135 * matches
136 * \retval true if an argument with \p name exists or \p pos_fallback is
137 * different from NO_INDEX and smaller than size()
138 * \retval false otherwise
139 */
140 bool has_arg(
141 const std::string& name, index_t pos_fallback = NO_INDEX,
142 Value::Type type = Value::NONE
143 ) const;
144
145 void add_arg(const std::string& name, const Value& value);
146
147 /**
148 * \brief Gets an argument value
149 * \param[in] name the name of the argument
150 * \param[in] pos_fallback a position fallback to be used if the
151 * arglist has no argument with \p name, or NO_INDEX if no positional
152 * fallback is specified
153 * \details fires an assertion failure if \p name is not bound and
154 * pos_fallback is NO_INDEX or greater and equal to size()
155 */
156 const Value& get_arg_value(
157 const std::string& name, index_t pos_fallback = NO_INDEX
158 ) const;
159
160 /**
161 * \brief Gets an argument of type double
162 * \param[in] name the name of the argument
163 * \param[in] default_val default argument value
164 * \param[in] pos_fallback a position fallback to be used if the
165 * arglist has no argument with \p name, or NO_INDEX if no positional
166 * fallback is specified
167 * \retval the value of the argument if present in the argument list
168 * by name or by positional fallback
169 * \retval \p default_val otherwise
170 * \details fires a std::logic_error exception if argument exists but
171 * has incorrect type
172 */
173 double get_arg(
174 const std::string& name, double default_val,
175 index_t pos_fallback = NO_INDEX
176 ) const;
177
178 /**
179 * \brief Gets an argument of type int
180 * \param[in] name the name of the argument
181 * \param[in] default_val default argument value
182 * \param[in] pos_fallback a position fallback to be used if the
183 * arglist has no argument with \p name, or NO_INDEX if no positional
184 * fallback is specified
185 * \retval the value of the argument if present in the argument list
186 * by name or by positional fallback
187 * \retval \p default_val otherwise
188 * \details fires a std::logic_error exception if argument exists but
189 * has incorrect type
190 */
191 int get_arg(
192 const std::string& name, int default_val,
193 index_t pos_fallback = NO_INDEX
194 ) const;
195
196 /**
197 * \brief Gets an argument of type bool
198 * \param[in] name the name of the argument
199 * \param[in] default_val default argument value
200 * \param[in] pos_fallback a position fallback to be used if the
201 * arglist has no argument with \p name, or NO_INDEX if no positional
202 * fallback is specified
203 * \retval the value of the argument if present in the argument list
204 * by name or by positional fallback
205 * \retval \p default_val otherwise
206 * \details fires a std::logic_error exception if argument exists but
207 * has incorrect type
208 */
209 bool get_arg(
210 const std::string& name, bool default_val,
211 index_t pos_fallback = NO_INDEX
212 ) const;
213
214 /**
215 * \brief Gets an argument of type vec2
216 * \param[in] name the name of the argument
217 * \param[in] default_val default argument value
218 * \param[in] pos_fallback a position fallback to be used if the
219 * arglist has no argument with \p name, or NO_INDEX if no positional
220 * fallback is specified
221 * \retval the value of the argument if present in the argument list
222 * by name or by positional fallback
223 * \retval \p default_val otherwise
224 * \details fires a std::logic_error exception if argument exists but
225 * has incorrect type
226 */
227 vec2 get_arg(
228 const std::string& name, vec2 default_val,
229 index_t pos_fallback = NO_INDEX
230 ) const;
231
232 /**
233 * \brief Gets an argument of type vec3
234 * \param[in] name the name of the argument
235 * \param[in] default_val default argument value
236 * \param[in] pos_fallback a position fallback to be used if the
237 * arglist has no argument with \p name, or NO_INDEX if no positional
238 * fallback is specified
239 * \retval the value of the argument if present in the argument list
240 * by name or by positional fallback
241 * \retval \p default_val otherwise
242 * \details fires a std::logic_error exception if argument exists but
243 * has incorrect type
244 */
245 vec3 get_arg(
246 const std::string& name, vec3 default_val,
247 index_t pos_fallback = NO_INDEX
248 ) const;
249
250 /**
251 * \brief Gets an argument of type vec4
252 * \param[in] name the name of the argument
253 * \param[in] default_val default argument value
254 * \param[in] pos_fallback a position fallback to be used if the
255 * arglist has no argument with \p name, or NO_INDEX if no positional
256 * fallback is specified
257 * \retval the value of the argument if present in the argument list
258 * by name or by positional fallback
259 * \retval \p default_val otherwise
260 * \details fires a std::logic_error exception if argument exists but
261 * has incorrect type
262 */
263 vec4 get_arg(
264 const std::string& name, vec4 default_val,
265 index_t pos_fallback = NO_INDEX
266 ) const;
267
268 /**
269 * \brief Gets an argument of type mat4
270 * \param[in] name the name of the argument
271 * \param[in] default_val default argument value
272 * \param[in] pos_fallback a position fallback to be used if the
273 * arglist has no argument with \p name, or NO_INDEX if no positional
274 * fallback is specified
275 * \retval the value of the argument if present in the argument list
276 * by name or by positional fallback
277 * \retval \p default_val otherwise
278 * \details fires a std::logic_error exception if argument exists but
279 * has incorrect type
280 */
281 mat4 get_arg(
282 const std::string& name, const mat4& default_val,
283 index_t pos_fallback = NO_INDEX
284 ) const;
285
286 /**
287 * \brief Gets an argument of type string
288 * \param[in] name the name of the argument
289 * \param[in] default_val default argument value
290 * \param[in] pos_fallback a position fallback to be used if the
291 * arglist has no argument with \p name, or NO_INDEX if no positional
292 * fallback is specified
293 * \retval the value of the argument if present in the argument list
294 * by name or by positional fallback
295 * \retval \p default_val otherwise
296 * \details fires a std::logic_error exception if argument exists but
297 * has incorrect type
298 */
299 std::string get_arg(
300 const std::string& name, const std::string& default_val,
301 index_t pos_fallback = NO_INDEX
302 ) const;
303
304 /**
305 * \brief Gets an argument of type string
306 * \param[in] name the name of the argument
307 * \param[in] default_val default argument value, as a const char*
308 * \param[in] pos_fallback a position fallback to be used if the
309 * arglist has no argument with \p name, or NO_INDEX if no positional
310 * fallback is specified
311 * \retval the value of the argument if present in the argument list
312 * by name or by positional fallback
313 * \retval \p default_val otherwise
314 * \details fires a std::logic_error exception if argument exists but
315 * has incorrect type
316 */
317 std::string get_arg(
318 const std::string& name, const char* default_val,
319 index_t pos_fallback = NO_INDEX
320 ) const {
321 return get_arg(name, std::string(default_val), pos_fallback);
322 }
323
324 protected:
325 /**
326 * \brief Gets the internal name used for an unnamed argument
327 * \param[in] i the index for the i-th unnamed argument
328 * \return the internal name of the i-th unnamed argument as a string
329 */
330 832 static std::string unnamed_arg_name(index_t i) {
331
2/4
✓ Branch 1 taken 832 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 832 times.
✗ Branch 5 not taken.
832 return "$unnamed_" + String::to_string(i);
332 }
333 private:
334 vector<Arg> args_;
335 index_t nb_unnamed_;
336 };
337
338 /**********************************************************************/
339 /**** General sweeping function ****/
340 /**********************************************************************/
341
342 /**
343 * \brief Symbolic constants for sweep()
344 */
345 enum SweepCapping {
346 SWEEP_CAP,
347 SWEEP_POLE,
348 SWEEP_PERIODIC
349 };
350
351 /**
352 * \brief The generalized sweeping operation
353 * \details Used to implement sphere(), cylinder(), linear_extrude() and
354 * rotate_extrude()
355 * \param[in,out] M on entry, a 2D mesh. On exit, a 3D mesh. The triangles
356 * present in the mesh are used to generate the caps. They are copied to
357 * generate the second cap if \p capping is set to SWEEP_CAP (default).
358 * \param[in] nv number of sweeping steps. Minimum is 2.
359 * \param[in] sweep_path a function that maps u,v indices to 3D
360 * points, where u is the index of a initial 2D vertex and v
361 * in [0..nv-1] the sweeping step. One can use the point at vertex
362 * u to evaluate the path (it will not be overwritten before calling
363 * sweep_path()). Note that u vertices are not necessarily ordered.
364 * \param[in] capping one of:
365 * - SWEEP_CAP standard sweeping, generate second capping by
366 * copying first one
367 * - SWEEP_POLE if last sweeping step degenerates to a
368 * single point
369 * - SWEEP_PERIODIC if no cappings should be generated and last
370 * sweeping step corresponds to first one
371 */
372 void GEOGRAM_API sweep(
373 std::shared_ptr<Mesh>& M, index_t nv,
374 std::function<vec3(index_t, index_t)> sweep_path,
375 SweepCapping capping = SWEEP_CAP
376 );
377
378 /**
379 * \brief keeps only triangles and vertices embedded in the z=0 plane, and
380 * makes the mesh 2D.
381 * \param[in,out] M a shared pointer to the mesh
382 */
383 void GEOGRAM_API keep_z0_only(std::shared_ptr<Mesh>& M);
384
385 /**********************************************************************/
386 /**** Call OpenSCAD for help (and cache result) ****/
387 /**********************************************************************/
388
389 /**
390 * \brief Specifies that starting from now all cached OpenSCAD files
391 * are considered to be out of date and will be re-generated.
392 */
393 void GEOGRAM_API OpenSCAD_cache_invalidate();
394
395 /**
396 * \brief Specifies that last modification time should be ignored when
397 * considering the OpenSCAD cache.
398 * \details It is interesting to do so for testsuites that embark the
399 * OpenSCached directory for users who cannot install OpenSCAD.
400 */
401 void GEOGRAM_API OpenSCAD_cache_ignore_time();
402
403 std::shared_ptr<Mesh> GEOGRAM_API call_OpenSCAD(
404 const std::filesystem::path& path, const std::string& command,
405 const ArgList& args, bool TWO_D=false
406 );
407
408 std::string GEOGRAM_API load_OpenSCAD(const std::filesystem::path& filename);
409
410 /**********************************************************************/
411 /**** Functions to estimate number of fragments and slices, ****/
412 /**** taken from OpenSCAD ****/
413 /**********************************************************************/
414
415 /**
416 * \brief Taken from OpenSCAD
417 */
418 int GEOGRAM_API get_fragments_from_r_and_twist(
419 double r, double twist, double fn, double fs, double fa
420 );
421
422 /**
423 * \brief Taken from OpenSCAD
424 */
425 119 inline int get_fragments_from_r(
426 double r, double fn, double fs, double fa
427 ) {
428 119 return get_fragments_from_r_and_twist(r, 360.0, fn, fs, fa);
429 }
430
431 /**
432 * \brief Taken from OpenSCAD
433 */
434 int GEOGRAM_API get_linear_extrusion_slices(
435 std::shared_ptr<Mesh> M, double height, vec2 scale, double twist,
436 double fn, double fs, double fa
437 );
438
439 }
440
441 #endif
442