GCC Code Coverage Report


Directory: ./
File: lib/geogram/mesh/mesh_reorder.cpp
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 182 280 65.0%
Functions: 84 162 51.9%
Branches: 284 680 41.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 #include <geogram/mesh/mesh_reorder.h>
41 #include <geogram/mesh/mesh.h>
42 #include <geogram/mesh/mesh_geometry.h>
43 #include <geogram/mesh/mesh_repair.h>
44 #include <geogram/mesh/index.h>
45 #include <geogram/delaunay/periodic.h>
46 #include <geogram/basic/permutation.h>
47 #include <geogram/basic/process.h>
48 #include <geogram/basic/logger.h>
49 #include <geogram/basic/algorithm.h>
50 #include <geogram/bibliography/bibliography.h>
51
52 namespace {
53
54 using namespace GEO;
55
56 /**
57 * \brief Splits a sequence into two ordered halves.
58 * \details The algorithm shuffles the sequence and
59 * partitions its into two halves with the same number of elements
60 * and such that the elements of the first half are smaller
61 * than the elements of the second half.
62 * \param[in] begin an iterator to the first element
63 * \param[in] end an iterator one position past the last element
64 * \param[in] cmp the comparator object
65 * \return an iterator to the middle of the sequence that separates
66 * the two halves
67 */
68 template <class IT, class CMP>
69 1710350 inline IT reorder_split(
70 IT begin, IT end, CMP cmp
71 ) {
72
2/2
✓ Branch 0 taken 77824 times.
✓ Branch 1 taken 777351 times.
1710350 if(begin >= end) {
73 155648 return begin;
74 }
75 1554702 IT middle = begin + (end - begin) / 2;
76 1554702 std::nth_element(begin, middle, end, cmp);
77 1554702 return middle;
78 }
79
80 /************************************************************************/
81
82 /**
83 * \brief Used by VertexMesh.
84 * \details Exposes an interface compatible with the requirement
85 * of Hilbert sort templates for a raw array of vertices.
86 */
87 class VertexArray {
88 public:
89
90 /**
91 * \brief Constructs a new VertexArray.
92 * \param[in] base address of the points
93 * \param[in] stride number of doubles between
94 * two consecutive points
95 */
96 VertexArray(
97 index_t nb_vertices,
98 const double* base, index_t stride
99 81 ) :
100 81 base_(base),
101 81 stride_(stride) {
102 81 nb_vertices_ = nb_vertices;
103 }
104
105 /**
106 * \brief Gets a vertex by its index.
107 * \param[in] i the index of the point
108 * \return a const pointer to the coordinates of the vertex
109 */
110 const double* point_ptr(index_t i) const {
111 geo_debug_assert(i < nb_vertices_);
112 9199428 return base_ + i * stride_;
113 }
114
115 private:
116 const double* base_;
117 index_t stride_;
118 index_t nb_vertices_;
119 };
120
121
122 /**
123 * \brief Exposes an interface compatible with the requirement
124 * of Hilbert sort templates for a raw array of vertices.
125 */
126 class VertexMesh {
127 public:
128 /**
129 * \brief Constructs a new VertexMesh.
130 * \param[in] base address of the points
131 * \param[in] stride number of doubles between
132 * two consecutive points
133 */
134 VertexMesh(
135 index_t nb_vertices,
136 const double* base, index_t stride
137 ) : vertices(nb_vertices, base, stride) {
138 }
139 VertexArray vertices;
140 };
141
142 /************************************************************************/
143
144 /**
145 * \brief The generic comparator class for Hilbert vertex
146 * ordering.
147 * \tparam COORD the coordinate to compare
148 * \tparam UP if true, use direct order, else use reverse order
149 * \tparam MESH the class that represents meshes
150 */
151 template <int COORD, bool UP, class MESH>
152 struct Hilbert_vcmp {
153 };
154
155 /**
156 * \brief Specialization (UP=true) of the generic comparator class
157 * for Hilbert vertex ordering.
158 * \see Hilbert_vcmp
159 * \tparam COORD the coordinate to compare
160 * \tparam MESH the class that represents meshes
161 */
162 template <int COORD, class MESH>
163 struct Hilbert_vcmp<COORD, true, MESH> {
164
165 /**
166 * \brief Constructs a new Hilbert_vcmp.
167 * \param[in] mesh the mesh in which the compared
168 * points reside.
169 */
170 178609 Hilbert_vcmp(const MESH& mesh) :
171 178609 mesh_(mesh) {
172 }
173
174 /**
175 * \brief Compares two points.
176 * \param[in] i1 index of the first point to compare
177 * \param[in] i2 index of the second point to compare
178 * \return true if point \p i1 is before point \p i2,
179 * false otherwise.
180 */
181 bool operator() (index_t i1, index_t i2) {
182 return
183
12/30
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 80 times.
✓ Branch 9 taken 444 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 518 times.
✓ Branch 15 taken 444 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 108 times.
✓ Branch 19 taken 528 times.
✓ Branch 20 taken 93 times.
✓ Branch 21 taken 462 times.
✓ Branch 22 taken 521 times.
✓ Branch 23 taken 528 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 518 times.
✓ Branch 27 taken 462 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
4706 mesh_.vertices.point_ptr(i1)[COORD] <
184
90/120
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 9262 times.
✓ Branch 17 taken 44828 times.
✓ Branch 18 taken 434110 times.
✓ Branch 19 taken 295953 times.
✓ Branch 20 taken 426055 times.
✓ Branch 21 taken 295953 times.
✓ Branch 22 taken 30519 times.
✓ Branch 23 taken 24416 times.
✓ Branch 24 taken 14395 times.
✓ Branch 25 taken 16124 times.
✓ Branch 26 taken 10060 times.
✓ Branch 27 taken 14356 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 2 times.
✗ Branch 31 not taken.
✓ Branch 32 taken 80 times.
✓ Branch 33 taken 444 times.
✓ Branch 34 taken 2045 times.
✓ Branch 35 taken 2050 times.
✓ Branch 36 taken 2121 times.
✓ Branch 37 taken 2050 times.
✓ Branch 38 taken 356 times.
✓ Branch 39 taken 320 times.
✓ Branch 40 taken 159 times.
✓ Branch 41 taken 197 times.
✓ Branch 42 taken 102 times.
✓ Branch 43 taken 218 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 34821 times.
✓ Branch 49 taken 44828 times.
✓ Branch 50 taken 6 times.
✗ Branch 51 not taken.
✓ Branch 52 taken 78 times.
✓ Branch 53 taken 696 times.
✓ Branch 54 taken 3325 times.
✓ Branch 55 taken 3920 times.
✓ Branch 56 taken 3643 times.
✓ Branch 57 taken 3920 times.
✓ Branch 58 taken 283 times.
✓ Branch 59 taken 418 times.
✓ Branch 60 taken 80 times.
✓ Branch 61 taken 203 times.
✓ Branch 62 taken 133 times.
✓ Branch 63 taken 285 times.
✓ Branch 64 taken 9295 times.
✓ Branch 65 taken 45591 times.
✓ Branch 66 taken 581668 times.
✓ Branch 67 taken 406784 times.
✓ Branch 68 taken 684662 times.
✓ Branch 69 taken 406784 times.
✓ Branch 70 taken 31810 times.
✓ Branch 71 taken 25479 times.
✓ Branch 72 taken 14700 times.
✓ Branch 73 taken 17110 times.
✓ Branch 74 taken 10359 times.
✓ Branch 75 taken 15120 times.
✓ Branch 76 taken 518 times.
✓ Branch 77 taken 444 times.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✓ Branch 80 taken 108 times.
✓ Branch 81 taken 528 times.
✓ Branch 82 taken 5713 times.
✓ Branch 83 taken 3929 times.
✓ Branch 84 taken 4692 times.
✓ Branch 85 taken 3929 times.
✓ Branch 86 taken 392 times.
✓ Branch 87 taken 429 times.
✓ Branch 88 taken 165 times.
✓ Branch 89 taken 227 times.
✓ Branch 90 taken 124 times.
✓ Branch 91 taken 305 times.
✓ Branch 92 taken 93 times.
✓ Branch 93 taken 462 times.
✓ Branch 94 taken 3783 times.
✓ Branch 95 taken 3949 times.
✓ Branch 96 taken 4965 times.
✓ Branch 97 taken 3949 times.
✓ Branch 98 taken 358 times.
✓ Branch 99 taken 409 times.
✓ Branch 100 taken 130 times.
✓ Branch 101 taken 228 times.
✓ Branch 102 taken 135 times.
✓ Branch 103 taken 274 times.
✓ Branch 104 taken 297 times.
✓ Branch 105 taken 696 times.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✓ Branch 108 taken 35780 times.
✓ Branch 109 taken 45591 times.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✓ Branch 112 taken 521 times.
✓ Branch 113 taken 528 times.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✓ Branch 116 taken 518 times.
✓ Branch 117 taken 462 times.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
3981632 mesh_.vertices.point_ptr(i2)[COORD];
185 }
186
187 const MESH& mesh_;
188 };
189
190 /**
191 * \brief Specialization (UP=false) of the generic comparator class
192 * for Hilbert vertex ordering.
193 * \see Hilbert_vcmp
194 * \tparam COORD the coordinate to compare
195 * \tparam MESH the class that represents meshes
196 */
197 template <int COORD, class MESH>
198 struct Hilbert_vcmp<COORD, false, MESH> {
199
200 /**
201 * \brief Constructs a new Hilbert_vcmp.
202 * \param[in] mesh the mesh in which the compared
203 * points reside.
204 */
205 179685 Hilbert_vcmp(const MESH& mesh) :
206 179685 mesh_(mesh) {
207 }
208
209 /**
210 * \brief Compares two points.
211 * \param[in] i1 index of the first point to compare
212 * \param[in] i2 index of the second point to compare
213 * \return true if point \p i1 is before point \p i2,
214 * false otherwise.
215 */
216 bool operator() (index_t i1, index_t i2) {
217 return
218
12/30
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 93 times.
✓ Branch 13 taken 501 times.
✓ Branch 14 taken 85 times.
✓ Branch 15 taken 468 times.
✓ Branch 16 taken 89 times.
✓ Branch 17 taken 508 times.
✓ Branch 18 taken 470 times.
✓ Branch 19 taken 501 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✓ Branch 22 taken 540 times.
✓ Branch 23 taken 468 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 540 times.
✓ Branch 27 taken 508 times.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
4771 mesh_.vertices.point_ptr(i1)[COORD] >
219
87/120
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ 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.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 94 times.
✓ Branch 25 taken 766 times.
✓ Branch 26 taken 3840 times.
✓ Branch 27 taken 3985 times.
✓ Branch 28 taken 3535 times.
✓ Branch 29 taken 3985 times.
✓ Branch 30 taken 279 times.
✓ Branch 31 taken 391 times.
✓ Branch 32 taken 83 times.
✓ Branch 33 taken 196 times.
✓ Branch 34 taken 126 times.
✓ Branch 35 taken 265 times.
✓ Branch 36 taken 9235 times.
✓ Branch 37 taken 45138 times.
✓ Branch 38 taken 593072 times.
✓ Branch 39 taken 407131 times.
✓ Branch 40 taken 570583 times.
✓ Branch 41 taken 407131 times.
✓ Branch 42 taken 31404 times.
✓ Branch 43 taken 25647 times.
✓ Branch 44 taken 14820 times.
✓ Branch 45 taken 16584 times.
✓ Branch 46 taken 10388 times.
✓ Branch 47 taken 15259 times.
✓ Branch 48 taken 9333 times.
✓ Branch 49 taken 45775 times.
✓ Branch 50 taken 838021 times.
✓ Branch 51 taken 552513 times.
✓ Branch 52 taken 711069 times.
✓ Branch 53 taken 552513 times.
✓ Branch 54 taken 32493 times.
✓ Branch 55 taken 26906 times.
✓ Branch 56 taken 15167 times.
✓ Branch 57 taken 17326 times.
✓ Branch 58 taken 10964 times.
✓ Branch 59 taken 15942 times.
✓ Branch 60 taken 93 times.
✓ Branch 61 taken 501 times.
✓ Branch 62 taken 4730 times.
✓ Branch 63 taken 3792 times.
✓ Branch 64 taken 4400 times.
✓ Branch 65 taken 3792 times.
✓ Branch 66 taken 378 times.
✓ Branch 67 taken 427 times.
✓ Branch 68 taken 135 times.
✓ Branch 69 taken 243 times.
✓ Branch 70 taken 156 times.
✓ Branch 71 taken 271 times.
✓ Branch 72 taken 85 times.
✓ Branch 73 taken 468 times.
✓ Branch 74 taken 4331 times.
✓ Branch 75 taken 3886 times.
✓ Branch 76 taken 4432 times.
✓ Branch 77 taken 3886 times.
✓ Branch 78 taken 338 times.
✓ Branch 79 taken 407 times.
✓ Branch 80 taken 141 times.
✓ Branch 81 taken 197 times.
✓ Branch 82 taken 126 times.
✓ Branch 83 taken 281 times.
✓ Branch 84 taken 89 times.
✓ Branch 85 taken 508 times.
✓ Branch 86 taken 6464 times.
✓ Branch 87 taken 5264 times.
✓ Branch 88 taken 6826 times.
✓ Branch 89 taken 5264 times.
✓ Branch 90 taken 376 times.
✓ Branch 91 taken 471 times.
✓ Branch 92 taken 143 times.
✓ Branch 93 taken 233 times.
✓ Branch 94 taken 154 times.
✓ Branch 95 taken 317 times.
✓ Branch 96 taken 345 times.
✓ Branch 97 taken 766 times.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✓ Branch 100 taken 34847 times.
✓ Branch 101 taken 45138 times.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✓ Branch 104 taken 36174 times.
✓ Branch 105 taken 45775 times.
✓ Branch 106 taken 4 times.
✓ Branch 107 taken 2 times.
✓ Branch 108 taken 470 times.
✓ Branch 109 taken 501 times.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✓ Branch 112 taken 540 times.
✓ Branch 113 taken 468 times.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✓ Branch 116 taken 540 times.
✓ Branch 117 taken 508 times.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
5102131 mesh_.vertices.point_ptr(i2)[COORD];
220 }
221
222 const MESH& mesh_;
223 };
224
225 /************************************************************************/
226
227 /**
228 * \brief Comparator class for Morton vertex
229 * ordering.
230 * \tparam COORD the coordinate to compare
231 * \tparam UP ignored in Morton order
232 * \tparam MESH the class that represents meshes
233 */
234 template <int COORD, bool UP, class MESH>
235 struct Morton_vcmp {
236
237 /**
238 * \brief Constructs a new Morton_vcmp.
239 * \param[in] mesh the mesh in which the compared
240 * points reside.
241 */
242 Morton_vcmp(const MESH& mesh) :
243 mesh_(mesh) {
244 }
245
246 /**
247 * \brief Compares two points.
248 * \param[in] i1 index of the first point to compare
249 * \param[in] i2 index of the second point to compare
250 * \return true if point \p i1 is before point \p i2,
251 * false otherwise.
252 */
253 bool operator() (index_t i1, index_t i2) {
254 return
255 mesh_.vertices.point_ptr(i1)[COORD] <
256 mesh_.vertices.point_ptr(i2)[COORD];
257 }
258
259 const MESH& mesh_;
260 };
261
262 /************************************************************************/
263
264 #ifndef GEOGRAM_PSM
265
266 /**
267 * \brief Base class for facets ordering.
268 * \tparam COORD the coordinate to compare
269 * \tparam MESH the class that represents meshes
270 */
271 template <int COORD, class MESH>
272 class Base_fcmp {
273 public:
274 /**
275 * \brief Constructs a new Base_vcmp.
276 * \param[in] mesh the mesh in which the compared
277 * facets reside.
278 */
279 494837 Base_fcmp(const MESH& mesh) :
280 494837 mesh_(mesh) {
281 }
282
283 /**
284 * \brief Computes the compared coordinate from a facet index.
285 * \param[in] f the index of the facet
286 * \return the coordinate at the center of facet \p f
287 */
288 28455080 double center(index_t f) const {
289 double result = 0.0;
290
4/4
✓ Branch 0 taken 7404 times.
✓ Branch 1 taken 14220136 times.
✓ Branch 2 taken 14220136 times.
✓ Branch 3 taken 7404 times.
28469888 double s = 1.0 / double(mesh_.facets.nb_vertices(f));
291
2/2
✓ Branch 0 taken 42690024 times.
✓ Branch 1 taken 14227540 times.
113835128 for(index_t c: mesh_.facets.corners(f)) {
292 85380048 result += s*mesh_.vertices.point_ptr(
293 mesh_.facet_corners.vertex(c)
294 85380048 )[COORD];
295 }
296 28455080 return result;
297 }
298
299 private:
300 const MESH& mesh_;
301 };
302
303 /**
304 * \brief The generic comparator class for Hilbert facet
305 * ordering.
306 * \tparam COORD the coordinate to compare
307 * \tparam UP if true, use direct order, else use reverse order
308 * \tparam MESH the class that represents meshes
309 */
310 template <int COORD, bool UP, class MESH>
311 struct Hilbert_fcmp {
312 };
313
314 /**
315 * \brief Specialization (UP=true) of the generic comparator class
316 * for Hilbert vertex ordering.
317 * \see Hilbert_vcmp
318 * \tparam COORD the coordinate to compare
319 * \tparam MESH the class that represents meshes
320 */
321 template <int COORD, class MESH>
322 class Hilbert_fcmp<COORD, true, MESH> : public Base_fcmp<COORD, MESH> {
323 public:
324 /**
325 * \brief Constructs a new Hilbert_fcmp.
326 * \param[in] mesh the mesh in which the compared
327 * facets reside.
328 */
329 Hilbert_fcmp(const MESH& mesh) :
330 Base_fcmp<COORD, MESH>(mesh) {
331 }
332
333 /**
334 * \brief Compares two facets.
335 * \param[in] f1 index of the first facet to compare
336 * \param[in] f2 index of the second facet to compare
337 * \return true if facet \p f1 is before facet \p f2,
338 * false otherwise.
339 */
340 bool operator() (index_t f1, index_t f2) {
341 112020 return this->center(f1) < this->center(f2);
342 }
343 };
344
345 /**
346 * \brief Specialization (UP=false) of the generic comparator class
347 * for Hilbert vertex ordering.
348 * \see Hilbert_vcmp
349 * \tparam COORD the coordinate to compare
350 * \tparam MESH the class that represents meshes
351 */
352 template <int COORD, class MESH>
353 class Hilbert_fcmp<COORD, false, MESH> : public Base_fcmp<COORD, MESH> {
354 public:
355 /**
356 * \brief Constructs a new Hilbert_fcmp.
357 * \param[in] mesh the mesh in which the compared
358 * facets reside.
359 */
360 Hilbert_fcmp(const MESH& mesh) :
361 Base_fcmp<COORD, MESH>(mesh) {
362 }
363
364 /**
365 * \brief Compares two facets.
366 * \param[in] f1 index of the first facet to compare
367 * \param[in] f2 index of the second facet to compare
368 * \return true if facet \p f1 is before facet \p f2,
369 * false otherwise.
370 */
371 bool operator() (index_t f1, index_t f2) {
372 142946 return this->center(f1) > this->center(f2);
373 }
374 };
375
376 /**
377 * \brief Comparator class for Morton facet
378 * ordering.
379 * \tparam COORD the coordinate to compare
380 * \tparam UP ignored in Morton order
381 * \tparam MESH the class that represents meshes
382 */
383 template <int COORD, bool UP, class MESH>
384 class Morton_fcmp : public Base_fcmp<COORD, MESH> {
385 public:
386 /**
387 * \brief Constructs a new Morton_fcmp.
388 * \param[in] mesh the mesh in which the compared
389 * facets reside.
390 */
391 Morton_fcmp(const MESH& mesh) :
392 Base_fcmp<COORD, MESH>(mesh) {
393 }
394
395 /**
396 * \brief Compares two facets.
397 * \param[in] f1 index of the first facet to compare
398 * \param[in] f2 index of the second facet to compare
399 * \return true if facet \p f1 is before facet \p f2,
400 * false otherwise.
401 */
402 bool operator() (index_t f1, index_t f2) {
403 6858804 return this->center(f1) < this->center(f2);
404 }
405 };
406
407 /************************************************************************/
408
409 /**
410 * \brief Base class for tetrahedra ordering.
411 * \tparam COORD the coordinate to compare
412 * \tparam MESH the class that represents meshes
413 */
414 template <int COORD, class MESH>
415 class Base_tcmp {
416 public:
417 /**
418 * \brief Constructs a new Base_tcmp.
419 * \param[in] mesh the mesh in which the compared
420 * tetrahedra reside.
421 */
422 2044 Base_tcmp(const MESH& mesh) :
423 2044 mesh_(mesh) {
424 }
425
426 /**
427 * \brief Computes the compared coordinate from a tetra index.
428 * \param[in] t the index of the tetra
429 * \return the coordinate at the center of tetra \p f
430 */
431 135904 double center(index_t t) const {
432 double result = 0.0;
433
2/2
✓ Branch 0 taken 271808 times.
✓ Branch 1 taken 67952 times.
679520 for(index_t lv = 0; lv < 4; ++lv) {
434
1/2
✓ Branch 0 taken 271808 times.
✗ Branch 1 not taken.
543616 result += mesh_.vertices.point_ptr(
435 mesh_.cells.vertex(t, lv)
436 543616 )[COORD];
437 }
438 135904 return result;
439 }
440
441 private:
442 const MESH& mesh_;
443 };
444
445 /**
446 * \brief The generic comparator class for Hilbert tetra
447 * ordering.
448 * \tparam COORD the coordinate to compare
449 * \tparam UP if true, use direct order, else use reverse order
450 * \tparam MESH the class that represents meshes
451 */
452 template <int COORD, bool UP, class MESH>
453 struct Hilbert_tcmp {
454 };
455
456 /**
457 * \brief Specialization (UP=true) of the generic comparator class
458 * for Hilbert tetra ordering.
459 * \see Hilbert_tcmp
460 * \tparam COORD the coordinate to compare
461 * \tparam MESH the class that represents meshes
462 */
463 template <int COORD, class MESH>
464 class Hilbert_tcmp<COORD, true, MESH> : public Base_tcmp<COORD, MESH> {
465 public:
466 /**
467 * \brief Constructs a new Hilbert_tcmp.
468 * \param[in] mesh the mesh in which the compared
469 * tetrahedra reside.
470 */
471 Hilbert_tcmp(const MESH& mesh) :
472 Base_tcmp<COORD, MESH>(mesh) {
473 }
474
475 /**
476 * \brief Compares two tetrahedra.
477 * \param[in] t1 index of the first tetra to compare
478 * \param[in] t2 index of the second tetra to compare
479 * \return true if tetra \p t1 is before tetra \p t2,
480 * false otherwise.
481 */
482 bool operator() (index_t t1, index_t t2) {
483 14212 return this->center(t1) < this->center(t2);
484 }
485 };
486
487 /**
488 * \brief Specialization (UP=false) of the generic comparator class
489 * for Hilbert tetra ordering.
490 * \see Hilbert_tcmp
491 * \tparam COORD the coordinate to compare
492 * \tparam MESH the class that represents meshes
493 */
494 template <int COORD, class MESH>
495 class Hilbert_tcmp<COORD, false, MESH> : public Base_tcmp<COORD, MESH> {
496 public:
497 /**
498 * \brief Constructs a new Hilbert_tcmp.
499 * \param[in] mesh the mesh in which the compared
500 * tetrahedra reside.
501 */
502 Hilbert_tcmp(const MESH& mesh) :
503 Base_tcmp<COORD, MESH>(mesh) {
504 }
505
506 /**
507 * \brief Compares two tetrahedra.
508 * \param[in] t1 index of the first tetra to compare
509 * \param[in] t2 index of the second tetra to compare
510 * \return true if tetra \p t1 is before tetra \p t2,
511 * false otherwise.
512 */
513 bool operator() (index_t t1, index_t t2) {
514 19764 return this->center(t1) > this->center(t2);
515 }
516 };
517
518 /**
519 * \brief Comparator class for Morton tetra
520 * ordering.
521 * \tparam COORD the coordinate to compare
522 * \tparam UP ignored in Morton order
523 * \tparam MESH the class that represents meshes
524 */
525 template <int COORD, bool UP, class MESH>
526 class Morton_tcmp : public Base_tcmp<COORD, MESH> {
527 public:
528 /**
529 * \brief Constructs a new Morton_tcmp.
530 * \param[in] mesh the mesh in which the compared
531 * tetrahedra reside.
532 */
533 Morton_tcmp(const MESH& mesh) :
534 Base_tcmp<COORD, MESH>(mesh) {
535 }
536
537 /**
538 * \brief Compares two tetrahedra.
539 * \param[in] t1 index of the first tetra to compare
540 * \param[in] t2 index of the second tetra to compare
541 * \return true if tetra \p t1 is before tetra \p t2,
542 * false otherwise.
543 */
544 bool operator() (index_t t1, index_t t2) {
545 return this->center(t1) < this->center(t2);
546 }
547 };
548
549 /************************************************************************/
550
551 /**
552 * \brief Base class for cells ordering.
553 * \tparam COORD the coordinate to compare
554 * \tparam MESH the class that represents meshes
555 */
556 template <int COORD, class MESH>
557 class Base_ccmp {
558 public:
559 /**
560 * \brief Constructs a new Base_ccmp.
561 * \param[in] mesh the mesh in which the compared
562 * cells reside.
563 */
564 Base_ccmp(const MESH& mesh) :
565 mesh_(mesh) {
566 }
567
568 /**
569 * \brief Computes the compared coordinate from a cell index.
570 * \param[in] c the index of the cell
571 * \return the coordinate at the center of cell \p c
572 */
573 double center(index_t c) const {
574 double result = 0.0;
575 for(index_t lv = 0; lv < mesh_.cells.nb_vertices(c); ++lv) {
576 result += mesh_.vertices.point_ptr(
577 mesh_.cells.vertex(c, lv)
578 )[COORD];
579 }
580 return result / double(mesh_.cells.nb_vertices(c));
581 }
582
583 private:
584 const MESH& mesh_;
585 };
586
587 /**
588 * \brief The generic comparator class for Hilbert cell
589 * ordering.
590 * \tparam COORD the coordinate to compare
591 * \tparam UP if true, use direct order, else use reverse order
592 * \tparam MESH the class that represents meshes
593 */
594 template <int COORD, bool UP, class MESH>
595 struct Hilbert_ccmp {
596 };
597
598 /**
599 * \brief Specialization (UP=true) of the generic comparator class
600 * for Hilbert cell ordering.
601 * \see Hilbert_ccmp
602 * \tparam COORD the coordinate to compare
603 * \tparam MESH the class that represents meshes
604 */
605 template <int COORD, class MESH>
606 class Hilbert_ccmp<COORD, true, MESH> : public Base_ccmp<COORD, MESH> {
607 public:
608 /**
609 * \brief Constructs a new Hilbert_ccmp.
610 * \param[in] mesh the mesh in which the compared
611 * cells reside.
612 */
613 Hilbert_ccmp(const MESH& mesh) :
614 Base_ccmp<COORD, MESH>(mesh) {
615 }
616
617 /**
618 * \brief Compares two cells
619 * \param[in] c1 index of the first cell to compare
620 * \param[in] c2 index of the second cell to compare
621 * \return true if cell \p c1 is before cell \p c2,
622 * false otherwise.
623 */
624 bool operator() (index_t c1, index_t c2) {
625 return this->center(c1) < this->center(c2);
626 }
627 };
628
629 /**
630 * \brief Specialization (UP=false) of the generic comparator class
631 * for Hilbert cell ordering.
632 * \see Hilbert_ccmp
633 * \tparam COORD the coordinate to compare
634 * \tparam MESH the class that represents meshes
635 */
636 template <int COORD, class MESH>
637 class Hilbert_ccmp<COORD, false, MESH> : public Base_ccmp<COORD, MESH> {
638 public:
639 /**
640 * \brief Constructs a new Hilbert_ccmp.
641 * \param[in] mesh the mesh in which the compared
642 * tetrahedra reside.
643 */
644 Hilbert_ccmp(const MESH& mesh) :
645 Base_ccmp<COORD, MESH>(mesh) {
646 }
647
648 /**
649 * \brief Compares two cells.
650 * \param[in] c1 index of the first cell to compare
651 * \param[in] c2 index of the second cell to compare
652 * \return true if cell \p c1 is before cell \p c2,
653 * false otherwise.
654 */
655 bool operator() (index_t c1, index_t c2) {
656 return this->center(c1) > this->center(c2);
657 }
658 };
659
660 /**
661 * \brief Comparator class for Morton cell
662 * ordering.
663 * \tparam COORD the coordinate to compare
664 * \tparam UP ignored in Morton order
665 * \tparam MESH the class that represents meshes
666 */
667 template <int COORD, bool UP, class MESH>
668 class Morton_ccmp : public Base_ccmp<COORD, MESH> {
669 public:
670 /**
671 * \brief Constructs a new Morton_ccmp.
672 * \param[in] mesh the mesh in which the compared
673 * cells reside.
674 */
675 Morton_ccmp(const MESH& mesh) :
676 Base_ccmp<COORD, MESH>(mesh) {
677 }
678
679 /**
680 * \brief Compares two tetrahedra.
681 * \param[in] c1 index of the first cell to compare
682 * \param[in] c2 index of the second cell to compare
683 * \return true if cell \p c1 is before cell \p c2,
684 * false otherwise.
685 */
686 bool operator() (index_t c1, index_t c2) {
687 return this->center(c1) < this->center(c2);
688 }
689 };
690
691 #endif
692
693 /************************************************************************/
694
695 /**
696 * \brief Generic class for sorting arbitrary elements in
697 * Hilbert and Morton orders in 3d.
698 * \details The implementation is inspired by:
699 * - Christophe Delage and Olivier Devillers. Spatial Sorting.
700 * In CGAL User and Reference Manual. CGAL Editorial Board,
701 * 3.9 edition, 2011
702 * \tparam CMP the comparator class for ordering the elements. CMP
703 * is itself a template parameterized by~:
704 * - COORD the coordinate along which elements should be
705 * sorted
706 * - UP a boolean that indicates whether direct or reverse
707 * order should be used
708 * - MESH the class that represents meshes
709 * \tparam MESH the class that represents meshes
710 */
711 template <template <int COORD, bool UP, class MESH> class CMP, class MESH>
712 struct HilbertSort3d {
713
714 /**
715 * \brief Low-level recursive spatial sorting function
716 * \details This function is recursive
717 * \param[in] M the mesh in which the elements reside
718 * \param[in] begin an iterator that points to the
719 * first element of the sequence
720 * \param[in] end an iterator that points one position past the
721 * last element of the sequence
722 * \param[in] limit subsequences smaller than limit are left unsorted
723 * \tparam COORDX the first coordinate, can be 0,1 or 2. The second
724 * and third coordinates are COORDX+1 modulo 3 and COORDX+2 modulo 3
725 * respectively
726 * \tparam UPX whether ordering along the first coordinate
727 * is direct or inverse
728 * \tparam UPY whether ordering along the second coordinate
729 * is direct or inverse
730 * \tparam UPZ whether ordering along the third coordinate
731 * is direct or inverse
732 */
733 template <int COORDX, bool UPX, bool UPY, bool UPZ, class IT>
734
2/2
✓ Branch 0 taken 514626 times.
✓ Branch 1 taken 73470 times.
1176192 static void sort(
735 const MESH& M, IT begin, IT end, index_t limit = 1
736 ) {
737 const int COORDY = (COORDX + 1) % 3, COORDZ = (COORDY + 1) % 3;
738
2/2
✓ Branch 0 taken 514626 times.
✓ Branch 1 taken 73470 times.
1176192 if(end - begin <= signed_index_t(limit)) {
739 1029252 return;
740 }
741 146940 IT m0 = begin, m8 = end;
742 146940 IT m4 = reorder_split(m0, m8, CMP<COORDX, UPX, MESH>(M));
743 146940 IT m2 = reorder_split(m0, m4, CMP<COORDY, UPY, MESH>(M));
744 146940 IT m1 = reorder_split(m0, m2, CMP<COORDZ, UPZ, MESH>(M));
745 146940 IT m3 = reorder_split(m2, m4, CMP<COORDZ, !UPZ, MESH>(M));
746 146940 IT m6 = reorder_split(m4, m8, CMP<COORDY, !UPY, MESH>(M));
747 146940 IT m5 = reorder_split(m4, m6, CMP<COORDZ, UPZ, MESH>(M));
748 146940 IT m7 = reorder_split(m6, m8, CMP<COORDZ, !UPZ, MESH>(M));
749 146940 sort<COORDZ, UPZ, UPX, UPY>(M, m0, m1);
750 146940 sort<COORDY, UPY, UPZ, UPX>(M, m1, m2);
751 146940 sort<COORDY, UPY, UPZ, UPX>(M, m2, m3);
752 146940 sort<COORDX, UPX, !UPY, !UPZ>(M, m3, m4);
753 146940 sort<COORDX, UPX, !UPY, !UPZ>(M, m4, m5);
754 146940 sort<COORDY, !UPY, UPZ, !UPX>(M, m5, m6);
755 146940 sort<COORDY, !UPY, UPZ, !UPX>(M, m6, m7);
756 146940 sort<COORDZ, !UPZ, !UPX, UPY>(M, m7, m8);
757 }
758
759 /**
760 * \brief Sorts a sequence of elements spatially.
761 * \details This function does an indirect sort,
762 * in the sense that a sequence
763 * of indices that refer to the elements is sorted.
764 * This function uses a multithreaded implementation.
765 * \param[in] M the mesh in which the elements to sort reside
766 * \param[in] b an iterator to the first index to be sorted
767 * \param[in] e an iterator one position past the last index
768 * to be sorted
769 * \param[in] limit subsequences smaller than limit are left unsorted
770 */
771 238 HilbertSort3d(
772 const MESH& M,
773 vector<index_t>::iterator b,
774 vector<index_t>::iterator e,
775 index_t limit = 1
776 ) :
777 238 M_(M)
778 {
779 geo_debug_assert(e >= b);
780 238 geo_cite_with_info(
781 "WEB:SpatialSorting",
782 "The implementation of spatial sort is inspired by "
783 "the use of \\verb|std::nth_element()| and the recursive"
784 " template in the spatial sort package of CGAL"
785 );
786
787 // If the sequence is smaller than the limit, skip it
788
1/2
✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
238 if(index_t(e - b) <= limit) {
789 return;
790 }
791
792 // If the sequence is smaller than 1024, use sequential sorting
793
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 31 times.
238 if(index_t(e - b) < 1024) {
794 176 sort<0, false, false, false>(M_, b, e);
795 176 return;
796 }
797
798 // Parallel sorting (2 then 4 then 8 sorts in parallel)
799
800 // Unfortunately we cannot access consts/constexprs for template
801 // arguments in lambdas in all compilers (gcc/clang OK but not
802 // MSVC) so I'm using macros here (it is ugly, but it is not a
803 // big drama), and I prefer that instead of hardwired constants
804 // that would make the code more difficult to read.
805
806 # define COORDX 0
807 # define COORDY 1
808 # define COORDZ 2
809 # define UPX false
810 # define UPY false
811 # define UPZ false
812
813 62 m0_ = b;
814 62 m8_ = e;
815
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
62 m4_ = reorder_split(m0_, m8_, CMP<COORDX, UPX, MESH>(M));
816
817
818
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 parallel(
819
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { m2_ = reorder_split(m0_, m4_, CMP<COORDY, UPY, MESH>(M_)); },
820
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
93 [this]() { m6_ = reorder_split(m4_, m8_, CMP<COORDY, !UPY, MESH>(M_)); }
821 );
822
823
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 parallel(
824
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { m1_ = reorder_split(m0_, m2_, CMP<COORDZ, UPZ, MESH>(M_)); },
825
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { m3_ = reorder_split(m2_, m4_, CMP<COORDZ, !UPZ, MESH>(M_)); },
826
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { m5_ = reorder_split(m4_, m6_, CMP<COORDZ, UPZ, MESH>(M_)); },
827
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
93 [this]() { m7_ = reorder_split(m6_, m8_, CMP<COORDZ, !UPZ, MESH>(M_)); }
828 );
829
830
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 parallel(
831
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDZ, UPZ, UPX, UPY>(M_, m0_, m1_); },
832
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDY, UPY, UPZ, UPX>(M_, m1_, m2_); },
833
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDY, UPY, UPZ, UPX>(M_, m2_, m3_); },
834
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDX, UPX, !UPY, !UPZ>(M_, m3_, m4_); },
835
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDX, UPX, !UPY, !UPZ>(M_, m4_, m5_); },
836
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDY, !UPY, UPZ, !UPX>(M_, m5_, m6_); },
837
1/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 [this]() { sort<COORDY, !UPY, UPZ, !UPX>(M_, m6_, m7_); },
838 93 [this]() { sort<COORDZ, !UPZ, !UPX, UPY>(M_, m7_, m8_); }
839 );
840
841 # undef COORDX
842 # undef COORDY
843 # undef COORDZ
844 # undef UPX
845 # undef UPY
846 # undef UPZ
847
848 }
849
850 private:
851 const MESH& M_;
852 vector<index_t>::iterator
853 m0_, m1_, m2_, m3_, m4_, m5_, m6_, m7_, m8_;
854 };
855
856 /************************************************************************/
857
858 /**
859 * \brief Generic class for sorting arbitrary elements in
860 * Hilbert and Morton orders in 3d.
861 * \details The implementation is inspired by:
862 * - Christophe Delage and Olivier Devillers. Spatial Sorting.
863 * In CGAL User and Reference Manual. CGAL Editorial Board,
864 * 3.9 edition, 2011
865 * \tparam CMP the comparator class for ordering the elements. CMP
866 * is itself a template parameterized by~:
867 * - COORD the coordinate along which elements should be
868 * sorted
869 * - UP a boolean that indicates whether direct or reverse
870 * order should be used
871 * - MESH the class that represents meshes
872 * \tparam MESH the class that represents meshes
873 */
874 template <template <int COORD, bool UP, class MESH> class CMP, class MESH>
875 struct HilbertSort2d {
876
877 /**
878 * \brief Low-level recursive spatial sorting function
879 * \details This function is recursive
880 * \param[in] M the mesh in which the elements reside
881 * \param[in] begin an iterator that points to the
882 * first element of the sequence
883 * \param[in] end an iterator that points one position past the
884 * last element of the sequence
885 * \param[in] limit subsequences smaller than limit are left unsorted
886 * \tparam COORDX the first coordinate, can be 0,1 or 2. The second
887 * coordinate is COORDX+1 modulo 2.
888 * \tparam UPX whether ordering along the first coordinate
889 * is direct or inverse
890 * \tparam UPY whether ordering along the second coordinate
891 * is direct or inverse
892 */
893 template <int COORDX, bool UPX, bool UPY, class IT>
894
2/2
✓ Branch 0 taken 340737 times.
✓ Branch 1 taken 113556 times.
908586 static void sort(
895 const MESH& M, IT begin, IT end, index_t limit = 1
896 ) {
897 const int COORDY = (COORDX + 1) % 2;
898
2/2
✓ Branch 0 taken 340737 times.
✓ Branch 1 taken 113556 times.
908586 if(end - begin <= signed_index_t(limit)) {
899 681474 return;
900 }
901 227112 IT m0 = begin, m4 = end;
902
903 227112 IT m2 = reorder_split (m0, m4, CMP<COORDX, UPX, MESH>(M));
904 227112 IT m1 = reorder_split (m0, m2, CMP<COORDY, UPY, MESH>(M));
905 227112 IT m3 = reorder_split (m2, m4, CMP<COORDY, !UPY, MESH>(M));
906
907 227112 sort<COORDY, UPY, UPX> (M, m0, m1);
908 227112 sort<COORDX, UPX, UPY> (M, m1, m2);
909 227112 sort<COORDX, UPX, UPY> (M, m2, m3);
910 227112 sort<COORDY,!UPY,!UPX> (M, m3, m4);
911 }
912
913 /**
914 * \brief Sorts a sequence of elements spatially.
915 * \details This function does an indirect sort,
916 * in the sense that a sequence
917 * of indices that refer to the elements is sorted.
918 * This function uses a multithreaded implementation.
919 * \param[in] M the mesh in which the elements to sort reside
920 * \param[in] b an iterator to the first index to be sorted
921 * \param[in] e an iterator one position past the last index
922 * to be sorted
923 * \param[in] limit subsequences smaller than limit are left unsorted
924 */
925 69 HilbertSort2d(
926 const MESH& M,
927 vector<index_t>::iterator b,
928 vector<index_t>::iterator e,
929 index_t limit = 1
930 ) :
931 69 M_(M)
932 {
933 geo_debug_assert(e > b);
934 69 geo_cite_with_info(
935 "WEB:SpatialSorting",
936 "The implementation of spatial sort is inspired by "
937 "the use of \\verb|std::nth_element()| and the recursive"
938 " template in the spatial sort package of CGAL"
939 );
940
941 // If the sequence is smaller than the limit, skip it
942
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 if(index_t(e - b) <= limit) {
943 return;
944 }
945 69 sort<0, false, false>(M_, b, e);
946 }
947 private:
948 const MESH& M_;
949 };
950
951 /************************************************************************/
952
953 #ifndef GEOGRAM_PSM
954
955 /**
956 * \brief Sorts the vertices of a mesh according to the Hilbert ordering.
957 * \details The function does not change the mesh, it computes instead
958 * the permutation. The permutation can then be reused to order other
959 * arrays that may depend on the order of the vertices in the mesh (i.e.
960 * attributes).
961 * \param[in] M the mesh where the vertices to be sorted reside
962 * \param[out] sorted_indices the permutation to be applied
963 to the vertices
964 */
965 10 void hilbert_vsort_3d(
966 const Mesh& M, vector<index_t>& sorted_indices
967 ) {
968 10 sorted_indices.resize(M.vertices.nb());
969
2/2
✓ Branch 0 taken 4489 times.
✓ Branch 1 taken 10 times.
4499 for(index_t i: M.vertices) {
970 4489 sorted_indices[i] = i;
971 }
972 10 HilbertSort3d<Hilbert_vcmp, Mesh>(
973 M, sorted_indices.begin(), sorted_indices.end()
974 10 );
975 10 }
976
977 /**
978 * \brief Sorts the facets of a mesh according to the Hilbert ordering.
979 * \details The function does not change the mesh, it computes instead
980 * the permutation. The permutation can then be reused to order other
981 * arrays that may depend on the order of the facets in the mesh (i.e.
982 * attributes).
983 * \param[in] M the mesh where the facets to be sorted reside
984 * \param[out] sorted_indices the permutation to be
985 * applied to the facets
986 */
987 10 void hilbert_fsort_3d(
988 const Mesh& M, vector<index_t>& sorted_indices
989 ) {
990 10 sorted_indices.resize(M.facets.nb());
991
2/2
✓ Branch 0 taken 8566 times.
✓ Branch 1 taken 10 times.
8576 for(index_t i: M.facets) {
992 8566 sorted_indices[i] = i;
993 }
994 10 HilbertSort3d<Hilbert_fcmp, Mesh>(
995 M, sorted_indices.begin(), sorted_indices.end()
996 10 );
997 10 }
998
999 /**
1000 * \brief Sorts the cells of a mesh according to the Hilbert ordering.
1001 * \details The function does not change the mesh, it computes instead
1002 * the permutation. The permutation can then be reused to order other
1003 * arrays that may depend on the order of the tets in the mesh (i.e.
1004 * attributes).
1005 * \param[in] M the mesh where the cells to be sorted reside
1006 * \param[out] sorted_indices the permutation to be applied to the tets
1007 */
1008 4 void hilbert_csort_3d(
1009 const Mesh& M, vector<index_t>& sorted_indices
1010 ) {
1011 4 sorted_indices.resize(M.cells.nb());
1012
2/2
✓ Branch 0 taken 1536 times.
✓ Branch 1 taken 4 times.
1540 for(index_t i: M.cells) {
1013 1536 sorted_indices[i] = i;
1014 }
1015
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(M.cells.are_simplices()) {
1016 4 HilbertSort3d<Hilbert_tcmp, Mesh>(
1017 M, sorted_indices.begin(), sorted_indices.end()
1018 4 );
1019 } else {
1020 HilbertSort3d<Hilbert_ccmp, Mesh>(
1021 M, sorted_indices.begin(), sorted_indices.end()
1022 );
1023 }
1024 4 }
1025
1026 /**
1027 * \brief Sorts the vertices of a mesh according to the Morton ordering.
1028 * \details The function does not change the mesh, it computes instead
1029 * the permutation. The permutation can then be reused to order other
1030 * arrays that may depend on the order of the vertices in the mesh (i.e.
1031 * attributes).
1032 * \param[in] M the mesh where the vertices to be sorted reside
1033 * \param[out] sorted_indices the permutation to be applied to the vertices
1034 */
1035 void morton_vsort_3d(
1036 const Mesh& M, vector<index_t>& sorted_indices
1037 ) {
1038 sorted_indices.resize(M.vertices.nb());
1039 for(index_t i: M.vertices) {
1040 sorted_indices[i] = i;
1041 }
1042 HilbertSort3d<Morton_vcmp, Mesh>(
1043 M, sorted_indices.begin(), sorted_indices.end()
1044 );
1045 }
1046
1047 /**
1048 * \brief Sorts the facets of a mesh according to the Morton ordering.
1049 * \details The function does not change the mesh, it computes instead
1050 * the permutation. The permutation can then be reused to order other
1051 * arrays that may depend on the order of the facets in the mesh (i.e.
1052 * attributes).
1053 * \param[in] M the mesh where the facets to be sorted reside
1054 * \param[out] sorted_indices the permutation to be applied to the facets
1055 */
1056 83 void morton_fsort_3d(
1057 const Mesh& M, vector<index_t>& sorted_indices
1058 ) {
1059 83 sorted_indices.resize(M.facets.nb());
1060
2/2
✓ Branch 0 taken 192734 times.
✓ Branch 1 taken 83 times.
192817 for(index_t i: M.facets) {
1061 192734 sorted_indices[i] = i;
1062 }
1063 83 HilbertSort3d<Morton_fcmp, Mesh>(
1064 M, sorted_indices.begin(), sorted_indices.end()
1065 83 );
1066 83 }
1067
1068 /**
1069 * \brief Sorts the cells of a mesh according to the Morton ordering.
1070 * \details The function does not change the mesh, it computes instead
1071 * the permutation. The permutation can then be reused to order other
1072 * arrays that may depend on the order of the tets in the mesh (i.e.
1073 * attributes).
1074 * \param[in] M the mesh where the tets to be sorted reside
1075 * \param[out] sorted_indices the permutation to be applied to the tets
1076 */
1077 void morton_csort_3d(
1078 const Mesh& M, vector<index_t>& sorted_indices
1079 ) {
1080 sorted_indices.resize(M.cells.nb());
1081 for(index_t i: M.cells) {
1082 sorted_indices[i] = i;
1083 }
1084 if(M.cells.are_simplices()) {
1085 HilbertSort3d<Morton_tcmp, Mesh>(
1086 M, sorted_indices.begin(), sorted_indices.end()
1087 );
1088 } else {
1089 HilbertSort3d<Morton_ccmp, Mesh>(
1090 M, sorted_indices.begin(), sorted_indices.end()
1091 );
1092 }
1093 }
1094
1095 #endif
1096
1097 /**
1098 * \brief Computes the BRIO order for a set of 3D points.
1099 * \details Implementation of compute_BRIO_order().
1100 * It is used to accelerate incremental insertion in Delaunay triangulation
1101 * \param[in] nb_vertices number of vertices to sort
1102 * \param[in] vertices pointer to the coordinates of the vertices
1103 * \param[in] stride number of doubles between two consecutive vertices
1104 * \param[in,out] sorted_indices indices to sort
1105 * \param[in] b iterator to the first index to sort
1106 * \param[in] e iterator one position past the last index to sort
1107 * \param[in] threshold minimum size of interval to be sorted
1108 * \param[in] ratio splitting ratio between current interval and
1109 * the rest to be sorted
1110 * \param[in,out] depth iteration depth
1111 * \param[out] levels if non-null, bounds of each level
1112 */
1113 81 void compute_BRIO_order_recursive(
1114 index_t nb_vertices, const double* vertices,
1115 index_t dimension, index_t stride,
1116 vector<index_t>& sorted_indices,
1117 vector<index_t>::iterator b,
1118 vector<index_t>::iterator e,
1119 index_t threshold,
1120 double ratio,
1121 index_t& depth,
1122 vector<index_t>* levels
1123 ) {
1124 geo_debug_assert(e > b);
1125
1126
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 41 times.
81 vector<index_t>::iterator m = b;
1127
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 41 times.
81 if(index_t(e - b) > threshold) {
1128 40 ++depth;
1129 40 m = b + signed_index_t(double(e - b) * ratio);
1130 40 compute_BRIO_order_recursive(
1131 nb_vertices, vertices,
1132 dimension, stride,
1133 sorted_indices, b, m,
1134 threshold, ratio, depth,
1135 levels
1136 );
1137 }
1138
1139 VertexMesh M(nb_vertices, vertices, stride);
1140
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 69 times.
81 if(dimension == 3) {
1141 12 HilbertSort3d<Hilbert_vcmp, VertexMesh>(
1142 M, m, e
1143 12 );
1144
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 } else if(dimension ==2) {
1145 69 HilbertSort2d<Hilbert_vcmp, VertexMesh>(
1146 M, m, e
1147 69 );
1148 } else {
1149 geo_assert_not_reached;
1150 }
1151
1152
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 72 times.
81 if(levels != nullptr) {
1153 9 levels->push_back(index_t(e - sorted_indices.begin()));
1154 }
1155 81 }
1156 }
1157
1158 /****************************************************************************/
1159
1160 namespace GEO {
1161
1162 #ifndef GEOGRAM_PSM
1163
1164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 void mesh_reorder(Mesh& M, MeshOrder order, MeshElementsFlags elements) {
1165
1166
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
10 geo_assert(M.vertices.dimension() >= 3);
1167
1168 // Step 1: reorder vertices
1169
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if((elements & MESH_VERTICES) != 0) {
1170 vector<index_t> sorted_indices;
1171
1/3
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
10 switch(order) {
1172 10 case MESH_ORDER_HILBERT:
1173
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 hilbert_vsort_3d(M, sorted_indices);
1174 break;
1175 case MESH_ORDER_MORTON:
1176 morton_vsort_3d(M, sorted_indices);
1177 break;
1178 }
1179
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 M.vertices.permute_elements(sorted_indices);
1180 }
1181
1182 // Step 2: reorder facets
1183
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 if(((elements & MESH_FACETS) != 0) && (M.facets.nb() != 0)) {
1184 vector<index_t> sorted_indices;
1185
1/3
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
10 switch(order) {
1186 10 case MESH_ORDER_HILBERT:
1187
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 hilbert_fsort_3d(M, sorted_indices);
1188 break;
1189 case MESH_ORDER_MORTON:
1190 morton_fsort_3d(M, sorted_indices);
1191 break;
1192 }
1193
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 M.facets.permute_elements(sorted_indices);
1194 }
1195
1196 // Step 3: reorder cells
1197
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 6 times.
10 if(((elements & MESH_CELLS) != 0) && (M.cells.nb() != 0)) {
1198 vector<index_t> sorted_indices;
1199
1/3
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
4 switch(order) {
1200 4 case MESH_ORDER_HILBERT:
1201
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 hilbert_csort_3d(M, sorted_indices);
1202 break;
1203 case MESH_ORDER_MORTON:
1204 morton_csort_3d(M, sorted_indices);
1205 break;
1206 }
1207
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 M.cells.permute_elements(sorted_indices);
1208 }
1209 10 }
1210
1211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 void compute_mesh_elements_spatial_order(
1212 const Mesh& M, MeshElementsFlags elements,
1213 vector<index_t>& sorted_indices, MeshOrder order
1214 ) {
1215
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
83 geo_assert(M.vertices.dimension() >= 3);
1216
1/5
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
83 switch(elements) {
1217 case MESH_VERTICES: {
1218 switch(order) {
1219 case MESH_ORDER_HILBERT:
1220 hilbert_vsort_3d(M, sorted_indices);
1221 break;
1222 case MESH_ORDER_MORTON:
1223 morton_vsort_3d(M, sorted_indices);
1224 break;
1225 }
1226 } break;
1227 83 case MESH_FACETS: {
1228
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
83 switch(order) {
1229 case MESH_ORDER_HILBERT:
1230 hilbert_fsort_3d(M, sorted_indices);
1231 break;
1232 83 case MESH_ORDER_MORTON:
1233 83 morton_fsort_3d(M, sorted_indices);
1234 83 break;
1235 }
1236 } break;
1237 case MESH_CELLS: {
1238 switch(order) {
1239 case MESH_ORDER_HILBERT:
1240 hilbert_csort_3d(M, sorted_indices);
1241 break;
1242 case MESH_ORDER_MORTON:
1243 morton_csort_3d(M, sorted_indices);
1244 break;
1245 }
1246 } break;
1247 case MESH_NONE:
1248 case MESH_EDGES:
1249 case MESH_ALL_ELEMENTS:
1250 case MESH_FACET_CORNERS:
1251 case MESH_CELL_CORNERS:
1252 case MESH_CELL_FACETS:
1253 case MESH_ALL_SUBELEMENTS:
1254 geo_assert_not_reached;
1255 }
1256 83 }
1257 #endif
1258
1259
1260 void compute_Hilbert_order(
1261 index_t total_nb_vertices, const double* vertices,
1262 vector<index_t>& sorted_indices,
1263 index_t first,
1264 index_t last,
1265 index_t dimension, index_t stride
1266 ) {
1267 geo_debug_assert(last > first);
1268 if(last - first <= 1) {
1269 return;
1270 }
1271 VertexMesh M(total_nb_vertices, vertices, stride);
1272 if(dimension == 3) {
1273 HilbertSort3d<Hilbert_vcmp, VertexMesh>(
1274 M, sorted_indices.begin() + int(first),
1275 sorted_indices.begin() + int(last)
1276 );
1277 } else if(dimension == 2) {
1278 HilbertSort2d<Hilbert_vcmp, VertexMesh>(
1279 M, sorted_indices.begin() + int(first),
1280 sorted_indices.begin() + int(last)
1281 );
1282 } else {
1283 geo_assert_not_reached;
1284 }
1285 }
1286
1287 41 void compute_BRIO_order(
1288 index_t nb_vertices, const double* vertices,
1289 vector<index_t>& sorted_indices,
1290 index_t dimension,
1291 index_t stride,
1292 index_t threshold,
1293 double ratio,
1294 vector<index_t>* levels
1295 ) {
1296
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 36 times.
41 if(levels != nullptr) {
1297 levels->clear();
1298 5 levels->push_back(0);
1299 }
1300 41 index_t depth = 0;
1301 41 sorted_indices.resize(nb_vertices);
1302
2/2
✓ Branch 0 taken 227234 times.
✓ Branch 1 taken 41 times.
227275 for(index_t i = 0; i < nb_vertices; ++i) {
1303 227234 sorted_indices[i] = i;
1304 }
1305
1306 41 GEO::random_shuffle(sorted_indices.begin(), sorted_indices.end());
1307
1308 41 compute_BRIO_order_recursive(
1309 nb_vertices, vertices,
1310 dimension, stride,
1311 sorted_indices,
1312 sorted_indices.begin(), sorted_indices.end(),
1313 threshold, ratio, depth, levels
1314 );
1315 41 }
1316 }
1317
1318 /**********************************************************************/
1319
1320 namespace {
1321 using namespace GEO;
1322
1323 // Same as in delaunay/periodic.cpp,
1324 // copied here for now because linker does not find
1325 // it under Android.
1326 int Periodic_translation[27][3] = {
1327 { 0, 0, 0}, //13 -> 0 + <-- zero displacement is first.
1328 { -1, -1, -1}, //0 -> 1 -
1329 { -1, -1, 0}, //1 -> 2 -
1330 { -1, -1, 1}, //2 -> 3 -
1331 { -1, 0, -1}, //3 -> 4 -
1332 { -1, 0, 0}, //4 -> 5 -
1333 { -1, 0, 1}, //5 -> 6 -
1334 { -1, 1, -1}, //6 -> 7 -
1335 { -1, 1, 0}, //7 -> 8 -
1336 { -1, 1, 1}, //8 -> 9 -
1337 { 0, -1, -1}, //9 -> 10 -
1338 { 0, -1, 0}, //10 -> 11 -
1339 { 0, -1, 1}, //11 -> 12 -
1340 { 0, 0, -1}, //12 -> 13 -
1341 // (zero displacement was there)
1342 { 0, 0, 1}, //14 -> 14 +
1343 { 0, 1, -1}, //15 -> 15 -
1344 { 0, 1, 0}, //16 -> 16 +
1345 { 0, 1, 1}, //17 -> 17 +
1346 { 1, -1, -1}, //18 -> 18 -
1347 { 1, -1, 0}, //19 -> 19 -
1348 { 1, -1, 1}, //20 -> 20 -
1349 { 1, 0, -1}, //21 -> 21 -
1350 { 1, 0, 0}, //22 -> 22 +
1351 { 1, 0, 1}, //23 -> 23 +
1352 { 1, 1, -1}, //24 -> 24 -
1353 { 1, 1, 0}, //25 -> 25 +
1354 { 1, 1, 1} //26 -> 26 +
1355 };
1356
1357
1358 /**
1359 * \details Exposes an interface compatible with the requirement
1360 * of Hilbert sort templates for a raw array of vertices.
1361 */
1362 class PeriodicVertexArray3d {
1363 public:
1364 /**
1365 * \brief Constructs a new PeriodicVertexArray.
1366 * \param[in] nb_vertices total number of vertices, including
1367 * the 27 copies.
1368 * \param[in] base address of the points.
1369 * \param[in] stride number of doubles between
1370 * two consecutive points.
1371 * \param[in] period the edge length of the periodic domain.
1372 */
1373 PeriodicVertexArray3d(
1374 index_t nb_vertices,
1375 const double* base, index_t stride,
1376 const vec3& period
1377 ) :
1378 base_(base),
1379 stride_(stride) {
1380 nb_vertices_ = nb_vertices;
1381 nb_real_vertices_ = nb_vertices_ / 27;
1382 geo_debug_assert(nb_vertices % 27 == 0);
1383
1384
1385 for(index_t i=0; i<27; ++i) {
1386 for(index_t j=0; j<3; ++j) {
1387 xlat_[i][j] = period[j] * double(Periodic_translation[i][j]);
1388 }
1389 }
1390 }
1391
1392 /**
1393 * \brief Gets a point coordinate by its index and coordinate.
1394 * \param[in] i the index of the point.
1395 * \param[in] coord the coordinate.
1396 * \return the value of the coordinate.
1397 */
1398 double point_coord(index_t i, index_t coord) const {
1399 index_t instance = i / nb_real_vertices_;
1400 i = i % nb_real_vertices_;
1401 return (base_ + i * stride_)[coord] + xlat_[instance][coord];
1402 }
1403
1404 private:
1405 const double* base_;
1406 index_t stride_;
1407 index_t nb_vertices_;
1408 index_t nb_real_vertices_;
1409 double xlat_[27][3];
1410 };
1411
1412 /**
1413 * \brief Exposes an interface compatible with the requirement
1414 * of Hilbert sort templates for a raw array of vertices.
1415 */
1416 class PeriodicVertexMesh3d {
1417 public:
1418 /**
1419 * \brief Constructs a new VertexMesh.
1420 * \param[in] nb_vertices total number of vertices, including
1421 * the 27 copies.
1422 * \param[in] base address of the points
1423 * \param[in] stride number of doubles between
1424 * two consecutive points
1425 */
1426 PeriodicVertexMesh3d(
1427 index_t nb_vertices,
1428 const double* base, index_t stride, const vec3& period
1429 ) : vertices(nb_vertices, base, stride, period) {
1430 }
1431
1432 PeriodicVertexArray3d vertices;
1433 };
1434
1435 /**
1436 * \brief Drop-in replacement of Hilbert_vcmp for
1437 * periodic vertices.
1438 * \details Needed because point_ptr() is not defined,
1439 * we need to use point_coord() instead.
1440 */
1441 template <int COORD, bool UP, class MESH>
1442 struct Hilbert_vcmp_periodic {
1443 };
1444
1445 /**
1446 * \brief Drop-in replacement of Hilbert_vcmp for
1447 * periodic vertices, specialization for UP.
1448 * \details Needed because point_ptr() is not defined,
1449 * we need to use point_coord() instead.
1450 */
1451 template <int COORD>
1452 struct Hilbert_vcmp_periodic<COORD, true, PeriodicVertexMesh3d> {
1453 Hilbert_vcmp_periodic(const PeriodicVertexMesh3d& mesh) :
1454 mesh_(mesh) {
1455 }
1456 bool operator() (index_t i1, index_t i2) {
1457 return
1458 mesh_.vertices.point_coord(i1,COORD) <
1459 mesh_.vertices.point_coord(i2,COORD);
1460 }
1461 const PeriodicVertexMesh3d& mesh_;
1462 };
1463
1464 /**
1465 * \brief Drop-in replacement of Hilbert_vcmp for
1466 * periodic vertices, specialization for !UP.
1467 * \details Needed because point_ptr() is not defined,
1468 * we need to use point_coord() instead.
1469 */
1470 template <int COORD>
1471 struct Hilbert_vcmp_periodic<COORD, false, PeriodicVertexMesh3d> {
1472 Hilbert_vcmp_periodic(const PeriodicVertexMesh3d& mesh) :
1473 mesh_(mesh) {
1474 }
1475 bool operator() (index_t i1, index_t i2) {
1476 return
1477 mesh_.vertices.point_coord(i1,COORD) >
1478 mesh_.vertices.point_coord(i2,COORD);
1479 }
1480 const PeriodicVertexMesh3d& mesh_;
1481 };
1482
1483 }
1484
1485 namespace GEO {
1486
1487 void Hilbert_sort_periodic(
1488 index_t nb_vertices, const double* vertices,
1489 vector<index_t>& sorted_indices,
1490 index_t dimension,
1491 index_t stride,
1492 vector<index_t>::iterator b,
1493 vector<index_t>::iterator e,
1494 const vec3& period
1495 ) {
1496 geo_assert(dimension == 3); // Only implemented for 3D.
1497 geo_argused(sorted_indices); // Accessed through b and e.
1498
1499 GEO::random_shuffle(b,e);
1500
1501 PeriodicVertexMesh3d M(nb_vertices, vertices, stride, period);
1502 HilbertSort3d<Hilbert_vcmp_periodic, PeriodicVertexMesh3d>(
1503 M, b, e
1504 );
1505 }
1506
1507 }
1508