GCC Code Coverage Report


Directory: ./
File: lib/geogram/voronoi/CVT.h
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 7 10 70.0%
Functions: 0 0 -%
Branches: 8 26 30.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_VORONOI_CVT
41 #define GEOGRAM_VORONOI_CVT
42
43 #include <geogram/basic/common.h>
44 #include <geogram/voronoi/RVD.h>
45 #include <geogram/voronoi/integration_simplex.h>
46 #include <geogram/mesh/mesh.h>
47 #include <geogram/delaunay/delaunay.h>
48
49
50 /**
51 * \file geogram/voronoi/CVT.h
52 * \brief Main class for computing centroidal Voronoi tesselations.
53 */
54
55 namespace GEO {
56
57 class RestrictedVoronoiDiagram;
58 class ProgressTask;
59
60 /**
61 * \brief CentroidalVoronoiTesselation is the main component
62 * of the remeshing algorithm.
63 *
64 * \details It evenly distributes points over a surface embedded in Rn,
65 * where n can be of arbitrary dimension. The geometrical
66 * computations are done by RestrictedVoronoiDiagram, and
67 * the numerical optimization by Optimizer.
68 */
69 class GEOGRAM_API CentroidalVoronoiTesselation {
70
71 /** \brief This class type */
72 typedef CentroidalVoronoiTesselation thisclass;
73
74 public:
75 /**
76 * \brief Constructs a new CentroidalVoronoiTesselation.
77 * \details This constructor should be used when the
78 * first three coordinates of the mesh are x,y,z.
79 * \param[in] mesh a pointer to the input mesh
80 * \param[in] dimension If set, uses only the dimension first
81 * coordinates in mesh, else dimension is determined
82 * by mesh->dimension().
83 * \param[in] delaunay factory name of the implementation of
84 * Delaunay triangulation. Default uses ANN and radius
85 * of security.
86 */
87 CentroidalVoronoiTesselation(
88 Mesh* mesh,
89 coord_index_t dimension = 0,
90
2/7
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
16 const std::string& delaunay = "default"
91 );
92
93 /**
94 * \brief Constructs a new CentroidalVoronoiTesselation.
95 * \details This constructor should be used when the coordinates of
96 * the mesh are not related with R3.
97 * \param[in] mesh a pointer to the input mesh
98 * \param[in] R3_embedding (dimension = mesh->nb_vertices()):
99 * coordinates of the mesh vertices in R3. Ignored
100 * if size is zero.
101 * \param[in] dimension If set, uses only the dimension first
102 * coordinates in mesh, else dimension is determined
103 * by mesh->dimension().
104 * \param[in] delaunay factory name of the implementation of
105 * Delaunay triangulation. delaunay="default" uses
106 * ANN and radius of security.
107 */
108 CentroidalVoronoiTesselation(
109 Mesh* mesh,
110 const vector<vec3>& R3_embedding, coord_index_t dimension = 0,
111 const std::string& delaunay = "default"
112 );
113
114 /**
115 * \brief Destructor
116 */
117 virtual ~CentroidalVoronoiTesselation();
118
119 /**
120 * \brief Computes a random initial sampling of the surface in nD.
121 *
122 * \details This initial sampling (of low quality/regularity) needs to
123 * be further optimized (using Lloyd_iterations() and
124 * Newton_iterations()).
125 *
126 * \param[in] nb_samples number of points to generate in the sampling
127 * \param[in] verbose if set, display message
128 */
129 bool compute_initial_sampling(index_t nb_samples, bool verbose=false);
130
131 /**
132 * \brief Initializes the points with a user-specified vector.
133 *
134 * \param[in] nb_points number of points in \p points
135 * \param[in] points (size = dimension()*nb_points):
136 * user-defined initialization. It is copied into
137 * an internal vector
138 */
139 void set_points(index_t nb_points, const double* points);
140
141 /**
142 * \brief Changes the number of points.
143 * \param[in] nb_points new number of points
144 * \details Resizes the internal vector used to store the points
145 */
146 void resize_points(index_t nb_points);
147
148 /**
149 * \brief Relaxes the points with Lloyd's algorithm.
150 * \details It is in general less efficient than Newton, but more
151 * resistant to heterogeneous point distribution. Therefore a
152 * small number of Lloyd iterations may be used right after
153 * a call to compute_initial_sampling() to regularize
154 * the point set before calling Newton_iterations().
155 * \param[in] nb_iter number of iterations
156 */
157 virtual void Lloyd_iterations(index_t nb_iter);
158
159 /**
160 * \brief Relaxes the points with Newton-Lloyd's algorithm.
161 * \param[in] nb_iter number of iterations
162 * \param[in] m number of evaluations used for Hessian approximation
163 */
164 virtual void Newton_iterations(index_t nb_iter, index_t m = 7);
165
166 /**
167 * \brief Computes the surfacic mesh (using the current points).
168 * \param[out] mesh the computed surface
169 * \param[in] multinerve If set, does topology control (uses
170 * the dual of the connected components of the RVD).
171 */
172 void compute_surface(Mesh* mesh, bool multinerve = true);
173
174 /**
175 * \brief Computes the volumetric mesh (using the current points).
176 * \param[out] mesh the computed volumetric mesh
177 * \pre volumetric()
178 */
179 void compute_volume(Mesh* mesh);
180
181 /**
182 * \brief Specifies whether a progress bar should be used.
183 * \param[in] x If set, shows iterations using a "progress bar".
184 */
185 void set_show_iterations(bool x) {
186 show_iterations_ = x;
187 }
188
189 /**
190 * \brief Specifies whether centroids of Voronoi cells should be used.
191 * \param[in] x If set (default = true), compute_surface() replaces
192 * the vertices with the centroids of the
193 * connected components of the restricted Voronoi cells.
194 */
195 void set_use_RVC_centroids(bool x) {
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 use_RVC_centroids_ = x;
197 }
198
199 /**
200 * \brief Specifies whether constrained mode should be used.
201 * \param[in] x If set (default = false), compute_surface() projects
202 * the vertices onto the input surface.
203 */
204 void set_constrained_cvt(bool x) {
205 constrained_cvt_ = x;
206 }
207
208 /**
209 * Returns the input mesh.
210 */
211 Mesh* mesh() {
212 return mesh_;
213 }
214
215 /**
216 * Returns the Delaunay triangulation.
217 */
218 Delaunay* delaunay() {
219 return delaunay_;
220 }
221
222 /**
223 * Returns the RestrictedVoronoiDiagram.
224 */
225 RestrictedVoronoiDiagram* RVD() {
226 return RVD_;
227 }
228
229 /**
230 * \brief Restricts computation to a part of the input mesh.
231 * \details The part of the input mesh should be specified as
232 * a contiguous range of facet indices.
233 * \param[in] facets_begin first facet in the range
234 * \param[in] facets_end one past last facet in the range
235 */
236 void set_facets_range(index_t facets_begin, index_t facets_end) {
237 RVD_->set_facets_range(facets_begin, facets_end);
238 }
239
240 /**
241 * \brief Makes this CentroidalVoronoiTesselation the current one.
242 * \details The Optimizer uses global variables, therefore there can
243 * be only one CentroidalVoronoiTesselation simultaneously active.
244 * This function can be used to change the currently active
245 * CentroidalVoronoiTesselation.
246 * \note Most users will not need to use this function.
247 * \pre There is no current CentroidalVoronoiTesselation.
248 */
249 void make_current() {
250 geo_assert(instance_ == nullptr);
251 instance_ = this;
252 }
253
254 /**
255 * \brief Resets the current CentroidalVoronoiTesselation to nullptr.
256 * \details The Optimizer uses global variables, therefore there can
257 * be only one CentroidalVoronoiTesselation simultaneously active.
258 * This function can be used to change the currently active
259 * CentroidalVoronoiTesselation.
260 * \note Most users will not need to use this function.
261 * \pre This CentroidalVoronoiTesselation is the current one.
262 */
263 void done_current() {
264 geo_assert(instance_ == this);
265 instance_ = nullptr;
266 }
267
268 public:
269 /**
270 * \brief Callback for the numerical solver.
271 * \details Evaluates the objective function and its gradient.
272 * \param[in] n number of variables
273 * \param[in] x current value of the variables
274 * \param[out] f current value of the objective function
275 * \param[out] g gradient of the objective function
276 */
277 static void funcgrad_CB(
278 index_t n, double* x, double& f, double* g
279 );
280
281 /**
282 * \brief Callback for the numerical solver.
283 * \details Updates the progress bar.
284 * \param[in] n number of variables
285 * \param[in] x current value of the variables
286 * \param[in] f current value of the objective function
287 * \param[in] g gradient of the objective function
288 * \param[in] gnorm norm of the gradient of the objective function
289 */
290 static void newiteration_CB(
291 index_t n, const double* x, double f, const double* g, double gnorm
292 );
293
294 /**
295 * \brief Sets a client for the progress bars.
296 * \param[in] progress the ProgressTask.
297 */
298 void set_progress_logger(ProgressTask* progress) {
299
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
14 progress_ = progress;
300 4 }
301
302 /**
303 * \brief Gets the dimension of the points.
304 * \details Can be smaller than the dimension of the mesh.
305 */
306 coord_index_t dimension() const {
307 return dimension_;
308 }
309
310 /**
311 * \brief Gets the number of points to be optimized.
312 */
313 index_t nb_points() const {
314 return index_t(points_.size() / dimension_);
315 }
316
317 /**
318 * \brief Gets the representation of a point in R3.
319 * \param[in] p index of the point
320 * \return a const reference to the 3d version of the point
321 * \pre p < nb_points()
322 */
323 const vec3& R3_embedding(index_t p) const {
324 return RVD_->R3_embedding(p);
325 }
326
327 /**
328 * \brief Returns the representation of a point in embedding space.
329 * \param[in] p index of the point
330 * \return a pointer to the coordinates of the point
331 * \pre p < nb_points()
332 */
333 double* embedding(index_t p) {
334 geo_debug_assert(p < nb_points());
335 return &(points_[0]) + dimension_ * p;
336 }
337
338 /**
339 * \brief Tests whether volumetric mode is used.
340 */
341 bool volumetric() const {
342 return RVD_->volumetric();
343 }
344
345 /**
346 * \brief Sets volumetric mode.
347 * \param[in] x if true, volumetric mode is used, otherwise
348 * surfacic mode is used.
349 */
350 void set_volumetric(bool x) {
351
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 RVD_->set_volumetric(x);
352 4 }
353
354 /**
355 * \brief Tests whether a point is locked.
356 * \details A locked point is constrained to stay at the same position
357 * during the optimization.
358 * \param[in] i index of the point
359 * \pre i < nb_points()
360 */
361 bool point_is_locked(index_t i) const {
362 geo_debug_assert(
363 point_is_locked_.size() == 0 || i < point_is_locked_.size()
364 );
365
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1247966 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1247966 return point_is_locked_.size() != 0 && point_is_locked_[i];
366 }
367
368 /**
369 * \brief Locks a point.
370 * \details A locked point is constrained to stay at the same position
371 * during the optimization.
372 * \param[in] i index of the point
373 * \pre i < nb_points()
374 */
375 void lock_point(index_t i) {
376 geo_debug_assert(i < nb_points());
377 if(point_is_locked_.size() != nb_points()) {
378 point_is_locked_.resize(nb_points(), false);
379 }
380 point_is_locked_[i] = true;
381 }
382
383 /**
384 * \brief Unlocks a point.
385 * \details A locked point is constrained to stay at the same position
386 * during the optimization.
387 * \param[in] i index of the point
388 * \pre i < nb_points()
389 */
390 void unlock_point(index_t i) {
391 geo_debug_assert(i < nb_points());
392 if(
393 point_is_locked_.size() != nb_points()
394 ) {
395 point_is_locked_.resize(nb_points(), false);
396 }
397 point_is_locked_[i] = false;
398 }
399
400 /**
401 * \brief Unlocks all the points.
402 * \details A locked point is constrained to stay at the same position
403 * during the optimization.
404 */
405 void unlock_all_points() {
406 point_is_locked_.clear();
407 }
408
409 protected:
410 /**
411 * \brief Callback for the numerical solver.
412 * \details Updates the progress bar.
413 */
414 virtual void newiteration();
415
416 /**
417 * \brief Computes the objective function and its gradient.
418 * \param[in] n number of variables
419 * \param[in] x current value of the variables
420 * \param[out] f current value of the objective function
421 * \param[out] g gradient of the objective function
422 */
423 virtual void funcgrad(index_t n, double* x, double& f, double* g);
424
425 /**
426 * \brief Constrains the locked points.
427 * \details Zeroes the gradient relative to the components
428 * of locked points.
429 * \param[in,out] g gradient of the objective function
430 */
431 void constrain_points(double* g) const;
432
433 /**
434 * \brief Computes the 3d representation of the Nd points.
435 * \details It projects the points onto the Nd surface, then recovers
436 * the 3d coordinates by barycentric interpolation.
437 */
438 void compute_R3_embedding();
439
440 static CentroidalVoronoiTesselation* instance_;
441 bool show_iterations_;
442 coord_index_t dimension_;
443 Delaunay_var delaunay_;
444 RestrictedVoronoiDiagram_var RVD_;
445 Mesh* mesh_;
446
447 vector<double> points_;
448 vector<vec3> points_R3_;
449 vector<bool> point_is_locked_;
450
451 ProgressTask* progress_;
452 index_t cur_iter_;
453 index_t nb_iter_;
454
455 bool is_projection_; /**< the Nd -> 3d transform is a projection */
456 bool constrained_cvt_;
457 bool use_RVC_centroids_;
458
459 IntegrationSimplex_var simplex_func_;
460 /**< \brief Integration simplex used by custom codes, e.g. LpCVT */
461
462 private:
463 /** \brief Forbids construction by copy. */
464 CentroidalVoronoiTesselation(const thisclass& rhs);
465
466 /** \brief Forbids assignment. */
467 thisclass& operator= (const thisclass& rhs);
468 };
469 }
470
471 #endif
472