GCC Code Coverage Report


Directory: ./
File: lib/geogram/delaunay/periodic_delaunay_3d.cpp
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 0 1760 0.0%
Functions: 0 119 0.0%
Branches: 0 2796 0.0%

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/delaunay/periodic_delaunay_3d.h>
41 #include <geogram/delaunay/cavity.h>
42
43 #include <geogram/mesh/mesh_reorder.h>
44 #include <geogram/numerics/predicates.h>
45 #include <geogram/basic/geometry.h>
46 #include <geogram/basic/stopwatch.h>
47 #include <geogram/basic/command_line.h>
48 #include <geogram/basic/permutation.h>
49 #include <geogram/basic/algorithm.h>
50 #include <geogram/bibliography/bibliography.h>
51
52 #include <stack>
53 #include <algorithm>
54
55 #include <mutex>
56 #include <condition_variable>
57
58 // ParallelDelaunayThread class, declared locally, has
59 // no out-of-line virtual functions. It is not a
60 // problem since they are only visible from this translation
61 // unit, but clang will complain.
62 #ifdef __clang__
63 #pragma GCC diagnostic ignored "-Wweak-vtables"
64 #endif
65
66 namespace {
67
68 using namespace GEO;
69
70 /**
71 * \brief Generates a random integer.
72 * \return a random integer between 0 and \p choices - 1
73 * \param [in] choices_in number of possible choices for the
74 * random variable (maximum value + 1)
75 * \details The function is thread-safe, and uses one seed
76 * per thread.
77 */
78 GEO::index_t thread_safe_random_(GEO::index_t choices_in) {
79 #ifdef GARGANTUA
80 typedef Numeric::int64 Int;
81 #else
82 typedef long int Int;
83 #endif
84 GEO::signed_index_t choices = signed_index_t(choices_in);
85 static thread_local Int randomseed = 1l ;
86 if (choices >= 714025l) {
87 Int newrandom = (randomseed * 1366l + 150889l) % 714025l;
88 randomseed = (newrandom * 1366l + 150889l) % 714025l;
89 newrandom = newrandom * (choices / 714025l) + randomseed;
90 if (newrandom >= choices) {
91 return GEO::index_t(newrandom - choices);
92 } else {
93 return GEO::index_t(newrandom);
94 }
95 } else {
96 randomseed = (randomseed * 1366l + 150889l) % 714025l;
97 return GEO::index_t(randomseed % choices);
98 }
99 }
100
101 /**
102 * \brief Generates a random integer between 0 and 3.
103 * \return a random integer between 0 and 3
104 * \details The function is thread-safe, and uses one seed
105 * per thread.
106 */
107 GEO::index_t thread_safe_random_4_() {
108 static thread_local long int randomseed = 1l ;
109 randomseed = (randomseed * 1366l + 150889l) % 714025l;
110 return GEO::index_t(randomseed % 4);
111 }
112
113 /**
114 * \brief Computes the number of bits set.
115 * \param[in] x an integer.
116 * \return the number of ones in the binary representation
117 * of the integer.
118 */
119 inline VBW::index_t pop_count(Numeric::uint32 x) {
120 #if defined(GEO_COMPILER_GCC_FAMILY)
121 return VBW::index_t(Numeric::uint32(__builtin_popcount(x)));
122 #elif defined(GEO_COMPILER_MSVC)
123 #if defined(_M_ARM64)
124 return VBW::index_t(_CountOneBits(x));
125 #else
126 return VBW::index_t(__popcnt(x));
127 #endif
128 #else
129 int result = 0;
130 for(int b=0; b<32; ++b) {
131 result += ((x & 1) != 0);
132 x >>= 1;
133 }
134 return VBW::index_t(result);
135 #endif
136 }
137
138 /************************************************************************/
139
140 // TODO: move these two functions to mesh_reorder.h
141
142 void compute_BRIO_order_periodic_recursive(
143 GEO::index_t nb_vertices, const double* vertices,
144 GEO::index_t dimension, GEO::index_t stride,
145 vector<GEO::index_t>& sorted_indices,
146 vector<GEO::index_t>::iterator b,
147 vector<GEO::index_t>::iterator e,
148 const vec3& period,
149 GEO::index_t threshold,
150 double ratio,
151 GEO::index_t& depth,
152 vector<GEO::index_t>* levels
153 ) {
154 geo_debug_assert(e > b);
155
156 vector<GEO::index_t>::iterator m = b;
157 if(GEO::index_t(e - b) > threshold) {
158 ++depth;
159 m = b + int(double(e - b) * ratio);
160 compute_BRIO_order_periodic_recursive(
161 nb_vertices, vertices,
162 dimension, stride,
163 sorted_indices, b, m,
164 period,
165 threshold, ratio, depth,
166 levels
167 );
168 }
169
170 Hilbert_sort_periodic(
171 nb_vertices, vertices,
172 sorted_indices,
173 dimension,
174 stride,
175 m,
176 e,
177 period
178 );
179
180 if(levels != nullptr) {
181 levels->push_back(GEO::index_t(e - sorted_indices.begin()));
182 }
183 }
184
185 void compute_BRIO_order_periodic(
186 GEO::index_t nb_vertices, const double* vertices,
187 GEO::index_t dimension, GEO::index_t stride,
188 vector<GEO::index_t>& sorted_indices,
189 vector<GEO::index_t>::iterator first,
190 vector<GEO::index_t>::iterator last,
191 const vec3& period,
192 GEO::index_t threshold = 64,
193 double ratio = 0.125,
194 vector<GEO::index_t>* levels = nullptr
195 ) {
196 if(levels != nullptr) {
197 levels->clear();
198 levels->push_back(GEO::index_t(first - sorted_indices.begin()));
199 }
200 GEO::index_t depth = 0;
201 GEO::random_shuffle(first, last);
202
203 compute_BRIO_order_periodic_recursive(
204 nb_vertices, vertices,
205 dimension, stride,
206 sorted_indices,
207 first, last,
208 period, threshold, ratio, depth, levels
209 );
210 }
211
212 /************************************************************************/
213
214 void delaunay_citations() {
215 geo_cite_with_info(
216 "DBLP:journals/cj/Bowyer81",
217 "One of the two initial references to the algorithm, "
218 "discovered independently and simultaneously by Bowyer and Watson."
219 );
220 geo_cite_with_info(
221 "journals/cj/Watson81",
222 "One of the two initial references to the algorithm, "
223 "discovered independently and simultaneously by Bowyer and Watson."
224 );
225 geo_cite_with_info(
226 "DBLP:conf/compgeom/AmentaCR03",
227 "Using spatial sorting has a dramatic impact on the performances."
228 );
229 geo_cite_with_info(
230 "DBLP:journals/comgeo/FunkeMN05",
231 "Initializing \\verb|locate()| with a non-exact version "
232 " (structural filtering) gains (a bit of) performance."
233 );
234 geo_cite_with_info(
235 "DBLP:journals/comgeo/BoissonnatDPTY02",
236 "The idea of traversing the cavity from inside "
237 " used in GEOGRAM is inspired by the implementation of "
238 " \\verb|Delaunay_triangulation_3| in CGAL."
239 );
240 geo_cite_with_info(
241 "DBLP:conf/imr/Si06",
242 "The triangulation data structure used in GEOGRAM is inspired "
243 "by Tetgen."
244 );
245 geo_cite_with_info(
246 "DBLP:journals/ijfcs/DevillersPT02",
247 "Analysis of the different versions of the line walk algorithm "
248 " used by \\verb|locate()|."
249 );
250 }
251 }
252
253 /************************************************************************/
254
255 // These two functions are missing when compiling in PSM mode.
256 #ifdef GEOGRAM_PSM
257 namespace GEO {
258 namespace PCK {
259 inline Sign det_3d(const vec3& p0, const vec3& p1, const vec3& p2) {
260 return det_3d(p0.data(), p1.data(), p2.data());
261 }
262 inline Sign det_4d(
263 const vec4& p0, const vec4& p1, const vec4& p2, const vec4& p3
264 ) {
265 return det_4d(p0.data(), p1.data(), p2.data(), p3.data());
266 }
267 }
268 }
269 #endif
270
271 /************************************************************************/
272
273 namespace GEO {
274
275 /**
276 * \brief One of the threads of the multi-threaded
277 * 3d Delaunay implementation.
278 */
279 class PeriodicDelaunay3dThread : public GEO::Thread, public Periodic {
280 public:
281 friend class PeriodicDelaunay3d;
282
283 /**
284 * \brief Symbolic value for cell_status_[t] that
285 * indicates that no thread owns t.
286 */
287 static constexpr index_t NO_THREAD = CellStatusArray::FREE_CELL;
288
289 /**
290 * \brief Creates a new PeriodicDelaunay3dThread.
291 * \details Each PeriodicDelaunay3dThread has an affected working
292 * zone, i.e. a range of tetrahedra indices in which the
293 * thread is allowed to create tetrahedra.
294 * \param[in] master a pointer to the PeriodicDelaunay3d
295 * this thread belongs to
296 * \param[in] pool_begin first tetrahedron index of
297 * the working zone of this PeriodicDelaunay3dThread
298 * \param[in] pool_end one position past the last tetrahedron
299 * index of the working zone of this PeriodicDelaunay3dThread
300 */
301 PeriodicDelaunay3dThread(
302 PeriodicDelaunay3d* master,
303 index_t pool_begin,
304 index_t pool_end
305 ) :
306 master_(master),
307 periodic_(master->periodic_),
308 period_(master->period_),
309 cell_to_v_store_(master_->cell_to_v_store_),
310 cell_to_cell_store_(master_->cell_to_cell_store_),
311 cell_next_(master_->cell_next_),
312 cell_status_(master_->cell_status_),
313 has_empty_cells_(false) {
314
315 max_t_ = master_->cell_next_.size();
316
317 nb_vertices_ = master_->nb_vertices();
318 nb_vertices_non_periodic_ = master_->nb_vertices_non_periodic_;
319 vertices_ = master_->vertex_ptr(0);
320 weights_ = master_->weights_;
321 dimension_ = master_->dimension();
322 reorder_ = master_->reorder_.data();
323
324 b_hint_ = NO_TETRAHEDRON;
325 e_hint_ = NO_TETRAHEDRON;
326
327 nb_rollbacks_ = 0;
328 nb_failed_locate_ = 0;
329
330 set_pool(pool_begin, pool_end);
331 }
332
333 /**
334 * \brief Resets thread statistics
335 */
336 void reset_stats() {
337 nb_rollbacks_ = 0;
338 nb_failed_locate_ = 0;
339 }
340
341 /**
342 * \brief Initializes the pool of tetrahedra for this thread.
343 * \param[in] pool_begin first tetrahedron index of
344 * the working zone of this PeriodicDelaunay3dThread
345 * \param[in] pool_end one position past the last tetrahedron
346 * index of the working zone of this PeriodicDelaunay3dThread
347 */
348 void set_pool(index_t pool_begin, index_t pool_end) {
349 pool_begin_ = pool_begin;
350 pool_end_ = pool_end;
351 // Initialize free list in memory pool
352 first_free_ = pool_begin;
353 for(index_t t=pool_begin; t<pool_end-1; ++t) {
354 cell_next_[t] = t+1;
355 }
356 cell_next_[pool_end-1] = END_OF_LIST;
357 nb_free_ = pool_end - pool_begin;
358 memory_overflow_ = false;
359 work_begin_ = NO_INDEX;
360 work_rbegin_ = NO_INDEX;
361 finished_ = false;
362 direction_ = true;
363 #ifdef GEO_DEBUG
364 nb_acquired_tets_ = 0;
365 #endif
366 interfering_thread_ = NO_THREAD;
367 nb_tets_to_create_ = 0;
368 t_boundary_ = NO_TETRAHEDRON;
369 f_boundary_ = NO_INDEX;
370 used_tets_end_ = pool_begin;
371 }
372
373 /**
374 * \brief Tests whether this thread created empty cells.
375 * \retval true if this thread created empty cells.
376 * \retval false otherwise.
377 */
378 bool has_empty_cells() {
379 return has_empty_cells_;
380 }
381
382 /**
383 * \brief Picks a random tetrahedron in this thread's pool.
384 * \retval If no valid tet exists in this thread's pool,
385 * returns NO_TETRAHEDRON. It can be a real tetrahedron
386 * \retval Otherwise, returns a finite tetrahedon, an infinite
387 * tetrahedron or a tetrahedon in the free list. Caller needs to check.
388 */
389 index_t pick_random_tet() const {
390 // Shit happens [Forrest Gump]
391 if(used_tets_end_ == pool_begin_) {
392 return NO_TETRAHEDRON;
393 }
394 return pool_begin_ + thread_safe_random_(
395 used_tets_end_ - pool_begin_
396 );
397 }
398
399 /**
400 * \brief Gets the number of rollbacks.
401 * \return the number of rollbacks
402 * \details rollbacks occur whenever a point
403 * could not be inserted, due to interferences
404 * from other threads
405 */
406 index_t nb_rollbacks() const {
407 return nb_rollbacks_;
408 }
409
410 /**
411 * \brief Gets the number of failed locate() calls.
412 * \return the number of failed locate() calls
413 * \details locate() can fail when it cannot acquire
414 * the tetrahedra that are traversed, due to
415 * interferences from another thread.
416 */
417 index_t nb_failed_locate() const {
418 return nb_failed_locate_;
419 }
420
421 /**
422 * \brief Gets the number of tetrahedra traversed by
423 * the latest locate() invocation.
424 */
425 index_t nb_traversed_tets() const {
426 return nb_traversed_tets_;
427 }
428
429 /**
430 * \brief Sets the point index sequence that
431 * should be processed by this thread.
432 * \param[in] b index of the first point to insert
433 * \param[in] e one position past the index of the
434 * last point to insert
435 */
436 void set_work(index_t b, index_t e) {
437 work_begin_ = b;
438 // e is one position past the last point index
439 // to insert. Internally we store the last point index
440 // to insert (like rbegin in STL containers). This is
441 // because we manipulate the point sequence to insert
442 // from both ends.
443 work_rbegin_ = e-1;
444 // reorder_ may have changed if new vertices were
445 // inserted into it
446 reorder_ = master_->reorder_.data();
447 }
448
449 /**
450 * \brief Gets the number of remaining points to
451 * be inserted.
452 * \return the number of points to be inserted by
453 * this thread
454 */
455 index_t work_size() const {
456 if(work_begin_ == NO_INDEX && work_rbegin_ == NO_INDEX) {
457 return 0;
458 }
459 geo_debug_assert(work_begin_ != NO_INDEX);
460 geo_debug_assert(work_rbegin_ != NO_INDEX);
461 return std::max(work_rbegin_ - work_begin_ + 1, index_t(0));
462 }
463
464 /**
465 * \brief Gets the number of threads.
466 * \return the number of threads created by
467 * the master PeriodicDelaunay3d of this thread.
468 */
469 index_t nb_threads() const {
470 return index_t(master_->threads_.size());
471 }
472
473 /**
474 * \brief Gets a thread by index
475 * \pre t < nb_threads()
476 * \param[in] t index of the thread
477 * \return a poiner to the \p t th thread
478 */
479 PeriodicDelaunay3dThread* thread(index_t t) {
480 return static_cast<PeriodicDelaunay3dThread*>(
481 master_->threads_[t].get()
482 );
483 }
484
485 /**
486 * \brief Inserts the point sequence allocated to
487 * this thread.
488 * \details The point sequence was previously defined
489 * by set_work().
490 */
491 void run() override {
492 has_empty_cells_ = false;
493 finished_ = false;
494
495 if(work_begin_ == NO_INDEX || work_rbegin_ == NO_INDEX) {
496 return ;
497 }
498
499 memory_overflow_ = false;
500
501 // Current hint associated with b
502 b_hint_ = NO_TETRAHEDRON;
503
504 // Current hint associated with e
505 e_hint_ = NO_TETRAHEDRON;
506
507 // If true, insert in b->e order,
508 // else insert in e->b order
509 direction_ = true;
510
511 while(
512 work_rbegin_ >= work_begin_ &&
513 !memory_overflow_ &&
514 !has_empty_cells_ &&
515 !master_->has_empty_cells_
516 ) {
517 index_t v = direction_ ? work_begin_ : work_rbegin_ ;
518 index_t& hint = direction_ ? b_hint_ : e_hint_ ;
519
520 // Try to insert v and update hint
521 bool success = insert(reorder_[v],hint);
522
523 // Notify all threads that are waiting for
524 // this thread to release some tetrahedra.
525 send_event();
526
527 if(success) {
528 if(direction_) {
529 ++work_begin_;
530 } else {
531 --work_rbegin_;
532 }
533 } else {
534 ++nb_rollbacks_;
535 if(interfering_thread_ != NO_THREAD) {
536 if(id() < interfering_thread_) {
537 // If this thread has a higher priority than
538 // the one that interfered, wait for the
539 // interfering thread to release the tets that
540 // it holds (then the loop will retry to insert
541 // the same vertex).
542 wait_for_event(interfering_thread_);
543 } else {
544 // If this thread has a lower priority than
545 // the interfering thread, try inserting
546 // from the other end of the points sequence.
547 direction_ = !direction_;
548 }
549 }
550 }
551 }
552
553 if(has_empty_cells_) {
554 master_->has_empty_cells_ = true;
555 }
556
557 // Fix by Hiep Vu: wake up threads that potentially missed
558 // the previous wake ups.
559 mutex_.lock();
560 finished_ = true;
561 send_event();
562 mutex_.unlock();
563 }
564
565 /**
566 * \brief Symbolic constant for uninitialized hint.
567 * \details Locate functions can be accelerated by
568 * specifying a hint. This constant indicates that
569 * no hint is given.
570 */
571 static constexpr index_t NO_TETRAHEDRON = NO_INDEX;
572
573 /**
574 * \brief Symbolic value for a vertex of a
575 * tetrahedron that indicates a virtual tetrahedron.
576 * \details The three other vertices then correspond to a
577 * facet on the convex hull of the points.
578 */
579 static constexpr index_t VERTEX_AT_INFINITY = NO_INDEX;
580
581
582 /**
583 * \brief Maximum valid index for a tetrahedron.
584 * \return the maximum valid index for a tetrahedron
585 * This includes not only real tetrahedra, but also
586 * the virtual ones on the border, the conflict
587 * list and the free list.
588 */
589 index_t max_t() const {
590 return max_t_;
591 }
592
593 /**
594 * \brief Sets the maximum valid index for a tetrahedron.
595 * \details Needs to be called when starting the threads, and
596 * whenever memory allocation occured.
597 * \param[in] max_t the maximum valid index for a tetrahedron.
598 * This includes not only real tetrahedra,
599 * but also the virtual ones on the border, the conflict
600 * list and the free list.
601 */
602 void set_max_t(index_t max_t) {
603 max_t_ = max_t;
604 }
605
606 /**
607 * \brief Tests whether a given tetrahedron
608 * is a finite one.
609 * \details Infinite tetrahedra are the ones
610 * that are incident to the infinite vertex
611 * (index -1)
612 * \param[in] t the index of the tetrahedron
613 * \retval true if \p t is finite
614 * \retval false otherwise
615 */
616 bool tet_is_finite(index_t t) const {
617 return
618 cell_to_v_store_[4 * t] != NO_INDEX &&
619 cell_to_v_store_[4 * t + 1] != NO_INDEX &&
620 cell_to_v_store_[4 * t + 2] != NO_INDEX &&
621 cell_to_v_store_[4 * t + 3] != NO_INDEX ;
622 }
623
624 /**
625 * \brief Tests whether a tetrahedron is
626 * a real one.
627 * \details Real tetrahedra are incident to
628 * four user-specified vertices (there are also
629 * virtual tetrahedra that are incident to the
630 * vertex at infinity, with index -1)
631 * \param[in] t index of the tetrahedron
632 * \retval true if tetrahedron \p t is a real one
633 * \retval false otherwise
634 */
635 bool tet_is_real(index_t t) const {
636 return !tet_is_free(t) && tet_is_finite(t);
637 }
638
639 /**
640 * \brief Tests whether a tetrahedron is
641 * a real one and has at least a vertex in the core.
642 * \details Real tetrahedra are incident to
643 * four user-specified vertices (there are also
644 * virtual tetrahedra that are incident to the
645 * vertex at infinity, with index -1)
646 * \param[in] t index of the tetrahedron
647 * \retval true if tetrahedron \p t is a real one
648 * \retval false otherwise
649 */
650 bool tet_is_real_non_periodic(index_t t) const {
651 return !tet_is_free(t) && tet_is_finite(t) && (
652 finite_tet_vertex(t,0) < nb_vertices_non_periodic_ ||
653 finite_tet_vertex(t,1) < nb_vertices_non_periodic_ ||
654 finite_tet_vertex(t,2) < nb_vertices_non_periodic_ ||
655 finite_tet_vertex(t,3) < nb_vertices_non_periodic_ ) ;
656 }
657
658 /**
659 * \brief Tests whether a tetrahedron is
660 * in the free list.
661 * \details Deleted tetrahedra are recycled
662 * in a free list.
663 * \param[in] t index of the tetrahedron
664 * \retval true if tetrahedron \p t is in
665 * the free list
666 * \retval false otherwise
667 */
668 bool tet_is_free(index_t t) const {
669 return tet_is_in_list(t);
670 }
671
672 /**
673 * \brief Tests whether a tetrahedron is contained
674 * by a given linked list.
675 * \details Used for debugging purposes.
676 * \param[in] t the tetrahedron
677 * \param[in] first the first element of the list
678 * \retval true if \p t is contained in the list starting
679 * at \p first
680 * \retval false otherwise
681 */
682 bool tet_is_in_list(index_t t, index_t first) const {
683 for(
684 index_t cur = first; cur != END_OF_LIST;
685 cur = tet_next(cur)
686 ) {
687 if(cur == t) {
688 return true;
689 }
690 }
691 return false;
692 }
693
694
695 /**
696 * \brief Finds in the pointset a set of four non-coplanar
697 * points and creates a tetrahedron that connects them.
698 * \details This function is used to initiate the incremental
699 * Delaunay construction, it should be called only once.
700 * \retval the index of the created tetrahedron
701 * \retval NO_TETRAHEDRON if all points were coplanar
702 */
703 index_t create_first_tetrahedron() {
704 index_t iv0,iv1,iv2,iv3;
705 if(nb_vertices() < 4) {
706 return NO_TETRAHEDRON;
707 }
708
709 iv0 = 0;
710
711 iv1 = 1;
712 while(
713 iv1 < nb_vertices_non_periodic_ &&
714 PCK::points_are_identical_3d(
715 non_periodic_vertex_ptr(iv0),
716 non_periodic_vertex_ptr(iv1)
717 )
718 ) {
719 ++iv1;
720 }
721 if(iv1 == nb_vertices()) {
722 return NO_TETRAHEDRON;
723 }
724
725 iv2 = iv1 + 1;
726 while(
727 iv2 < nb_vertices_non_periodic_ &&
728 PCK::points_are_colinear_3d(
729 non_periodic_vertex_ptr(iv0),
730 non_periodic_vertex_ptr(iv1),
731 non_periodic_vertex_ptr(iv2)
732 )
733 ) {
734 ++iv2;
735 }
736 if(iv2 == nb_vertices()) {
737 return NO_TETRAHEDRON;
738 }
739
740 iv3 = iv2 + 1;
741 Sign s = ZERO;
742 while(
743 iv3 < nb_vertices_non_periodic_ &&
744 (s = PCK::orient_3d(
745 non_periodic_vertex_ptr(iv0),
746 non_periodic_vertex_ptr(iv1),
747 non_periodic_vertex_ptr(iv2),
748 non_periodic_vertex_ptr(iv3)
749 )) == ZERO
750 ) {
751 ++iv3;
752 }
753
754 if(iv3 == nb_vertices()) {
755 return NO_TETRAHEDRON;
756 }
757
758 geo_debug_assert(s != ZERO);
759
760 if(s == NEGATIVE) {
761 std::swap(iv2, iv3);
762 }
763
764 // Create the first tetrahedron
765 index_t t0 = new_tetrahedron(iv0, iv1, iv2, iv3);
766
767 // Create the first four virtual tetrahedra surrounding it
768 index_t t[4];
769 for(index_t f = 0; f < 4; ++f) {
770 // In reverse order since it is an adjacent tetrahedron
771 index_t v1 = tet_vertex(t0, tet_facet_vertex(f,2));
772 index_t v2 = tet_vertex(t0, tet_facet_vertex(f,1));
773 index_t v3 = tet_vertex(t0, tet_facet_vertex(f,0));
774 t[f] = new_tetrahedron(VERTEX_AT_INFINITY, v1, v2, v3);
775 }
776
777 // Connect the virtual tetrahedra to the real one
778 for(index_t f=0; f<4; ++f) {
779 set_tet_adjacent(t[f], 0, t0);
780 set_tet_adjacent(t0, f, t[f]);
781 }
782
783 // Interconnect the four virtual tetrahedra along their common
784 // faces
785 for(index_t f = 0; f < 4; ++f) {
786 // In reverse order since it is an adjacent tetrahedron
787 index_t lv1 = tet_facet_vertex(f,2);
788 index_t lv2 = tet_facet_vertex(f,1);
789 index_t lv3 = tet_facet_vertex(f,0);
790 set_tet_adjacent(t[f], 1, t[lv1]);
791 set_tet_adjacent(t[f], 2, t[lv2]);
792 set_tet_adjacent(t[f], 3, t[lv3]);
793 }
794
795 release_tets();
796
797 return t0;
798 }
799
800
801 /**
802 * \brief Creates a star of tetrahedra filling the conflict zone.
803 * \param[in] v the index of the point to be inserted
804 * \details This function is used when the Cavity computed
805 * when traversing the conflict zone is OK, that is to say
806 * when its array sizes were not exceeded.
807 * \return the index of one the newly created tetrahedron
808 */
809 index_t stellate_cavity(index_t v) {
810 index_t new_tet = NO_INDEX;
811
812 for(index_t f=0; f<cavity_.nb_facets(); ++f) {
813 index_t old_tet = cavity_.facet_tet(f);
814 index_t lf = cavity_.facet_facet(f);
815 index_t t_neigh = tet_adjacent(old_tet, lf);
816 index_t v1 = cavity_.facet_vertex(f,0);
817 index_t v2 = cavity_.facet_vertex(f,1);
818 index_t v3 = cavity_.facet_vertex(f,2);
819 new_tet = new_tetrahedron(v, v1, v2, v3);
820 set_tet_adjacent(new_tet, 0, t_neigh);
821 set_tet_adjacent(
822 t_neigh, find_tet_adjacent(t_neigh,old_tet), new_tet
823 );
824 cavity_.set_facet_tet(f, new_tet);
825 }
826
827 for(index_t f=0; f<cavity_.nb_facets(); ++f) {
828 new_tet = cavity_.facet_tet(f);
829 index_t neigh1, neigh2, neigh3;
830 cavity_.get_facet_neighbor_tets(f, neigh1, neigh2, neigh3);
831 set_tet_adjacent(new_tet, 1, neigh1);
832 set_tet_adjacent(new_tet, 2, neigh2);
833 set_tet_adjacent(new_tet, 3, neigh3);
834 }
835
836 return new_tet;
837 }
838
839
840 /**
841 * \brief Inserts a point in the triangulation.
842 * \param[in] v the index of the point to be inserted
843 * \param[in,out] hint the index of a tetrahedron as near as
844 * possible to \p v, or NO_TETRAHEDRON if unspecified. On
845 * exit, the index of one of the tetrahedra incident to
846 * point \p v
847 * \retval true if insertion was successful, that is, if there
848 * was no interference. This includes the situation where the
849 * point already exists (even if this does not create a new
850 * vertex)
851 * \retval false otherwise
852 */
853 bool insert(index_t v, index_t& hint) {
854
855 vec3 p = vertex(v);
856
857 Sign orient[4];
858 index_t t = locate(v,p,hint,orient);
859
860 // locate() may fail due to tets already owned by
861 // other threads.
862 if(t == NO_TETRAHEDRON) {
863 ++nb_failed_locate_;
864 geo_debug_assert(nb_acquired_tets_ == 0);
865 return false;
866 }
867
868 // At this point, t is a valid tetrahedron,
869 // and this thread acquired a lock on it.
870
871 // Test whether the point already exists in
872 // the triangulation. The point already exists
873 // if it's located on three faces of the
874 // tetrahedron returned by locate().
875 int nb_zero =
876 (orient[0] == ZERO) +
877 (orient[1] == ZERO) +
878 (orient[2] == ZERO) +
879 (orient[3] == ZERO) ;
880
881 if(nb_zero >= 3) {
882 release_tet(t);
883 return true;
884 }
885
886 geo_debug_assert(nb_acquired_tets_ == 1);
887
888 index_t t_bndry = NO_TETRAHEDRON;
889 index_t f_bndry = NO_INDEX;
890
891 vec4 p_lifted = lifted_vertex(v,p);
892
893 cavity_.clear();
894
895 bool ok = find_conflict_zone(v,p_lifted,t,t_bndry,f_bndry);
896
897 // When in multithreading mode, we cannot allocate memory
898 // dynamically and we use a fixed pool. If the fixed pool
899 // is full, then we exit the thread (and the missing points
900 // are inserted after, in sequential mode).
901 if(
902 nb_tets_to_create_ > nb_free_ &&
903 Process::is_running_threads()
904 ) {
905 memory_overflow_ = true;
906 ok = false;
907 }
908
909 if(!ok) {
910 // At this point, this thread did not successfully
911 // acquire all the tets in the conflict zone, so
912 // we need to rollback.
913 release_tets();
914 geo_debug_assert(nb_acquired_tets_ == 0);
915 return false;
916 }
917
918 // The conflict list can be empty if
919 // the triangulation is weighted and v is not visible
920 if(tets_to_delete_.size() == 0) {
921 release_tets();
922 geo_debug_assert(nb_acquired_tets_ == 0);
923 has_empty_cells_ = true;
924 return true;
925 }
926
927
928 geo_debug_assert(
929 nb_acquired_tets_ ==
930 tets_to_delete_.size() + tets_to_release_.size()
931 );
932
933 #ifdef GEO_DEBUG
934 // Sanity check: make sure this threads owns all the tets
935 // in conflict and their neighbors.
936 for(index_t i=0; i<tets_to_delete_.size(); ++i) {
937 index_t tdel = tets_to_delete_[i];
938 geo_debug_assert(owns_tet(tdel));
939 for(index_t lf=0; lf<4; ++lf) {
940 geo_debug_assert(tet_adjacent(tdel,lf) != NO_INDEX);
941 geo_debug_assert(owns_tet(tet_adjacent(tdel,lf)));
942 }
943 }
944 #endif
945 geo_debug_assert(owns_tet(t_bndry));
946 geo_debug_assert(owns_tet(tet_adjacent(t_bndry,f_bndry)));
947 geo_debug_assert(
948 !tet_is_marked_as_conflict(tet_adjacent(t_bndry,f_bndry))
949 );
950
951 // At this point, this thread owns all the tets in conflict and
952 // their neighbors, therefore no other thread can interfere, and
953 // we can update the triangulation.
954
955 index_t new_tet = NO_INDEX;
956 if(cavity_.OK()) {
957 new_tet = stellate_cavity(v);
958 } else {
959 new_tet = stellate_conflict_zone_iterative(v,t_bndry,f_bndry);
960 }
961
962 // Recycle the tetrahedra of the conflict zone.
963 for(index_t i=0; i<tets_to_delete_.size()-1; ++i) {
964 cell_next_[tets_to_delete_[i]] = tets_to_delete_[i+1];
965 }
966 cell_next_[tets_to_delete_[tets_to_delete_.size()-1]] =
967 first_free_;
968 first_free_ = tets_to_delete_[0];
969 nb_free_ += nb_tets_in_conflict();
970
971 // Reset deleted tet.
972 // This is needed because we update_v_to_cell() in
973 // a transient state.
974 // Note: update_v_to_cell() is overloaded here,
975 // with a check on nb_vertices_non_periodic_,
976 // this is why the VERTEX_OF_DELETED_TET (= -2)
977 // does not make everything crash.
978 for(index_t i=0; i<tets_to_delete_.size(); ++i) {
979 index_t tdel = tets_to_delete_[i];
980 set_tet_vertex(tdel, 0, VERTEX_OF_DELETED_TET);
981 set_tet_vertex(tdel, 1, VERTEX_OF_DELETED_TET);
982 set_tet_vertex(tdel, 2, VERTEX_OF_DELETED_TET);
983 set_tet_vertex(tdel, 3, VERTEX_OF_DELETED_TET);
984 }
985
986 // Return one of the newly created tets
987 hint=new_tet;
988 release_tets();
989 geo_debug_assert(nb_acquired_tets_ == 0);
990 return true;
991 }
992
993 /**
994 * \brief Determines the list of tetrahedra in conflict
995 * with a given point.
996 * \param[in] v the index of the point to be inserted
997 * \param[in] t the index of a tetrahedron that contains
998 * \p p, as returned by locate()
999 * \param[out] t_bndry a tetrahedron adjacent to the
1000 * boundary of the conflict zone
1001 * \param[out] f_bndry the facet along which t_bndry is
1002 * adjacent to the boundary of the conflict zone
1003 * The other tetrahedra are linked, and can be traversed
1004 * from \p first by using tet_next() until \p last or END_OF_LIST
1005 * is reached.
1006 * The conflict zone can be empty under two circumstances:
1007 * - the vertex \p v already exists in the triangulation
1008 * - the triangulation is weighted and \p v is not visible
1009 * in either cases, both \p first and \p last contain END_OF_LIST
1010 * \retval true if all the tetrahedra of the conflict zone and their
1011 * neighbors could be acquired by this thread
1012 * \retval false otherwise
1013 */
1014 bool find_conflict_zone(
1015 index_t v, const vec4& p, index_t t,
1016 index_t& t_bndry, index_t& f_bndry
1017 ) {
1018 nb_tets_to_create_ = 0;
1019
1020 geo_debug_assert(t != NO_TETRAHEDRON);
1021 geo_debug_assert(owns_tet(t));
1022
1023 // Pointer to the coordinates of the point to be inserted
1024 //const double* p = vertex_ptr(v);
1025
1026 // Weighted triangulations can have dangling
1027 // vertices. Such vertices p are characterized by
1028 // the fact that p is not in conflict with the
1029 // tetrahedron returned by locate().
1030 if(!tet_is_in_conflict(t,v,p)) {
1031 release_tet(t);
1032 return true;
1033 }
1034
1035 mark_tet_as_conflict(t);
1036
1037 // Sanity check: the vertex to be inserted should
1038 // not correspond to one of the vertices of t.
1039 geo_debug_assert(v != tet_vertex(t,0));
1040 geo_debug_assert(v != tet_vertex(t,1));
1041 geo_debug_assert(v != tet_vertex(t,2));
1042 geo_debug_assert(v != tet_vertex(t,3));
1043
1044 // Note: points on edges and on facets are
1045 // handled by the way tet_is_in_conflict()
1046 // is implemented, that naturally inserts
1047 // the correct tetrahedra in the conflict list.
1048
1049 // Determine the conflict list by greedy propagation from t.
1050 bool result = find_conflict_zone_iterative(v, p,t);
1051 t_bndry = t_boundary_;
1052 f_bndry = f_boundary_;
1053 return result;
1054 }
1055
1056
1057 /**
1058 * \brief This function is used to implement find_conflict_zone.
1059 * \details This function detects the neighbors of \p t that are
1060 * in the conflict zone and calls itself recursively on them.
1061 * \param[in] v_in the index of the point to be inserted
1062 * \param[in] p_in the point to be inserted
1063 * \param[in] t_in index of a tetrahedron in the conflict zone
1064 * \pre The tetrahedron \p t was alredy marked as
1065 * conflict (tet_is_in_list(t))
1066 */
1067 bool find_conflict_zone_iterative(
1068 index_t v_in, const vec4& p_in, index_t t_in
1069 ) {
1070 geo_debug_assert(owns_tet(t_in));
1071 S_.push_back(SFrame(t_in, v_in, p_in));
1072
1073 while(S_.size() != 0) {
1074 index_t t = S_.rbegin()->t;
1075 index_t v = S_.rbegin()->v;
1076 vec4 p = S_.rbegin()->p;
1077 S_.pop_back();
1078
1079 geo_debug_assert(owns_tet(t));
1080
1081 for(index_t lf = 0; lf < 4; ++lf) {
1082 index_t v2=v;
1083 vec4 p2=p;
1084
1085 index_t t2 = tet_adjacent(t, lf);
1086
1087 // If t2 is already owned by current thread, then
1088 // its status was previously determined.
1089 if(owns_tet(t2)) {
1090
1091 geo_debug_assert(
1092 tet_is_marked_as_conflict(t2) ==
1093 tet_is_in_conflict(t2,v2,p2)
1094 );
1095
1096 // If t2 is not in conflict list, then t has a facet
1097 // on the border of the conflict zone, and there is
1098 // a tet to create.
1099 if(!tet_is_marked_as_conflict(t2)) {
1100 ++nb_tets_to_create_;
1101 cavity_.new_facet(
1102 t, lf,
1103 tet_vertex(t, tet_facet_vertex(lf,0)),
1104 tet_vertex(t, tet_facet_vertex(lf,1)),
1105 tet_vertex(t, tet_facet_vertex(lf,2))
1106 );
1107 }
1108 continue;
1109 }
1110
1111 if(!acquire_tet(t2)) {
1112 S_.resize(0);
1113 return false;
1114 }
1115
1116 geo_debug_assert(owns_tet(t2));
1117
1118 if(!tet_is_in_conflict(t2,v2,p2)) {
1119 mark_tet_as_neighbor(t2);
1120 // If t2 is not in conflict list, then t has a facet
1121 // on the border of the conflict zone, and there is
1122 // a tet to create.
1123 ++nb_tets_to_create_;
1124 } else {
1125 mark_tet_as_conflict(t2);
1126 geo_debug_assert(owns_tet(t2));
1127 S_.push_back(SFrame(t2,v2,p2));
1128 continue;
1129 }
1130
1131 // At this point, t is in conflict
1132 // and t2 is not in conflict.
1133 // We keep a reference to a tet on the boundary
1134 t_boundary_ = t;
1135 f_boundary_ = lf;
1136 cavity_.new_facet(
1137 t, lf,
1138 tet_vertex(t, tet_facet_vertex(lf,0)),
1139 tet_vertex(t, tet_facet_vertex(lf,1)),
1140 tet_vertex(t, tet_facet_vertex(lf,2))
1141 );
1142 geo_debug_assert(tet_adjacent(t,lf) == t2);
1143 geo_debug_assert(owns_tet(t));
1144 geo_debug_assert(owns_tet(t2));
1145 }
1146 }
1147 return true;
1148 }
1149
1150 /**
1151 * \brief Gets a pointer to a vertex by its global index.
1152 * \param[in] i global index of the vertex
1153 * \return a pointer to vertex \p i
1154 */
1155 const double* non_periodic_vertex_ptr(index_t i) const {
1156 geo_debug_assert(i < nb_vertices_non_periodic_);
1157 return vertices_ + i*3;
1158 }
1159
1160 /**
1161 * \brief Gets the weight of a vertex by its global index.
1162 * \param[in] i global index of the vertex
1163 * \return the weight associated with vertex i
1164 */
1165 double non_periodic_weight(index_t i) const {
1166 geo_debug_assert(i < nb_vertices_non_periodic_);
1167 return (weights_ == nullptr) ? 0.0 : weights_[i];
1168 }
1169
1170
1171 /**
1172 * \brief gets a vertex.
1173 * \param[in] v the index of the vertex, in 0..nb_vertices_-1
1174 * \param[out] result a pointer to the 3d coordinates of the vertex.
1175 * \details In periodic mode, does vertex translation (v is a
1176 * virtual vertex index).
1177 */
1178 void get_vertex(index_t v, double* result) const {
1179 if(!periodic_) {
1180 result[0] = vertices_[3*v];
1181 result[1] = vertices_[3*v+1];
1182 result[2] = vertices_[3*v+2];
1183 return;
1184 }
1185 index_t instance = periodic_vertex_instance(v);
1186 v = periodic_vertex_real(v);
1187 result[0] = vertices_[3*v];
1188 result[1] = vertices_[3*v+1];
1189 result[2] = vertices_[3*v+2];
1190 result[0] += double(translation[instance][0]) * period_.x;
1191 result[1] += double(translation[instance][1]) * period_.y;
1192 result[2] += double(translation[instance][2]) * period_.z;
1193 }
1194
1195 /**
1196 * \brief gets a vertex.
1197 * \param[in] v the index of the vertex, in 0..nb_vertices_-1
1198 * \details In periodic mode, does vertex translation (v is a
1199 * virtual vertex index).
1200 * \return the 3d vertex.
1201 */
1202 vec3 vertex(index_t v) const {
1203 vec3 result;
1204 get_vertex(v, result.data());
1205 return result;
1206 }
1207
1208 /**
1209 * \brief gets a lifted vertex.
1210 * \param[in] v the index of the vertex, in 0..nb_vertices_-1
1211 * \param[out] result the 4d coordinates of the vertex,
1212 * shifted on the paraboloid and shifted by the weight.
1213 * \details In periodic mode, does vertex translation (v is a
1214 * virtual vertex index).
1215 */
1216 void get_lifted_vertex(index_t v, double* result) const {
1217 index_t instance = 0;
1218 if(periodic_) {
1219 instance = periodic_vertex_instance(v);
1220 v = periodic_vertex_real(v);
1221 }
1222 result[0] = vertices_[3*v];
1223 result[1] = vertices_[3*v+1];
1224 result[2] = vertices_[3*v+2];
1225 result[3] = -non_periodic_weight(v);
1226 if(periodic_) {
1227 result[0] += double(translation[instance][0]) * period_.x;
1228 result[1] += double(translation[instance][1]) * period_.y;
1229 result[2] += double(translation[instance][2]) * period_.z;
1230 }
1231 result[3] +=
1232 geo_sqr(result[0]) + geo_sqr(result[1]) + geo_sqr(result[2]);
1233 }
1234
1235 /**
1236 * \brief gets a lifted vertex.
1237 * \param[in] v the index of the vertex, in 0..nb_vertices_-1
1238 * \details In periodic mode, does vertex translation (v is a
1239 * virtual vertex index).
1240 * \return the 4d vertex, lifted on the paraboloid, and shifted by
1241 * the weight.
1242 */
1243 vec4 lifted_vertex(index_t v) const {
1244 vec4 result;
1245 get_lifted_vertex(v,result.data());
1246 return result;
1247 }
1248
1249 /**
1250 * \brief gets a lifted vertex.
1251 * \param[in] v the index of the vertex, in 0..nb_vertices_-1
1252 * \param[in] p the geometric location of vertex v
1253 * \details In periodic mode, does vertex translation (v is a
1254 * virtual vertex index).
1255 * \return the 4d vertex, lifted on the paraboloid, and shifted by
1256 * the weight.
1257 */
1258 vec4 lifted_vertex(index_t v, const vec3& p) {
1259 return vec4(
1260 p.x, p.y, p.z,
1261 geo_sqr(p.x) + geo_sqr(p.y) + geo_sqr(p.z)
1262 - non_periodic_weight(periodic_vertex_real(v))
1263 );
1264 }
1265
1266 /**
1267 * \brief Orientation predicate with indices.
1268 * \param i , j , k , l the four indices of the four vertices.
1269 * \see PCK::orient_3d()
1270 */
1271 Sign orient_3d(index_t i, index_t j, index_t k, index_t l) const {
1272 // No need to reorder since there is no SOS.
1273 double V[4][3];
1274 get_vertex(i,V[0]);
1275 get_vertex(j,V[1]);
1276 get_vertex(k,V[2]);
1277 get_vertex(l,V[3]);
1278 return PCK::orient_3d(V[0], V[1], V[2], V[3]);
1279 }
1280
1281 /**
1282 * \brief Orientation predicate with indices.
1283 * \param i , j , k , l the four indices of the four vertices.
1284 * \see PCK::in_circle_3dlifted_SOS()
1285 */
1286 Sign in_circle_3dlifted_SOS(
1287 index_t i, index_t j, index_t k, index_t l
1288 ) const {
1289
1290 // In non-periodic mode, directly access vertices.
1291 if(!periodic_) {
1292 const double* pi = non_periodic_vertex_ptr(i);
1293 const double* pj = non_periodic_vertex_ptr(j);
1294 const double* pk = non_periodic_vertex_ptr(k);
1295 const double* pl = non_periodic_vertex_ptr(l);
1296
1297 double hi = geo_sqr(pi[0]) + geo_sqr(pi[1]) + geo_sqr(pi[2])
1298 - non_periodic_weight(i);
1299
1300 double hj = geo_sqr(pj[0]) + geo_sqr(pj[1]) + geo_sqr(pj[2])
1301 - non_periodic_weight(j);
1302
1303 double hk = geo_sqr(pk[0]) + geo_sqr(pk[1]) + geo_sqr(pk[2])
1304 - non_periodic_weight(k);
1305
1306 double hl = geo_sqr(pl[0]) + geo_sqr(pl[1]) + geo_sqr(pl[2])
1307 - non_periodic_weight(l);
1308
1309 return PCK::in_circle_3dlifted_SOS(
1310 pi, pj, pk, pl,
1311 hi, hj, hk, hl
1312 );
1313 }
1314
1315 // Note: in periodic mode, SOS mode is lexicographic.
1316 double V[4][4];
1317 get_lifted_vertex(i,V[0]);
1318 get_lifted_vertex(j,V[1]);
1319 get_lifted_vertex(k,V[2]);
1320 get_lifted_vertex(l,V[3]);
1321 return PCK::in_circle_3dlifted_SOS(
1322 V[0], V[1], V[2], V[3],
1323 V[0][3], V[1][3], V[2][3], V[3][3]
1324 );
1325 }
1326
1327 /**
1328 * \brief 4D Orientation predicate with indices.
1329 * \param i , j , k , l , m the five indices of the four vertices.
1330 * \see PCK::orient_3dlifted_SOS()
1331 */
1332 Sign orient_3dlifted_SOS(
1333 index_t i, index_t j, index_t k, index_t l, index_t m
1334 ) const {
1335
1336 // In non-periodic mode, directly access vertices.
1337 if(!periodic_) {
1338 const double* pi = non_periodic_vertex_ptr(i);
1339 const double* pj = non_periodic_vertex_ptr(j);
1340 const double* pk = non_periodic_vertex_ptr(k);
1341 const double* pl = non_periodic_vertex_ptr(l);
1342 const double* pm = non_periodic_vertex_ptr(m);
1343
1344 double hi = geo_sqr(pi[0]) + geo_sqr(pi[1]) + geo_sqr(pi[2])
1345 - non_periodic_weight(i);
1346
1347 double hj = geo_sqr(pj[0]) + geo_sqr(pj[1]) + geo_sqr(pj[2])
1348 - non_periodic_weight(j);
1349
1350 double hk = geo_sqr(pk[0]) + geo_sqr(pk[1]) + geo_sqr(pk[2])
1351 - non_periodic_weight(k);
1352
1353 double hl = geo_sqr(pl[0]) + geo_sqr(pl[1]) + geo_sqr(pl[2])
1354 - non_periodic_weight(l);
1355
1356 double hm = geo_sqr(pm[0]) + geo_sqr(pm[1]) + geo_sqr(pm[2])
1357 - non_periodic_weight(m);
1358
1359 return PCK::orient_3dlifted_SOS(
1360 pi, pj, pk, pl, pm,
1361 hi, hj, hk, hl, hm
1362 );
1363 }
1364
1365 // Periodic mode.
1366 // Note: in periodic mode, SOS mode is lexicographic.
1367 double V[5][4];
1368
1369 /*
1370 The code below is an inlined version of:
1371 (gains a little bit)
1372 get_lifted_vertex(i,V[0]);
1373 get_lifted_vertex(j,V[1]);
1374 get_lifted_vertex(k,V[2]);
1375 get_lifted_vertex(l,V[3]);
1376 get_lifted_vertex(m,V[4]);
1377 */
1378
1379 index_t ii = periodic_vertex_instance(i);
1380 index_t ij = periodic_vertex_instance(j);
1381 index_t ik = periodic_vertex_instance(k);
1382 index_t il = periodic_vertex_instance(l);
1383 index_t im = periodic_vertex_instance(m);
1384
1385 i = periodic_vertex_real(i);
1386 j = periodic_vertex_real(j);
1387 k = periodic_vertex_real(k);
1388 l = periodic_vertex_real(l);
1389 m = periodic_vertex_real(m);
1390
1391 V[0][0] = vertices_[3*i ] + double(translation[ii][0]) * period_.x;
1392 V[0][1] = vertices_[3*i+1] + double(translation[ii][1]) * period_.y;
1393 V[0][2] = vertices_[3*i+2] + double(translation[ii][2]) * period_.z;
1394 V[1][0] = vertices_[3*j ] + double(translation[ij][0]) * period_.x;
1395 V[1][1] = vertices_[3*j+1] + double(translation[ij][1]) * period_.y;
1396 V[1][2] = vertices_[3*j+2] + double(translation[ij][2]) * period_.z;
1397 V[2][0] = vertices_[3*k ] + double(translation[ik][0]) * period_.x;
1398 V[2][1] = vertices_[3*k+1] + double(translation[ik][1]) * period_.y;
1399 V[2][2] = vertices_[3*k+2] + double(translation[ik][2]) * period_.z;
1400 V[3][0] = vertices_[3*l ] + double(translation[il][0]) * period_.x;
1401 V[3][1] = vertices_[3*l+1] + double(translation[il][1]) * period_.y;
1402 V[3][2] = vertices_[3*l+2] + double(translation[il][2]) * period_.z;
1403 V[4][0] = vertices_[3*m ] + double(translation[im][0]) * period_.x;
1404 V[4][1] = vertices_[3*m+1] + double(translation[im][1]) * period_.y;
1405 V[4][2] = vertices_[3*m+2] + double(translation[im][2]) * period_.z;
1406
1407 // Beware the parentheses, they are necessary to ensure that computations
1408 // | give always the same result.
1409 // |________________________________________________________________________
1410 // | |
1411 // v v
1412 V[0][3] = -non_periodic_weight(i) + (geo_sqr(V[0][0]) + geo_sqr(V[0][1]) + geo_sqr(V[0][2]));
1413 V[1][3] = -non_periodic_weight(j) + (geo_sqr(V[1][0]) + geo_sqr(V[1][1]) + geo_sqr(V[1][2]));
1414 V[2][3] = -non_periodic_weight(k) + (geo_sqr(V[2][0]) + geo_sqr(V[2][1]) + geo_sqr(V[2][2]));
1415 V[3][3] = -non_periodic_weight(l) + (geo_sqr(V[3][0]) + geo_sqr(V[3][1]) + geo_sqr(V[3][2]));
1416 V[4][3] = -non_periodic_weight(m) + (geo_sqr(V[4][0]) + geo_sqr(V[4][1]) + geo_sqr(V[4][2]));
1417
1418 return PCK::orient_3dlifted_SOS(
1419 V[0], V[1], V[2], V[3], V[4],
1420 V[0][3], V[1][3], V[2][3], V[3][3], V[4][3]
1421 );
1422 }
1423
1424 /**
1425 * \brief Tests whether a given tetrahedron is in conflict with
1426 * a given 3d point.
1427 * \details A real tetrahedron is in conflict with a point whenever
1428 * the point is contained by its circumscribed sphere, and a
1429 * virtual tetrahedron is in conflict with a point whenever the
1430 * tetrahedron formed by its real face and with the point has
1431 * positive orientation.
1432 * \param[in] t the index of the tetrahedron
1433 * \param[in] v the index of the point
1434 * \param[in] p a pointer to the coordinates of the point
1435 * \retval true if point \p p is in conflict with tetrahedron \p t
1436 * \retval false otherwise
1437 */
1438 bool tet_is_in_conflict(index_t t, index_t v, const vec4& p) const {
1439
1440 geo_argused(p);
1441
1442 // Lookup tetrahedron vertices
1443
1444 index_t iv[4];
1445 for(index_t i=0; i<4; ++i) {
1446 iv[i] = tet_vertex(t,i);
1447 }
1448
1449
1450 // Check for virtual tetrahedra (then in_sphere()
1451 // is replaced with orient3d())
1452 for(index_t lf = 0; lf < 4; ++lf) {
1453
1454 if(iv[lf] == NO_INDEX) {
1455
1456 // Facet of a virtual tetrahedron opposite to
1457 // infinite vertex corresponds to
1458 // the triangle on the convex hull of the points.
1459 // Orientation is obtained by replacing vertex lf
1460 // with p.
1461 iv[lf] = v;
1462
1463 // no SOS, we can directly use the PCK predicate
1464 Sign sign = orient_3d(iv[0], iv[1], iv[2], iv[3]);
1465
1466 if(sign > 0) {
1467 return true;
1468 }
1469
1470 if(sign < 0) {
1471 return false;
1472 }
1473
1474 // If sign is zero, we check the real tetrahedron
1475 // adjacent to the facet on the convex hull.
1476 geo_debug_assert(tet_adjacent(t, lf) != NO_INDEX);
1477 index_t t2 = tet_adjacent(t, lf);
1478 geo_debug_assert(!tet_is_virtual(t2));
1479
1480 // If t2 was already visited by this thread, then
1481 // it is in conflict if it is already marked.
1482 if(owns_tet(t2)) {
1483 return tet_is_marked_as_conflict(t2);
1484 }
1485
1486 // If t2 was not already visited, then we need to
1487 // switch to the in_circum_circle_3d() predicate.
1488
1489 index_t iq0 = iv[(lf+1)%4];
1490 index_t iq1 = iv[(lf+2)%4];
1491 index_t iq2 = iv[(lf+3)%4];
1492
1493 return (in_circle_3dlifted_SOS(iq0, iq1, iq2, v) > 0);
1494 }
1495 }
1496
1497 // If the tetrahedron is a finite one, it is in conflict
1498 // if its circumscribed sphere contains the point (this is
1499 // the standard case).
1500
1501 return (orient_3dlifted_SOS(iv[0], iv[1], iv[2], iv[3], v) > 0);
1502 }
1503
1504
1505 /**
1506 * \brief Finds the tetrahedron that contains a point.
1507 * \details The tetrahedron is acquired by this thread. If the
1508 * tetrahedron could not be acquired, then NO_TETRAHEDRON is returned.
1509 * If the point is on a face, edge or vertex,
1510 * the function returns one of the tetrahedra incident
1511 * to that face, edge or vertex.
1512 * \param[in] v the index of the vertex to locate
1513 * \param[in] p the coordinates of the point that correspond to v
1514 * \param[out] orient a pointer to an array of four Sign%s
1515 * or nullptr. If non-nullptr, returns the orientation with respect
1516 * to the four facets of the tetrahedron that contains \p p.
1517 * \retval the index of a tetrahedron that contains \p p.
1518 * If the point is outside the convex hull of
1519 * the inserted so-far points, then the returned tetrahedron
1520 * is a virtual one (first vertex is the "vertex at infinity"
1521 * of index -1)
1522 * \retval NO_TETRAHEDRON if the tetrahedron could not be
1523 * acquired by this thread, or if the virtual tetrahedra
1524 * were previously removed
1525 */
1526 index_t locate(
1527 index_t& v, vec3& p, index_t hint = NO_TETRAHEDRON,
1528 Sign* orient = nullptr
1529 ) {
1530 geo_argused(v);
1531 nb_traversed_tets_ = 0;
1532
1533 // If no hint specified, find a tetrahedron randomly
1534
1535 if(hint != NO_TETRAHEDRON) {
1536 if(tet_is_free(hint)) {
1537 hint = NO_TETRAHEDRON;
1538 } else {
1539 if( !owns_tet(hint) && !acquire_tet(hint) ) {
1540 hint = NO_TETRAHEDRON;
1541 }
1542 if((hint != NO_TETRAHEDRON) && tet_is_free(hint)) {
1543 release_tet(hint);
1544 hint = NO_TETRAHEDRON;
1545 }
1546 }
1547 }
1548
1549 do {
1550 while(hint == NO_TETRAHEDRON) {
1551 hint = master_->thread(0)->pick_random_tet();
1552 // we could also pick from a random thread,
1553 // but at initialization only thread0 has tets,
1554 // so let us keep thread0 for now
1555 }
1556 if(
1557 tet_is_free(hint) || (!owns_tet(hint) && !acquire_tet(hint))
1558 ) {
1559 if(owns_tet(hint)) {
1560 release_tet(hint);
1561 }
1562 hint = NO_TETRAHEDRON;
1563 } else {
1564 for(index_t f=0; f<4; ++f) {
1565 if(tet_vertex(hint,f) == VERTEX_AT_INFINITY) {
1566 index_t new_hint = tet_adjacent(hint,f);
1567 if(
1568 tet_is_free(new_hint) ||
1569 !acquire_tet(new_hint)
1570 ) {
1571 new_hint = NO_TETRAHEDRON;
1572 }
1573 release_tet(hint);
1574 hint = new_hint;
1575 break;
1576 }
1577 }
1578 }
1579 } while(hint == NO_TETRAHEDRON) ;
1580
1581 index_t t = hint;
1582 index_t t_pred = NO_TETRAHEDRON;
1583 Sign orient_local[4];
1584 if(orient == nullptr) {
1585 orient = orient_local;
1586 }
1587
1588
1589 still_walking:
1590 {
1591 if(t_pred != NO_TETRAHEDRON) {
1592 release_tet(t_pred);
1593 }
1594
1595 if(tet_is_free(t)) {
1596 return NO_TETRAHEDRON;
1597 }
1598
1599 if(!owns_tet(t) && !acquire_tet(t)) {
1600 return NO_TETRAHEDRON;
1601 }
1602
1603
1604 if(!tet_is_real(t)) {
1605 release_tet(t);
1606 return NO_TETRAHEDRON;
1607 }
1608
1609 vec3 pv[4];
1610 pv[0] = vertex(finite_tet_vertex(t,0));
1611 pv[1] = vertex(finite_tet_vertex(t,1));
1612 pv[2] = vertex(finite_tet_vertex(t,2));
1613 pv[3] = vertex(finite_tet_vertex(t,3));
1614
1615 // Start from a random facet
1616 index_t f0 = thread_safe_random_4_();
1617 for(index_t df = 0; df < 4; ++df) {
1618 index_t f = (f0 + df) % 4;
1619
1620 index_t t_next = tet_adjacent(t,f);
1621
1622 // If the opposite tet is -1, then it means that
1623 // we are trying to locate() (e.g. called from
1624 // nearest_vertex) within a tetrahedralization
1625 // from which the infinite tets were removed.
1626 if(t_next == NO_INDEX) {
1627 release_tet(t);
1628 return NO_TETRAHEDRON;
1629 }
1630
1631 // If the candidate next tetrahedron is the
1632 // one we came from, then we know already that
1633 // the orientation is positive, thus we examine
1634 // the next candidate (or exit the loop if they
1635 // are exhausted).
1636 if(t_next == t_pred) {
1637 orient[f] = POSITIVE ;
1638 continue ;
1639 }
1640
1641 // To test the orientation of p w.r.t. the facet f of
1642 // t, we replace vertex number f with p in t (same
1643 // convention as in CGAL).
1644 // This is equivalent to tet_facet_point_orient3d(t,f,p)
1645 // (but less costly, saves a couple of lookups)
1646 vec3 pv_bkp = pv[f];
1647 pv[f] = p;
1648 orient[f] = PCK::orient_3d(
1649 pv[0].data(), pv[1].data(), pv[2].data(), pv[3].data()
1650 );
1651
1652 // If the orientation is not negative, then we cannot
1653 // walk towards t_next, and examine the next candidate
1654 // (or exit the loop if they are exhausted).
1655 if(orient[f] != NEGATIVE) {
1656 pv[f] = pv_bkp;
1657 continue;
1658 }
1659
1660 // If the opposite tet is a virtual tet, then
1661 // the point has a positive orientation relative
1662 // to the facet on the border of the convex hull,
1663 // thus t_next is a tet in conflict and we are
1664 // done.
1665 if(tet_is_virtual(t_next)) {
1666 release_tet(t);
1667 if(!acquire_tet(t_next)) {
1668 return NO_TETRAHEDRON;
1669 }
1670 for(index_t lf = 0; lf < 4; ++lf) {
1671 orient[lf] = POSITIVE;
1672 }
1673 return t_next;
1674 }
1675
1676 ++nb_traversed_tets_;
1677
1678 // If we reach this point, then t_next is a valid
1679 // successor, thus we are still walking.
1680 t_pred = t;
1681 t = t_next;
1682 goto still_walking;
1683 }
1684 }
1685
1686 // If we reach this point, we did not find a valid successor
1687 // for walking (a face for which p has negative orientation),
1688 // thus we reached the tet for which p has all positive
1689 // face orientations (i.e. the tet that contains p).
1690
1691 #ifdef GEO_DEBUG
1692 geo_debug_assert(tet_is_real(t));
1693
1694 vec3 pv[4];
1695 Sign signs[4];
1696 pv[0] = vertex(finite_tet_vertex(t,0));
1697 pv[1] = vertex(finite_tet_vertex(t,1));
1698 pv[2] = vertex(finite_tet_vertex(t,2));
1699 pv[3] = vertex(finite_tet_vertex(t,3));
1700 for(index_t f=0; f<4; ++f) {
1701 vec3 pv_bkp = pv[f];
1702 pv[f] = vec3(p.x, p.y, p.z);
1703 signs[f] = PCK::orient_3d(
1704 pv[0].data(), pv[1].data(), pv[2].data(), pv[3].data()
1705 );
1706 geo_debug_assert(signs[f] >= 0);
1707 pv[f] = pv_bkp;
1708 }
1709 #endif
1710
1711 return t;
1712 }
1713
1714
1715 protected:
1716
1717 /**
1718 * \brief Tests whether a tetrahedron was marked as conflict.
1719 * \pre owns_tet(t)
1720 * \param[in] t the index of the tetrahedron to be tested
1721 * \retval true if \p t was marked as conflict
1722 * \retval false otherwise
1723 */
1724 bool tet_is_marked_as_conflict(index_t t) const {
1725 geo_debug_assert(owns_tet(t));
1726 return cell_status_.cell_is_marked_as_conflict(t);
1727 }
1728
1729
1730 /**
1731 * \brief Gets the number of tetrahedra in conflict.
1732 * \return the number of tetrahedra in conflict,
1733 * specified by mark_tet_as_conflict()
1734 */
1735 index_t nb_tets_in_conflict() const {
1736 return tets_to_delete_.size();
1737 }
1738
1739 /**
1740 * \brief Marks a tetrahedron as conflict.
1741 * \details The index of the tetrahedron is also
1742 * stored it in the list of conflict tetrahedra.
1743 * \param[in] t index of the tetrahedron to mark
1744 * \pre owns_tet(t)
1745 */
1746 void mark_tet_as_conflict(index_t t) {
1747 geo_debug_assert(owns_tet(t));
1748 tets_to_delete_.push_back(t);
1749 cell_status_.mark_cell_as_conflict(t);
1750 geo_debug_assert(owns_tet(t));
1751 geo_debug_assert(tet_is_marked_as_conflict(t));
1752 }
1753
1754 /**
1755 * \brief Marks a tetrahedron as neighbor of the conflict zone.
1756 * \details The index of the tetrahedron is also
1757 * stored it in the list of tetrahedra to release.
1758 * \param[in] t index of the tetrahedron to mark
1759 * \pre owns_tet(t)
1760 */
1761 void mark_tet_as_neighbor(index_t t) {
1762 // Note: nothing to change in cell_status_[t]
1763 // since MSB=0 means neigbhor tet.
1764 tets_to_release_.push_back(t);
1765 }
1766
1767 /**
1768 * \brief Acquires a lock on a tetrahedron and keep
1769 * it in the list of acquired tetrahedra.
1770 * \param[in] t index of the tetrahedron to acquire
1771 */
1772 void acquire_and_mark_tet_as_created(index_t t) {
1773 // The tet was created in this thread's tet pool,
1774 // therefore there is no need to use sync
1775 // primitives to acquire a lock on it.
1776 geo_debug_assert(cell_status_.cell_thread(t) == NO_THREAD);
1777 cell_status_.set_cell_status(
1778 t, CellStatusArray::thread_index_t(id())
1779 );
1780
1781 #ifdef GEO_DEBUG
1782 ++nb_acquired_tets_;
1783 #endif
1784 tets_to_release_.push_back(t);
1785 }
1786
1787
1788 /**
1789 * \brief Releases all the tetrahedron locks that were
1790 * acquired using mark_tet_as_neighbor(),
1791 * acquire_and_mark_tet_as_created() and mark_as_conflict().
1792 */
1793 void release_tets() {
1794 for(index_t i=0; i<tets_to_release_.size(); ++i) {
1795 release_tet(tets_to_release_[i]);
1796 }
1797 tets_to_release_.resize(0);
1798 for(index_t i=0; i<tets_to_delete_.size(); ++i) {
1799 release_tet(tets_to_delete_[i]);
1800 }
1801 tets_to_delete_.resize(0);
1802 }
1803
1804 /**
1805 * \brief Atomically acquires a lock on a tetrahedron.
1806 * \details When the lock could not be acquired, interfering_thread_
1807 * contains the id of the thread that owns the lock.
1808 * \param[in] t the index of the tetrahedron to acquire
1809 * \retval true if the lock was successfully acquired
1810 * \retval false otherwise
1811 */
1812 bool acquire_tet(index_t t) {
1813 geo_debug_assert(t < max_t());
1814 geo_debug_assert(!owns_tet(t));
1815
1816 interfering_thread_ = cell_status_.acquire_cell(
1817 t, CellStatusArray::thread_index_t(id())
1818 );
1819
1820 if(interfering_thread_ == NO_THREAD) {
1821 geo_debug_assert(t == first_free_ || !tet_is_in_list(t));
1822 #ifdef GEO_DEBUG
1823 ++nb_acquired_tets_;
1824 #endif
1825 return true;
1826 }
1827 return false;
1828 }
1829
1830 /**
1831 * \brief Releases a lock on a tetrahedron, making it
1832 * available to the other threads.
1833 */
1834 void release_tet(index_t t) {
1835 geo_debug_assert(t < max_t());
1836 geo_debug_assert(owns_tet(t));
1837 #ifdef GEO_DEBUG
1838 --nb_acquired_tets_;
1839 #endif
1840 cell_status_.release_cell(t);
1841 }
1842
1843
1844 /**
1845 * \brief Tests whether this thread owns a tetrahedron.
1846 * \param[in] t index of the tetrahedron
1847 * \retval true if this thread owns t
1848 * \retval false otherwise
1849 */
1850 bool owns_tet(index_t t) const {
1851 geo_debug_assert(t < max_t());
1852 return (
1853 cell_status_.cell_thread(t) ==
1854 CellStatusArray::thread_index_t(id())
1855 );
1856 }
1857
1858 /**
1859 * \brief Tests whether a tetrahedron is
1860 * a virtual one.
1861 * \details Virtual tetrahedra are tetrahedra
1862 * incident to the vertex at infinity.
1863 * \param[in] t index of the tetrahedron
1864 * \retval true if tetrahedron \p t is virtual
1865 * \retval false otherwise
1866 */
1867 bool tet_is_virtual(index_t t) const {
1868 return
1869 !tet_is_free(t) && (
1870 cell_to_v_store_[4 * t] == VERTEX_AT_INFINITY ||
1871 cell_to_v_store_[4 * t + 1] == VERTEX_AT_INFINITY ||
1872 cell_to_v_store_[4 * t + 2] == VERTEX_AT_INFINITY ||
1873 cell_to_v_store_[4 * t + 3] == VERTEX_AT_INFINITY
1874 ) ;
1875 }
1876
1877
1878 /**
1879 * \brief Returns the local index of a vertex by
1880 * facet and by local vertex index in the facet.
1881 * \details
1882 * tet facet vertex is such that the tetrahedron
1883 * formed with:
1884 * - vertex lv
1885 * - tet_facet_vertex(lv,0)
1886 * - tet_facet_vertex(lv,1)
1887 * - tet_facet_vertex(lv,2)
1888 * has the same orientation as the original tetrahedron for
1889 * any vertex lv.
1890 * \param[in] f local facet index, in (0,1,2,3)
1891 * \param[in] v local vertex index, in (0,1,2)
1892 * \return the local tetrahedron vertex index of
1893 * vertex \p v in facet \p f
1894 */
1895 static index_t tet_facet_vertex(index_t f, index_t v) {
1896 geo_debug_assert(f < 4);
1897 geo_debug_assert(v < 3);
1898 return index_t(tet_facet_vertex_[f][v]);
1899 }
1900
1901 /**
1902 * \brief Gets the index of a vertex of a tetrahedron
1903 * \param[in] t index of the tetrahedron
1904 * \param[in] lv local vertex (0,1,2 or 3) index in \p t
1905 * \return the global index of the \p lv%th vertex of tetrahedron \p t
1906 * or -1 if the vertex is at infinity
1907 */
1908 index_t tet_vertex(index_t t, index_t lv) const {
1909 geo_debug_assert(t < max_t());
1910 geo_debug_assert(lv < 4);
1911 return cell_to_v_store_[4 * t + lv];
1912 }
1913
1914 /**
1915 * \brief Finds the index of the vertex in a tetrahedron.
1916 * \param[in] t the tetrahedron
1917 * \param[in] v the vertex
1918 * \return iv such that tet_vertex(t,v)==iv
1919 * \pre \p t is incident to \p v
1920 */
1921 index_t find_tet_vertex(index_t t, index_t v) const {
1922 geo_debug_assert(t < max_t());
1923 // Find local index of v in tetrahedron t vertices.
1924 const index_t* T = &(cell_to_v_store_[4 * t]);
1925 return find_4(T,v);
1926 }
1927
1928
1929 /**
1930 * \brief Gets the index of a vertex of a tetrahedron
1931 * \param[in] t index of the tetrahedron
1932 * \param[in] lv local vertex (0,1,2 or 3) index in \p t
1933 * \return the global index of the \p lv%th vertex of tetrahedron \p t
1934 * \pre Vertex \p lv of tetrahedron \p t is not at infinity
1935 */
1936 index_t finite_tet_vertex(index_t t, index_t lv) const {
1937 geo_debug_assert(t < max_t());
1938 geo_debug_assert(lv < 4);
1939 geo_debug_assert(cell_to_v_store_[4 * t + lv] != NO_INDEX);
1940 return cell_to_v_store_[4 * t + lv];
1941 }
1942
1943 /**
1944 * \brief Sets a tetrahedron-to-vertex adjacency.
1945 * \param[in] t index of the tetrahedron
1946 * \param[in] lv local vertex index (0,1,2 or 3) in \p t
1947 * \param[in] v global index of the vertex
1948 */
1949 void set_tet_vertex(index_t t, index_t lv, index_t v) {
1950 geo_debug_assert(t < max_t());
1951 geo_debug_assert(lv < 4);
1952 geo_debug_assert(owns_tet(t));
1953 cell_to_v_store_[4 * t + lv] = v;
1954 }
1955
1956 /**
1957 * \brief Gets the index of a tetrahedron adjacent to another one.
1958 * \param[in] t index of the tetrahedron
1959 * \param[in] lf local facet (0,1,2 or 3) index in \p t
1960 * \return the tetrahedron adjacent to \p t accorss facet \p lf
1961 */
1962 index_t tet_adjacent(index_t t, index_t lf) const {
1963 geo_debug_assert(t < max_t());
1964 geo_debug_assert(lf < 4);
1965 index_t result = cell_to_cell_store_[4 * t + lf];
1966 return result;
1967 }
1968
1969 /**
1970 * \brief Sets a tetrahedron-to-tetrahedron adjacency.
1971 * \param[in] t1 index of the first tetrahedron
1972 * \param[in] lf1 local facet index (0,1,2 or 3) in t1
1973 * \param[in] t2 index of the tetrahedron
1974 * adjacent to \p t1 across \p lf1
1975 */
1976 void set_tet_adjacent(index_t t1, index_t lf1, index_t t2) {
1977 geo_debug_assert(t1 < max_t());
1978 geo_debug_assert(t2 < max_t());
1979 geo_debug_assert(lf1 < 4);
1980 geo_debug_assert(owns_tet(t1));
1981 geo_debug_assert(owns_tet(t2));
1982 cell_to_cell_store_[4 * t1 + lf1] = t2;
1983 }
1984
1985 /**
1986 * \brief Finds the index of the facet across which t1 is
1987 * adjacent to t2.
1988 * \param[in] t1 first tetrahedron
1989 * \param[in] t2 second tetrahedron
1990 * \return f such that tet_adjacent(t1,f)==t2
1991 * \pre \p t1 and \p t2 are adjacent
1992 */
1993 index_t find_tet_adjacent(index_t t1, index_t t2) const {
1994 geo_debug_assert(t1 < max_t());
1995 geo_debug_assert(t2 < max_t());
1996 geo_debug_assert(t1 != t2);
1997
1998 // Find local index of t2 in tetrahedron t1 adajcent tets.
1999 const index_t* T = &(cell_to_cell_store_[4 * t1]);
2000 index_t result = find_4(T,t2);
2001
2002 // Sanity check: make sure that t1 is adjacent to t2
2003 // only once!
2004 geo_debug_assert(tet_adjacent(t1,(result+1)%4) != t2);
2005 geo_debug_assert(tet_adjacent(t1,(result+2)%4) != t2);
2006 geo_debug_assert(tet_adjacent(t1,(result+3)%4) != t2);
2007 return result;
2008 }
2009
2010 /**
2011 * Gets the local facet index incident to an
2012 * oriented halfedge.
2013 * \param[in] t index of the tetrahedron
2014 * \param[in] v1 global index of the first extremity
2015 * \param[in] v2 global index of the second extremity
2016 * \return the local index of the facet incident to
2017 * the oriented edge \p v1, \p v2.
2018 */
2019 index_t get_facet_by_halfedge(index_t t, index_t v1, index_t v2) const {
2020 geo_debug_assert(t < max_t());
2021 geo_debug_assert(v1 != v2);
2022 // Find local index of v1 and v2 in tetrahedron t
2023 const index_t* T = &(cell_to_v_store_[4 * t]);
2024
2025 index_t lv1, lv2;
2026 lv1 = find_4(T,v1);
2027 lv2 = find_4(T,v2);
2028 geo_debug_assert(lv1 != lv2);
2029 return index_t(halfedge_facet_[lv1][lv2]);
2030 }
2031
2032
2033 /**
2034 * Gets the local facet indices incident to an
2035 * oriented halfedge.
2036 * \param[in] t index of the tetrahedron
2037 * \param[in] v1 global index of the first extremity
2038 * \param[in] v2 global index of the second extremity
2039 * \param[out] f12 the local index of the facet
2040 * indicent to the halfedge [v1,v2]
2041 * \param[out] f21 the local index of the facet
2042 * indicent to the halfedge [v2,v1]
2043 */
2044 void get_facets_by_halfedge(
2045 index_t t, index_t v1, index_t v2,
2046 index_t& f12, index_t& f21
2047 ) const {
2048 geo_debug_assert(t < max_t());
2049 geo_debug_assert(v1 != v2);
2050
2051 // Find local index of v1 and v2 in tetrahedron t
2052 // The following expression is 10% faster than using
2053 // if() statements (multiply by boolean result of test).
2054 // Thank to Laurent Alonso for this idea.
2055 const index_t* T = &(cell_to_v_store_[4 * t]);
2056
2057 index_t lv1 = index_t((T[1] == v1) | ((T[2] == v1) * 2) | ((T[3] == v1) * 3));
2058 index_t lv2 = index_t((T[1] == v2) | ((T[2] == v2) * 2) | ((T[3] == v2) * 3));
2059 geo_debug_assert(lv1 != 0 || T[0] == v1);
2060 geo_debug_assert(lv2 != 0 || T[0] == v2);
2061 geo_debug_assert(lv1 != lv2);
2062
2063 f12 = index_t(halfedge_facet_[lv1][lv2]);
2064 f21 = index_t(halfedge_facet_[lv2][lv1]);
2065 }
2066
2067 /**
2068 * \brief Symbolic value of the cell_next_ field
2069 * that indicates the end of list in a linked
2070 * list of tetrahedra.
2071 */
2072 static constexpr index_t END_OF_LIST = NO_INDEX;
2073
2074 /**
2075 * \brief Symbolic value of the cell_next_ field
2076 * for a tetrahedron that is not in a list.
2077 */
2078 static constexpr index_t NOT_IN_LIST = index_t(-2);
2079
2080 /**
2081 * \brief Symbolic value for t2v_[] indicating a deleted tetrahedron.
2082 */
2083 static constexpr index_t VERTEX_OF_DELETED_TET = index_t(-2);
2084
2085 /**
2086 * \brief Gets the number of vertices.
2087 * \return the number of vertices in this Delaunay
2088 */
2089 index_t nb_vertices() const {
2090 return nb_vertices_;
2091 }
2092
2093 /**
2094 * \brief Tests whether a tetrahedron belongs to a linked
2095 * list.
2096 * \details Tetrahedra can be linked, it is used to manage
2097 * both the free list that recycles deleted tetrahedra,
2098 * the conflict region and the list of newly created
2099 * tetrahedra.
2100 * \param[in] t the index of the tetrahedron
2101 * \retval true if tetrahedron \p t belongs to a linked list
2102 * \retval false otherwise
2103 */
2104 bool tet_is_in_list(index_t t) const {
2105 geo_debug_assert(t < max_t());
2106 return (cell_next_[t] != NOT_IN_LIST);
2107 }
2108
2109 /**
2110 * \brief Gets the index of a successor of a tetrahedron.
2111 * \details Tetrahedra can be linked, it is used to manage
2112 * both the free list that recycles deleted tetrahedra.
2113 * \param[in] t the index of the tetrahedron
2114 * \retval END_OF_LIST if the end of the list is reached
2115 * \retval the index of the successor of
2116 * tetrahedron \t otherwise
2117 * \pre tet_is_in_list(t)
2118 */
2119 index_t tet_next(index_t t) const {
2120 geo_debug_assert(t < max_t());
2121 geo_debug_assert(tet_is_in_list(t));
2122 return cell_next_[t];
2123 }
2124
2125
2126 index_t tet_thread(index_t t) const {
2127 geo_debug_assert(t < max_t());
2128 return cell_status_.cell_thread(t);
2129 }
2130
2131 /**
2132 * \brief Adds a tetrahedron to a linked list.
2133 * \details Tetrahedra can be linked, it is used to manage
2134 * the free list that recycles deleted tetrahedra.
2135 * \param[in] t the index of the tetrahedron
2136 * \param[in,out] first first item of the list or END_OF_LIST if
2137 * the list is empty
2138 * \param[in,out] last last item of the list or END_OF_LIST if
2139 * the list is empty
2140 */
2141 void add_tet_to_list(index_t t, index_t& first, index_t& last) {
2142 geo_debug_assert(t < max_t());
2143 geo_debug_assert(!tet_is_in_list(t));
2144 geo_debug_assert(owns_tet(t));
2145 if(last == END_OF_LIST) {
2146 geo_debug_assert(first == END_OF_LIST);
2147 first = last = t;
2148 cell_next_[t] = END_OF_LIST;
2149 } else {
2150 cell_next_[t] = first;
2151 first = t;
2152 }
2153 }
2154
2155 /**
2156 * \brief Removes a tetrahedron from the linked list it
2157 * belongs to.
2158 * \details Tetrahedra can be linked, it is used to manage
2159 * the free list that recycles deleted tetrahedra.
2160 * \param[in] t the index of the tetrahedron
2161 */
2162 void remove_tet_from_list(index_t t) {
2163 geo_debug_assert(t < max_t());
2164 geo_debug_assert(tet_is_in_list(t));
2165 geo_debug_assert(owns_tet(t));
2166 cell_next_[t] = NOT_IN_LIST;
2167 }
2168
2169
2170 /**
2171 * \brief Creates a new tetrahedron.
2172 * \details Uses either a tetrahedron recycled
2173 * from the free list, or creates a new one by
2174 * expanding the two indices arrays.
2175 * \return the index of the newly created tetrahedron
2176 */
2177 index_t new_tetrahedron() {
2178 // If the memory pool is full, then we expand it.
2179 // This cannot be done when running multiple threads.
2180 if(first_free_ == END_OF_LIST) {
2181 geo_debug_assert(!Process::is_running_threads());
2182
2183 if(
2184 master_->cell_to_v_store_.size() ==
2185 master_->cell_to_v_store_.capacity()
2186 ) {
2187 master_->nb_reallocations_++;
2188 }
2189
2190 master_->cell_to_v_store_.resize(
2191 master_->cell_to_v_store_.size() + 4, NO_INDEX
2192 );
2193 master_->cell_to_cell_store_.resize(
2194 master_->cell_to_cell_store_.size() + 4, NO_INDEX
2195 );
2196 master_->cell_next_.push_back(END_OF_LIST);
2197 master_->cell_status_.grow();
2198 ++nb_free_;
2199 ++max_t_;
2200 first_free_ = master_->cell_status_.size() - 1;
2201 }
2202
2203 acquire_and_mark_tet_as_created(first_free_);
2204 index_t result = first_free_;
2205
2206 first_free_ = tet_next(first_free_);
2207 remove_tet_from_list(result);
2208
2209 cell_to_cell_store_[4 * result] = NO_INDEX;
2210 cell_to_cell_store_[4 * result + 1] = NO_INDEX;
2211 cell_to_cell_store_[4 * result + 2] = NO_INDEX;
2212 cell_to_cell_store_[4 * result + 3] = NO_INDEX;
2213
2214 used_tets_end_ = std::max(used_tets_end_, result+1);
2215
2216 --nb_free_;
2217 return result;
2218 }
2219
2220 /**
2221 * \brief Creates a new tetrahedron.
2222 * \details Sets the vertices. Adjacent tetrahedra index are
2223 * left uninitialized. Uses either a tetrahedron recycled
2224 * from the free list, or creates a new one by
2225 * expanding the two indices arrays.
2226 * \param[in] v1 index of the first vertex
2227 * \param[in] v2 index of the second vertex
2228 * \param[in] v3 index of the third vertex
2229 * \param[in] v4 index of the fourth vertex
2230 * \return the index of the newly created tetrahedron
2231 */
2232 index_t new_tetrahedron(
2233 index_t v1, index_t v2,
2234 index_t v3, index_t v4
2235 ) {
2236 index_t result = new_tetrahedron();
2237 cell_to_v_store_[4 * result] = v1;
2238 cell_to_v_store_[4 * result + 1] = v2;
2239 cell_to_v_store_[4 * result + 2] = v3;
2240 cell_to_v_store_[4 * result + 3] = v4;
2241 return result;
2242 }
2243
2244 /**
2245 * \brief Finds the index of an integer in an array of four integers.
2246 * \param[in] T a const pointer to an array of four integers
2247 * \param[in] v the integer to retrieve in \p T
2248 * \return the index (0,1,2 or 3) of \p v in \p T
2249 * \pre The four entries of \p T are different and one of them is
2250 * equal to \p v.
2251 */
2252 static index_t find_4(const index_t* T, index_t v) {
2253 // The following expression is 10% faster than using
2254 // if() statements. This uses the C++ norm, that
2255 // ensures that the 'true' boolean value converted to
2256 // an int is always 1. With most compilers, this avoids
2257 // generating branching instructions.
2258 // Thank to Laurent Alonso for this idea.
2259 // Note: Laurent also has this version:
2260 // (T[0] != v)+(T[2]==v)+2*(T[3]==v)
2261 // that avoids a *3 multiply, but it is not faster in
2262 // practice.
2263 index_t result = index_t(
2264 (T[1] == v) | ((T[2] == v) * 2) | ((T[3] == v) * 3)
2265 );
2266 // Sanity check, important if it was T[0], not explicitly
2267 // tested (detects input that does not meet the precondition).
2268 geo_debug_assert(T[result] == v);
2269 return result;
2270 }
2271
2272
2273 /**
2274 * \brief Finds the index of an integer in an array of four integers.
2275 * \param[in] T a const pointer to an array of four integers
2276 * \param[in] v the real vertex to retrieve in \p T
2277 * \return the index (0,1,2 or 3) of \p v in \p T
2278 * \pre The four entries of \p T are different and one of them is
2279 * equal to \p v.
2280 */
2281 index_t find_4_periodic(const index_t* T, index_t v) const {
2282
2283 // v needs to be a real vertex.
2284 geo_debug_assert(periodic_vertex_instance(v) == 0);
2285
2286 geo_debug_assert(
2287 T[0] != NO_INDEX && T[1] != NO_INDEX &&
2288 T[2] != NO_INDEX && T[3] != NO_INDEX
2289 );
2290
2291 // The following expression is 10% faster than using
2292 // if() statements. This uses the C++ norm, that
2293 // ensures that the 'true' boolean value converted to
2294 // an int is always 1. With most compilers, this avoids
2295 // generating branching instructions.
2296 // Thank to Laurent Alonso for this idea.
2297 // Note: Laurent also has this version:
2298 // (T[0] != v)+(T[2]==v)+2*(T[3]==v)
2299 // that avoids a *3 multiply, but it is not faster in
2300 // practice.
2301 index_t result = index_t(
2302 ( periodic_vertex_real(index_t(T[1])) == v) |
2303 ((periodic_vertex_real(index_t(T[2])) == v) * 2) |
2304 ((periodic_vertex_real(index_t(T[3])) == v) * 3)
2305 );
2306
2307 // Sanity check, important if it was T[0], not explicitly
2308 // tested (detects input that does not meet the precondition).
2309 geo_debug_assert(periodic_vertex_real(index_t(T[result])) == v);
2310 return result;
2311 }
2312
2313
2314 /**
2315 * \brief Wakes up all the threads that are waiting for
2316 * this thread.
2317 */
2318 void send_event() {
2319 cond_.notify_all();
2320 }
2321
2322 /**
2323 * \brief Waits for a thread.
2324 * \details Sleeps until thread \p t calls send_event().
2325 * \param[in] t index of the thread
2326 * \pre t < nb_threads()
2327 */
2328 void wait_for_event(index_t t) {
2329 // Fixed by Hiep Vu: enlarged critical section (contains
2330 // now the test (!thrd->finished)
2331 PeriodicDelaunay3dThread* thrd = thread(t);
2332 // RAII: ctor locks, dtor unlocks
2333 std::unique_lock<std::mutex> L(thrd->mutex_);
2334 if(!thrd->finished_) {
2335 thrd->cond_.wait(L);
2336 }
2337 }
2338
2339 /****** iterative stellate_conflict_zone *****************/
2340
2341 /**
2342 * \brief Used to represent the stack in the
2343 * (de-recursified) stellate_conflict_zone_iterative()
2344 * function.
2345 */
2346 class StellateConflictStack {
2347 public:
2348
2349 /**
2350 * \brief Pushes a new frame onto the stack.
2351 * \details This also creates the local variables (they are
2352 * left uninitialized).
2353 * \param[in] t1 index of a tetrahedron on the border of
2354 * the conflict zone
2355 * \param[in] t1fbord index of the facet of \p t1 that is
2356 * on the border of the conflict zone
2357 * \param[in] t1fprev index of the facet of \p t1 that we
2358 * come from, or NO_INDEX if \p t1 is the first tetrahedron
2359 */
2360 void push(index_t t1, index_t t1fbord, index_t t1fprev) {
2361 store_.resize(store_.size()+1);
2362 top().t1 = t1;
2363 top().t1fbord = Numeric::uint8(t1fbord);
2364 top().t1fprev = Numeric::uint8(t1fprev);
2365 }
2366
2367 /**
2368 * \brief Saves local variables into the current stack frame.
2369 * \param[in] new_t the index of the newly created tetrahedron
2370 * \param[in] t1ft2 the facet of t1 that is adjacent to t2
2371 * \param[in] t2ft1 the facet of t2 that is adjacent to t1
2372 */
2373 void save_locals(index_t new_t, index_t t1ft2, index_t t2ft1) {
2374 geo_debug_assert(!empty());
2375 top().new_t = new_t;
2376 top().t1ft2 = Numeric::uint8(t1ft2);
2377 top().t2ft1 = Numeric::uint8(t2ft1);
2378 }
2379
2380 /**
2381 * \brief Gets the parameters from the current stack frame.
2382 * \param[out] t1 index of a tetrahedron on the border of
2383 * the conflict zone
2384 * \param[out] t1fbord index of the facet of \p t1 that is
2385 * on the border of the conflict zone
2386 * \param[out] t1fprev index of the facet of \p t1 that we
2387 * come from, or NO_INDEX if \p t1 is the first tetrahedron
2388 */
2389 void get_parameters(
2390 index_t& t1, index_t& t1fbord, index_t& t1fprev
2391 ) const {
2392 geo_debug_assert(!empty());
2393 t1 = top().t1;
2394 t1fbord = index_t(top().t1fbord);
2395 t1fprev = index_t(top().t1fprev);
2396 }
2397
2398
2399 /**
2400 * \brief Gets the local variables from the current stack frame.
2401 * \param[out] new_t the index of the newly created tetrahedron
2402 * \param[out] t1ft2 the facet of t1 that is adjacent to t2
2403 * \param[out] t2ft1 the facet of t2 that is adjacent to t1
2404 */
2405 void get_locals(
2406 index_t& new_t, index_t& t1ft2, index_t& t2ft1
2407 ) const {
2408 geo_debug_assert(!empty());
2409 new_t = top().new_t;
2410 t1ft2 = index_t(top().t1ft2);
2411 t2ft1 = index_t(top().t2ft1);
2412 }
2413
2414 /**
2415 * \brief Pops a stack frame.
2416 */
2417 void pop() {
2418 geo_debug_assert(!empty());
2419 store_.pop_back();
2420 }
2421
2422 /**
2423 * \brief Tests whether the stack is empty.
2424 * \retval true if the stack is empty
2425 * \retval false otherwise
2426 */
2427 bool empty() const {
2428 return store_.empty();
2429 }
2430
2431 private:
2432
2433 /**
2434 * \brief The parameters and local
2435 * variables stored in a stack frame.
2436 */
2437 struct Frame {
2438 // Parameters
2439 index_t t1;
2440 index_t new_t;
2441 Numeric::uint8 t1fbord ;
2442
2443 // Local variables
2444 Numeric::uint8 t1fprev ;
2445 Numeric::uint8 t1ft2 ;
2446 Numeric::uint8 t2ft1 ;
2447 };
2448
2449 /**
2450 * \brief Gets the top of the stack.
2451 * \return a modifiable reference to the Frame on
2452 * the top of the stack
2453 * \pre !empty()
2454 */
2455 Frame& top() {
2456 geo_debug_assert(!empty());
2457 return *store_.rbegin();
2458 }
2459
2460 /**
2461 * \brief Gets the top of the stack.
2462 * \return a const reference to the Frame on
2463 * the top of the stack
2464 * \pre !empty()
2465 */
2466 const Frame& top() const {
2467 geo_debug_assert(!empty());
2468 return *store_.rbegin();
2469 }
2470
2471 std::vector<Frame> store_;
2472 };
2473
2474 /**
2475 * \brief Creates a star of tetrahedra filling the conflict
2476 * zone.
2477 * \details For each tetrahedron facet on the border of the
2478 * conflict zone, a new tetrahedron is created, resting on
2479 * the facet and incident to vertex \p v. The function is
2480 * called recursively until the entire conflict zone is filled.
2481 * \param[in] v the index of the point to be inserted
2482 * \param[in] t1 index of a tetrahedron on the border
2483 * of the conflict zone.
2484 * \param[in] t1fbord index of the facet along which \p t_bndry
2485 * is incident to the border of the conflict zone
2486 * \param[in] t1fprev the facet of \p t_bndry connected to the
2487 * tetrahedron that \p t_bndry was reached from, or NO_INDEX
2488 * if it is the first tetrahedron.
2489 * \return the index of one the newly created tetrahedron
2490 */
2491 index_t stellate_conflict_zone_iterative(
2492 index_t v, index_t t1, index_t t1fbord,
2493 index_t t1fprev = NO_INDEX
2494 ) {
2495 // This function is de-recursified because some degenerate
2496 // inputs can cause stack overflow (system stack is limited to
2497 // a few megs). For instance, it can happen when a large number
2498 // of points are on the same sphere exactly.
2499
2500 // To de-recursify, it uses class StellateConflictStack
2501 // that emulates system's stack for storing functions's
2502 // parameters and local variables in all the nested stack
2503 // frames.
2504
2505 S2_.push(t1, t1fbord, t1fprev);
2506
2507 index_t new_t; // the newly created tetrahedron.
2508
2509 index_t t1ft2; // traverses the 4 facets of t1.
2510
2511 index_t t2; // the tetrahedron on the border of
2512 // the conflict zone that shares an
2513 // edge with t1 along t1ft2.
2514
2515 index_t t2fbord; // the facet of t2 on the border of
2516 // the conflict zone.
2517
2518 index_t t2ft1; // the facet of t2 that is incident to t1.
2519
2520 entry_point:
2521 S2_.get_parameters(t1, t1fbord, t1fprev);
2522
2523
2524 geo_debug_assert(owns_tet(t1));
2525 geo_debug_assert(tet_adjacent(t1,t1fbord) != NO_INDEX);
2526 geo_debug_assert(owns_tet(tet_adjacent(t1,t1fbord)));
2527 geo_debug_assert(tet_is_marked_as_conflict(t1));
2528 geo_debug_assert(
2529 !tet_is_marked_as_conflict(tet_adjacent(t1,t1fbord))
2530 );
2531
2532 // Create new tetrahedron with same vertices as t_bndry
2533
2534 new_t = new_tetrahedron(
2535 tet_vertex(t1,0),
2536 tet_vertex(t1,1),
2537 tet_vertex(t1,2),
2538 tet_vertex(t1,3)
2539 );
2540
2541 index_t tbord = tet_adjacent(t1,t1fbord);
2542
2543 // We generate the tetrahedron with the three vertices
2544 // of the tet outside the conflict zone and the newly
2545 // created vertex in the local frame of the tet outside
2546 // the conflict zone.
2547
2548 // Replace in new_t the vertex opposite to t1fbord with v
2549 set_tet_vertex(new_t, t1fbord, v);
2550
2551 {
2552 // Connect new_t with t1's neighbor across t1fbord
2553 set_tet_adjacent(new_t, t1fbord, tbord);
2554 set_tet_adjacent(tbord, find_tet_adjacent(tbord,t1), new_t);
2555 }
2556
2557 // Lookup new_t's neighbors across its three other
2558 // facets and connect them
2559 for(t1ft2=0; t1ft2<4; ++t1ft2) {
2560
2561 if(t1ft2 == t1fprev || tet_adjacent(new_t,t1ft2) != NO_INDEX) {
2562 continue;
2563 }
2564
2565 // Get t1's neighbor along the border of the conflict zone
2566 if(!get_neighbor_along_conflict_zone_border(
2567 t1,t1fbord,t1ft2, t2,t2fbord,t2ft1
2568 )) {
2569 // If t1's neighbor is not a new tetrahedron,
2570 // create a new tetrahedron through a recursive call.
2571 S2_.save_locals(new_t, t1ft2, t2ft1);
2572 S2_.push(t2, t2fbord, t2ft1);
2573 goto entry_point;
2574
2575 return_point:
2576 // This is the return value of the called function.
2577 index_t result = new_t;
2578 S2_.pop();
2579
2580 // Special case: we were in the outermost frame,
2581 // then we (truly) return from the function.
2582 if(S2_.empty()) {
2583 return result;
2584 }
2585
2586 S2_.get_parameters(t1, t1fbord, t1fprev);
2587 S2_.get_locals(new_t, t1ft2, t2ft1);
2588 t2 = result;
2589 }
2590
2591 set_tet_adjacent(t2, t2ft1, new_t);
2592 set_tet_adjacent(new_t, t1ft2, t2);
2593 }
2594
2595 // Except for the initial call (see "Special case" above),
2596 // the nested calls all come from the same location,
2597 // thus there is only one possible return point
2598 // (no need to push any return address).
2599 goto return_point;
2600 }
2601
2602 /**
2603 * \brief Finds the neighbor of a tetrahedron on the border of the
2604 * conflict zone.
2605 * \details This function is used by stellate_conflict_zone_iterative()
2606 * \param[in] t1 a tetrahedron on the border of the conflict zone
2607 * \param[in] t1fborder the local facet index of \p t1 along which it
2608 * is on the border of the conflict zone
2609 * \param[in] t1ft2 the local facet index of \p t1 that will be
2610 * traversed
2611 * \param[out] t2 a tetrahedron on the border of the conflict zone,
2612 * with an edge common to facets \p t1fborder and \p t1ft2 of
2613 * tetrahedron \p t1
2614 * \param[out] t2fborder the local facet index of \p t2 along which it
2615 * is on the border of the conflict zone
2616 * \param[out] t2ft1 the local index of the facet of \p t2 that has a
2617 * common edge with facets \p t1fborder and \p t1ft2 of tetrahedron
2618 * \p t1
2619 * \retval true if \p t2 is a newly created tetrahedron
2620 * \retval false if \p t2 is an old tetrahedron in conflict
2621 */
2622 bool get_neighbor_along_conflict_zone_border(
2623 index_t t1,
2624 index_t t1fborder,
2625 index_t t1ft2,
2626 index_t& t2,
2627 index_t& t2fborder,
2628 index_t& t2ft1
2629 ) const {
2630
2631 // Note: this function is a bit long for an inline function,
2632 // but I observed a (modest) performance gain doing so.
2633
2634 // Find two vertices that are both on facets new_f and f1
2635 // (the edge around which we are turning)
2636 // This uses duality as follows:
2637 // Primal form (not used here):
2638 // halfedge_facet_[v1][v2] returns a facet that is incident
2639 // to both v1 and v2.
2640 // Dual form (used here):
2641 // halfedge_facet_[f1][f2] returns a vertex that both
2642 // f1 and f2 are incident to.
2643 index_t ev1 =
2644 tet_vertex(t1, index_t(halfedge_facet_[t1ft2][t1fborder]));
2645 index_t ev2 =
2646 tet_vertex(t1, index_t(halfedge_facet_[t1fborder][t1ft2]));
2647
2648 // Turn around edge [ev1,ev2] inside the conflict zone
2649 // until we reach again the boundary of the conflict zone.
2650 // Traversing inside the conflict zone is faster (as compared
2651 // to outside) since it traverses a smaller number of tets.
2652 index_t cur_t = t1;
2653 index_t cur_f = t1ft2;
2654 index_t next_t = tet_adjacent(cur_t,cur_f);
2655 while(tet_is_marked_as_conflict(next_t)) {
2656 geo_debug_assert(next_t != t1);
2657 cur_t = next_t;
2658 cur_f = get_facet_by_halfedge(cur_t,ev1,ev2);
2659 next_t = tet_adjacent(cur_t, cur_f);
2660 }
2661
2662 // At this point, cur_t is in conflict zone and
2663 // next_t is outside the conflict zone.
2664 index_t f12,f21;
2665 get_facets_by_halfedge(next_t, ev1, ev2, f12, f21);
2666 t2 = tet_adjacent(next_t,f21);
2667 index_t v_neigh_opposite = tet_vertex(next_t,f12);
2668 t2ft1 = find_tet_vertex(t2, v_neigh_opposite);
2669 t2fborder = cur_f;
2670
2671 // Test whether the found neighboring tet was created
2672 // (then return true) or is an old tet in conflict
2673 // (then return false).
2674 return(t2 != cur_t);
2675 }
2676
2677 /**
2678 * \brief Used by the (de-recursified)
2679 * stellate_conflict_zone_iterative() function.
2680 */
2681 StellateConflictStack S2_;
2682
2683 /*************************** debugging ************************/
2684
2685 /**
2686 * \brief For debugging purposes, displays a tetrahedron adjacency.
2687 * \param[in] t index of the tetrahedron to display.
2688 * \param[in] lf local index (0,1,2 or 3) of the tetrahedron
2689 * facet adjacenty to display.
2690 */
2691 void show_tet_adjacent(index_t t, index_t lf) const {
2692 index_t adj = tet_adjacent(t, lf);
2693 if(adj != NO_INDEX) {
2694 std::cerr << (tet_is_in_list(adj) ? '*' : ' ');
2695 }
2696 std::cerr << adj;
2697 std::cerr << ' ';
2698 }
2699
2700
2701 /**
2702 * \brief For debugging purposes, displays a tetrahedron.
2703 * \param[in] t index of the tetrahedron to display.
2704 */
2705 void show_tet(index_t t) const {
2706 std::cerr << "tet"
2707 << (tet_is_in_list(t) ? '*' : ' ')
2708 << t
2709 << ", v=["
2710 << tet_vertex(t, 0)
2711 << ' '
2712 << tet_vertex(t, 1)
2713 << ' '
2714 << tet_vertex(t, 2)
2715 << ' '
2716 << tet_vertex(t, 3)
2717 << "] adj=[";
2718 show_tet_adjacent(t, 0);
2719 show_tet_adjacent(t, 1);
2720 show_tet_adjacent(t, 2);
2721 show_tet_adjacent(t, 3);
2722 std::cerr << "] ";
2723
2724 for(index_t f = 0; f < 4; ++f) {
2725 std::cerr << 'f' << f << ':';
2726 for(index_t v = 0; v < 3; ++v) {
2727 std::cerr << tet_vertex(t, tet_facet_vertex(f,v))
2728 << ',';
2729 }
2730 std::cerr << ' ';
2731 }
2732 std::cerr << std::endl;
2733 }
2734
2735 public:
2736
2737 /**
2738 * \brief For debugging purposes, tests some combinatorial properties.
2739 */
2740 void check_combinatorics(bool verbose) const {
2741 if(verbose) {
2742 std::cerr << std::endl;
2743 }
2744 bool ok = true;
2745 std::vector<bool> v_has_tet(nb_vertices(), false);
2746 for(index_t t = 0; t < max_t(); ++t) {
2747 if(tet_is_free(t)) {
2748 if(verbose) {
2749 std::cerr << "-Deleted tet: ";
2750 show_tet(t);
2751 }
2752 } else {
2753 if(verbose) {
2754 std::cerr << "Checking tet: ";
2755 show_tet(t);
2756 }
2757 for(index_t lf = 0; lf < 4; ++lf) {
2758 if(tet_adjacent(t, lf) == NO_INDEX) {
2759 std::cerr << lf << ":Missing adjacent tet"
2760 << std::endl;
2761 ok = false;
2762 } else if(tet_adjacent(t, lf) == t) {
2763 std::cerr << lf << ":Tet is adjacent to itself"
2764 << std::endl;
2765 ok = false;
2766 } else {
2767 index_t t2 = tet_adjacent(t, lf);
2768 bool found = false;
2769 for(index_t lf2 = 0; lf2 < 4; ++lf2) {
2770 if(tet_adjacent(t2, lf2) == t) {
2771 found = true;
2772 }
2773 }
2774 if(!found) {
2775 std::cerr
2776 << lf
2777 << ":Adjacent link is not bidirectional"
2778 << std::endl;
2779 ok = false;
2780 }
2781 }
2782 }
2783 index_t nb_infinite = 0;
2784 for(index_t lv = 0; lv < 4; ++lv) {
2785 if(tet_vertex(t, lv) == NO_INDEX) {
2786 ++nb_infinite;
2787 }
2788 }
2789 if(nb_infinite > 1) {
2790 ok = false;
2791 std::cerr << "More than one infinite vertex"
2792 << std::endl;
2793 }
2794 }
2795 for(index_t lv = 0; lv < 4; ++lv) {
2796 index_t v = tet_vertex(t, lv);
2797 if(v != NO_INDEX && v != NOT_IN_LIST) {
2798 v_has_tet[periodic_vertex_real(v)] = true;
2799 }
2800 }
2801 }
2802
2803 index_t nb_v = nb_vertices();
2804 for(index_t v = 0; v < nb_v; ++v) {
2805 if(!v_has_tet[v]) {
2806 if(verbose) {
2807 std::cerr << "Vertex " << v
2808 << " is isolated (duplicated ?)" << std::endl;
2809 }
2810 }
2811 }
2812 geo_assert(ok);
2813 if(verbose) {
2814 std::cerr << std::endl;
2815 }
2816 std::cerr << std::endl << "Delaunay Combi OK" << std::endl;
2817 }
2818
2819
2820 /**
2821 * \brief For debugging purposes, test some geometrical properties.
2822 */
2823 void check_geometry(bool verbose) const {
2824 bool ok = true;
2825 for(index_t t = 0; t < max_t(); ++t) {
2826 if(!tet_is_free(t)) {
2827 index_t v0 = tet_vertex(t, 0);
2828 index_t v1 = tet_vertex(t, 1);
2829 index_t v2 = tet_vertex(t, 2);
2830 index_t v3 = tet_vertex(t, 3);
2831 for(index_t v = 0; v < nb_vertices(); ++v) {
2832 vec4 p = lifted_vertex(v);
2833 if(v == v0 || v == v1 || v == v2 || v == v3) {
2834 continue;
2835 }
2836 if(tet_is_in_conflict(t, v, p)) {
2837 ok = false;
2838 if(verbose) {
2839 std::cerr << "Tet " << t <<
2840 " is in conflict with vertex " << v
2841 << std::endl;
2842
2843 std::cerr << " offending tet: ";
2844 show_tet(t);
2845 }
2846 }
2847 }
2848 }
2849 }
2850 geo_assert(ok);
2851 std::cerr << std::endl << "Delaunay Geo OK" << std::endl;
2852 }
2853
2854 private:
2855 PeriodicDelaunay3d* master_;
2856 bool periodic_;
2857 vec3 period_;
2858 index_t nb_vertices_;
2859 const double* vertices_;
2860 const double* weights_;
2861 index_t* reorder_;
2862 index_t dimension_;
2863 index_t pool_begin_;
2864 index_t pool_end_;
2865 index_t max_t_;
2866 index_t used_tets_end_;
2867
2868 vector<index_t>& cell_to_v_store_;
2869 vector<index_t>& cell_to_cell_store_;
2870 vector<index_t>& cell_next_;
2871 CellStatusArray& cell_status_;
2872
2873 index_t first_free_;
2874 index_t nb_free_;
2875 bool memory_overflow_;
2876
2877 /** \brief used by find_conflict_zone_iterative() */
2878 struct SFrame {
2879
2880 SFrame() {
2881 }
2882
2883 SFrame(
2884 index_t t_in,
2885 index_t v_in,
2886 const vec4& p_in
2887 ) :
2888 t(t_in),
2889 v(v_in),
2890 p(p_in) {
2891 }
2892
2893 SFrame(
2894 const SFrame& rhs
2895 ):
2896 t(rhs.t),
2897 v(rhs.v),
2898 p(rhs.p) {
2899 }
2900
2901 SFrame& operator=(const SFrame& rhs) {
2902 t = rhs.t;
2903 v = rhs.v;
2904 p = rhs.p;
2905 return *this;
2906 }
2907
2908 index_t t;
2909 index_t v;
2910 vec4 p;
2911 };
2912
2913 vector<SFrame> S_;
2914 index_t nb_tets_to_create_;
2915 index_t t_boundary_; // index of a tet,facet on the bndry
2916 index_t f_boundary_; // of the conflict zone.
2917
2918 bool direction_;
2919 index_t work_begin_;
2920 index_t work_rbegin_;
2921 index_t b_hint_;
2922 index_t e_hint_;
2923 bool finished_;
2924
2925 // Whenever acquire_tet() is unsuccessful, contains
2926 // the index of the thread that was interfering
2927 // (shifted to the left by 1 !!)
2928 CellStatusArray::thread_index_t interfering_thread_;
2929
2930 #ifdef GEO_DEBUG
2931 index_t nb_acquired_tets_;
2932 #endif
2933
2934 vector<index_t> tets_to_delete_;
2935 vector<index_t> tets_to_release_;
2936
2937 index_t nb_rollbacks_;
2938 index_t nb_failed_locate_;
2939
2940 std::condition_variable cond_;
2941 std::mutex mutex_;
2942
2943 bool has_empty_cells_;
2944
2945 /**
2946 * \brief Gives the indexing of tetrahedron facet
2947 * vertices.
2948 * \details tet_facet_vertex[lf][lv] gives the
2949 * local vertex index (in 0,1,2,3) from a
2950 * local facet index lf (in 0,1,2,3) and a
2951 * local vertex index within the facet (in 0,1,2).
2952 */
2953 static char tet_facet_vertex_[4][3];
2954
2955 /**
2956 * \brief Gives a local facet index by
2957 * halfedge extremities local indices.
2958 */
2959 static char halfedge_facet_[4][4];
2960
2961 /**
2962 * \brief Optimized representation of triangles on
2963 * the border of the cavity, for fast generation of
2964 * tetrahedra.
2965 */
2966 Cavity cavity_;
2967
2968 /**
2969 * \brief Statistics for locate()
2970 */
2971 index_t nb_traversed_tets_;
2972 };
2973
2974
2975 char PeriodicDelaunay3dThread::halfedge_facet_[4][4] = {
2976 {4, 2, 3, 1},
2977 {3, 4, 0, 2},
2978 {1, 3, 4, 0},
2979 {2, 0, 1, 4}
2980 };
2981
2982 // tet facet vertex is such that the tetrahedron
2983 // formed with:
2984 // vertex lv
2985 // tet_facet_vertex[lv][0]
2986 // tet_facet_vertex[lv][1]
2987 // tet_facet_vertex[lv][2]
2988 // has the same orientation as the original tetrahedron for
2989 // any vertex lv.
2990
2991 char PeriodicDelaunay3dThread::tet_facet_vertex_[4][3] = {
2992 {1, 2, 3},
2993 {0, 3, 2},
2994 {3, 0, 1},
2995 {1, 0, 2}
2996 };
2997
2998
2999 /*************************************************************************/
3000
3001 PeriodicDelaunay3d::PeriodicDelaunay3d(
3002 bool periodic, double period
3003 ) :
3004 Delaunay(3),
3005 periodic_(periodic),
3006 period_(period,period,period),
3007 weights_(nullptr),
3008 update_periodic_v_to_cell_(false),
3009 has_empty_cells_(false),
3010 nb_reallocations_(0),
3011 convex_cell_exact_predicates_(true)
3012 {
3013 debug_mode_ = CmdLine::get_arg_bool("dbg:delaunay");
3014 verbose_debug_mode_ = CmdLine::get_arg_bool("dbg:delaunay_verbose");
3015 debug_mode_ = (debug_mode_ || verbose_debug_mode_);
3016 benchmark_mode_ = CmdLine::get_arg_bool("dbg:delaunay_benchmark");
3017 detailed_benchmark_mode_ =
3018 CmdLine::get_arg_bool("dbg:detailed_delaunay_benchmark");
3019 nb_vertices_non_periodic_ = 0;
3020 delaunay_citations();
3021 }
3022
3023 PeriodicDelaunay3d::PeriodicDelaunay3d(
3024 const vec3& period, bool periodic
3025 ) :
3026 Delaunay(3),
3027 periodic_(periodic),
3028 period_(period),
3029 weights_(nullptr),
3030 update_periodic_v_to_cell_(false),
3031 has_empty_cells_(false),
3032 nb_reallocations_(0),
3033 convex_cell_exact_predicates_(true)
3034 {
3035 debug_mode_ = CmdLine::get_arg_bool("dbg:delaunay");
3036 verbose_debug_mode_ = CmdLine::get_arg_bool("dbg:delaunay_verbose");
3037 debug_mode_ = (debug_mode_ || verbose_debug_mode_);
3038 benchmark_mode_ = CmdLine::get_arg_bool("dbg:delaunay_benchmark");
3039 detailed_benchmark_mode_ =
3040 CmdLine::get_arg_bool("dbg:detailed_delaunay_benchmark");
3041 nb_vertices_non_periodic_ = 0;
3042 delaunay_citations();
3043 }
3044
3045 void PeriodicDelaunay3d::set_vertices(
3046 index_t nb_vertices, const double* vertices
3047 ) {
3048 has_empty_cells_ = false;
3049
3050 #ifndef GARGANTUA
3051 {
3052 Numeric::uint64 expected_max_index =
3053 Numeric::uint64(nb_vertices) * 7 * 4;
3054 if(periodic_) {
3055 expected_max_index *= 2;
3056 }
3057 if(expected_max_index > Numeric::uint64(INT32_MAX)) {
3058 Logger::err("OTM") << "indices will overflow" << std::endl;
3059 Logger::err("OTM")
3060 << "recompile with -DGARGANTUA "
3061 << "to activate 64bit indices" << std::endl;
3062 exit(0);
3063 }
3064 }
3065 #endif
3066
3067 if(periodic_) {
3068 PCK::set_SOS_mode(PCK::SOS_LEXICO);
3069 }
3070
3071 Stopwatch W("BRIO", benchmark_mode_);
3072 nb_vertices_non_periodic_ = nb_vertices;
3073
3074 Delaunay::set_vertices(nb_vertices, vertices);
3075 // Reorder the points
3076 if(do_reorder_) {
3077 compute_BRIO_order(
3078 nb_vertices, vertex_ptr(0), reorder_,
3079 3, dimension(),
3080 64, 0.125,
3081 &levels_
3082 );
3083 } else {
3084 reorder_.resize(nb_vertices);
3085 for(index_t i = 0; i < nb_vertices; ++i) {
3086 reorder_[i] = i;
3087 }
3088 geo_debug_assert(levels_[0] == 0);
3089 geo_debug_assert(levels_[levels_.size()-1] == nb_vertices);
3090 }
3091 }
3092
3093 void PeriodicDelaunay3d::set_weights(const double* weights) {
3094 has_empty_cells_ = false;
3095 weights_ = weights;
3096 }
3097
3098 void PeriodicDelaunay3d::compute() {
3099
3100 stats_.reset();
3101
3102 Stopwatch W_tot("total",false);
3103
3104 has_empty_cells_ = false;
3105
3106 if(periodic_) {
3107 reorder_.resize(nb_vertices_non_periodic_);
3108 }
3109
3110 {
3111 Stopwatch W("DelInternal", detailed_benchmark_mode_);
3112
3113 index_t expected_tetra = nb_vertices() * 7;
3114
3115 // Everything is allocated here, including for handling
3116 // periodic boundary conditions, much later. We need to
3117 // allocate sufficient space to have good chances of
3118 // inserting most of the additional points in parallel
3119 // (in insert_with_BRIO())
3120
3121 if(periodic_) {
3122 expected_tetra = index_t(double(expected_tetra)* 1.2);
3123 }
3124
3125 // Allocate the tetrahedra
3126 cell_to_v_store_.assign(expected_tetra * 4, NO_INDEX);
3127 cell_to_cell_store_.assign(expected_tetra * 4, NO_INDEX);
3128 cell_next_.assign(expected_tetra,NO_INDEX);
3129 cell_status_.resize(expected_tetra);
3130
3131 // Create the threads
3132 // The maximum number of threads is limited by the number
3133 // of bits used by cell_status_ (see delaunay_sync.h)
3134 index_t nb_threads = std::min(
3135 Process::maximum_concurrent_threads(),
3136 CellStatusArray::MAX_THREADS
3137 );
3138 index_t pool_size = expected_tetra / nb_threads;
3139 if (pool_size == 0) {
3140 // There are more threads than expected_tetra
3141 pool_size = 1;
3142 nb_threads = expected_tetra;
3143 }
3144 index_t pool_begin = 0;
3145 threads_.clear();
3146 for(index_t t=0; t<nb_threads; ++t) {
3147 index_t pool_end =
3148 (t == nb_threads - 1) ? expected_tetra
3149 : pool_begin + pool_size;
3150 threads_.push_back(
3151 new PeriodicDelaunay3dThread(this, pool_begin, pool_end)
3152 );
3153 pool_begin = pool_end;
3154 }
3155
3156
3157 // Create first tetrahedron and triangulate first set of points
3158 // in sequential mode.
3159
3160 PeriodicDelaunay3dThread* thread0 = thread(0);
3161 thread0->create_first_tetrahedron();
3162 {
3163 Stopwatch Wmain("DelMain", detailed_benchmark_mode_);
3164 insert_vertices_with_BRIO("DelMain", levels_);
3165 stats_.phase_0_t_ = Wmain.elapsed_time();
3166 }
3167
3168 if(has_empty_cells_) {
3169 return;
3170 }
3171
3172 if(periodic_) {
3173 Stopwatch W12("DelPhaseI-II", detailed_benchmark_mode_);
3174 handle_periodic_boundaries();
3175 }
3176
3177 if(has_empty_cells_) {
3178 return;
3179 }
3180 }
3181
3182 if(debug_mode_) {
3183 for(index_t i=0; i<threads_.size(); ++i) {
3184 std::cerr << i << " : " <<
3185 static_cast<PeriodicDelaunay3dThread*>(threads_[i].get())
3186 ->max_t() << std::endl;
3187 }
3188
3189 thread(0)->check_combinatorics(verbose_debug_mode_);
3190 thread(0)->check_geometry(verbose_debug_mode_);
3191 }
3192
3193 index_t nb_tets = 0;
3194 {
3195 nb_tets = compress();
3196
3197 set_arrays(
3198 nb_tets,
3199 cell_to_v_store_.data(),
3200 cell_to_cell_store_.data()
3201 );
3202
3203 // We need v_to_cell even if CICL is not stored.
3204 if(!stores_cicl()) {
3205 update_v_to_cell();
3206 }
3207 }
3208
3209 if(periodic_) {
3210 #ifdef GEO_DEBUG
3211 FOR(v, nb_vertices_non_periodic_) {
3212 index_t t = v_to_cell_[v];
3213 geo_assert(t == NO_INDEX || t < nb_tets);
3214 }
3215 #endif
3216 }
3217 stats_.total_t_ = W_tot.elapsed_time();
3218 if(benchmark_mode_) {
3219 Logger::out("Delaunay") << stats_.to_string() << std::endl;
3220 }
3221 }
3222
3223 index_t PeriodicDelaunay3d::compress(bool shrink) {
3224
3225 Stopwatch W("Compress",detailed_benchmark_mode_);
3226
3227 // Compress cell_to_v_store_ and cell_to_cell_store_
3228 // (remove free and virtual tetrahedra).
3229 // Since cell_next_ is not used at this point,
3230 // we reuse it for storing the conversion array that
3231 // maps old tet indices to new tet indices
3232 // Note: tet_is_real() uses the previous value of
3233 // cell_next(), but we are processing indices
3234 // in increasing order and since old2new[t] is always
3235 // smaller or equal to t, we never overwrite a value
3236 // before needing it.
3237
3238
3239 PeriodicDelaunay3dThread* thread0 = thread(0);
3240 vector<index_t>& old2new = cell_next_;
3241 index_t nb_tets = 0;
3242 index_t nb_tets_to_delete = 0;
3243
3244 {
3245 // Classify tets in parallel (on very large data sets,
3246 // >= 100M points, it gains a little bit of time)
3247 parallel_for(0, thread0->max_t(), [&,this](index_t t) {
3248 if(
3249 (keep_infinite_ && !thread0->tet_is_free(t)) ||
3250 (periodic_ && thread0->tet_is_real_non_periodic(t)) ||
3251 (!periodic_ && thread0->tet_is_real(t))
3252 ) {
3253 old2new[t] = 0; // keep tetrahedron
3254 } else {
3255 old2new[t] = NO_INDEX; // discard tetrahedron
3256 }
3257 });
3258
3259 // Compress the tet array
3260 for(index_t t = 0; t < thread0->max_t(); ++t) {
3261 if(old2new[t] != NO_INDEX) {
3262 if(t != nb_tets) {
3263 Memory::copy(
3264 &cell_to_v_store_[nb_tets * 4],
3265 &cell_to_v_store_[t * 4],
3266 4 * sizeof(index_t)
3267 );
3268 Memory::copy(
3269 &cell_to_cell_store_[nb_tets * 4],
3270 &cell_to_cell_store_[t * 4],
3271 4 * sizeof(index_t)
3272 );
3273 }
3274 old2new[t] = nb_tets;
3275 ++nb_tets;
3276 } else {
3277 ++nb_tets_to_delete;
3278 }
3279 }
3280
3281 if(shrink) {
3282 cell_to_v_store_.resize(4 * nb_tets);
3283 cell_to_cell_store_.resize(4 * nb_tets);
3284 }
3285
3286 // Apply permutation to cell_to_cell_ array
3287 parallel_for(0, 4*nb_tets, [this, &old2new](index_t i) {
3288 index_t t = cell_to_cell_store_[i];
3289 geo_debug_assert(t != NO_INDEX);
3290 t = old2new[t];
3291 // Note: t can be equal to -1 when a real tet is
3292 // adjacent to a virtual one (and this is how the
3293 // rest of Vorpaline expects to see tets on the
3294 // border).
3295 geo_debug_assert(!(keep_infinite_ && (t == NO_INDEX)));
3296 cell_to_cell_store_[i] = t;
3297 });
3298 }
3299
3300 // In "keep_infinite" mode, we reorder the cells in such
3301 // a way that finite cells have indices [0..nb_finite_cells_-1]
3302 // and infinite cells have indices [nb_finite_cells_ .. nb_cells_-1]
3303
3304 if(keep_infinite_) {
3305 nb_finite_cells_ = 0;
3306 index_t finite_ptr = 0;
3307 index_t infinite_ptr = nb_tets - 1;
3308 for(;;) {
3309 while(thread0->tet_is_finite(finite_ptr)) {
3310 old2new[finite_ptr] = finite_ptr;
3311 ++finite_ptr;
3312 ++nb_finite_cells_;
3313 }
3314 while(!thread0->tet_is_finite(infinite_ptr)) {
3315 old2new[infinite_ptr] = infinite_ptr;
3316 --infinite_ptr;
3317 }
3318 if(finite_ptr > infinite_ptr) {
3319 break;
3320 }
3321 old2new[finite_ptr] = infinite_ptr;
3322 old2new[infinite_ptr] = finite_ptr;
3323 ++nb_finite_cells_;
3324 for(index_t lf=0; lf<4; ++lf) {
3325 std::swap(
3326 cell_to_cell_store_[4*finite_ptr + lf],
3327 cell_to_cell_store_[4*infinite_ptr + lf]
3328 );
3329 }
3330 for(index_t lv=0; lv<4; ++lv) {
3331 std::swap(
3332 cell_to_v_store_[4*finite_ptr + lv],
3333 cell_to_v_store_[4*infinite_ptr + lv]
3334 );
3335 }
3336 ++finite_ptr;
3337 --infinite_ptr;
3338 }
3339 parallel_for(0, 4*nb_tets, [this, &old2new](index_t i) {
3340 index_t t = cell_to_cell_store_[i];
3341 geo_debug_assert(t != NO_INDEX);
3342 t = old2new[t];
3343 geo_debug_assert(t != NO_INDEX);
3344 cell_to_cell_store_[i] = t;
3345 });
3346 }
3347
3348
3349 if(detailed_benchmark_mode_) {
3350 Logger::out("DelCompress")
3351 << "max tets " << thread0->max_t()
3352 << std::endl;
3353
3354 Logger::out("DelCompress")
3355 << "Final number of tets " << nb_tets
3356 << std::endl;
3357 if(keep_infinite_) {
3358 Logger::out("DelCompress")
3359 << "Removed " << nb_tets_to_delete
3360 << " tets (free list)"
3361 << " : "
3362 << double(nb_tets_to_delete)*100.0/double(nb_tets) << "%"
3363 << std::endl;
3364 } else {
3365 Logger::out("DelCompress")
3366 << "Removed " << nb_tets_to_delete
3367 << " tets (free list and infinite)"
3368 << " : "
3369 << double(nb_tets_to_delete)*100.0/double(nb_tets) << "%"
3370 << std::endl;
3371 }
3372 }
3373
3374 for(index_t t=0; t<nb_tets; ++t) {
3375 cell_next_[t] = PeriodicDelaunay3dThread::NOT_IN_LIST;
3376 }
3377
3378 // Disconnect tets that were connected to infinite tets
3379 if(periodic_) {
3380 parallel_for(0, 4*nb_tets, [this,nb_tets](index_t i) {
3381 if(cell_to_cell_store_[i] >= nb_tets) {
3382 cell_to_cell_store_[i] = NO_INDEX;
3383 }
3384 });
3385 #ifdef GEO_DEBUG
3386 for(index_t i=0; i<4*nb_tets; ++i) {
3387 geo_debug_assert(cell_to_v_store_[i] != NO_INDEX);
3388 }
3389 #endif
3390 }
3391 return nb_tets;
3392 }
3393
3394 index_t PeriodicDelaunay3d::nearest_vertex(const double* p) const {
3395 // TODO
3396 return Delaunay::nearest_vertex(p);
3397 }
3398
3399 void PeriodicDelaunay3d::set_BRIO_levels(const vector<index_t>& levels) {
3400 levels_ = levels;
3401 }
3402
3403 void PeriodicDelaunay3d::update_v_to_cell() {
3404 geo_assert(!is_locked_); // Not thread-safe
3405 is_locked_ = true;
3406
3407 // Optimized version for large scale optimal transport,
3408 // can be removed (all cases treated)
3409 if(!update_periodic_v_to_cell_ && !keeps_infinite()) {
3410 v_to_cell_.assign(nb_vertices(), NO_INDEX);
3411 parallel_for(0, nb_cells(), [this](index_t c) {
3412 for(index_t lv = 0; lv < 4; lv++) {
3413 index_t v = cell_vertex(c, lv);
3414 // discriminates both vertex at infinity (NO_INDEX)
3415 // and VERTEX_OF_DELETED_TET (index_t(-2)).
3416 if(v < nb_vertices_non_periodic_) {
3417 v_to_cell_[v] = c;
3418 }
3419 }
3420 });
3421 is_locked_ = false; // Do not forget to unlock !
3422 return;
3423 }
3424
3425
3426 // Note: if keeps_infinite is set, then infinite vertex
3427 // tet chaining is at t2v_[nb_vertices].
3428
3429 // Create periodic_v_to_cell_ structure in compressed row
3430 // storage format.
3431
3432 // It was used in previous version for handling periodic boundary
3433 // conditions based on ConvexCell, it is no longer the case, new
3434 // code solely uses tetrahedra. It is kept here for reference for
3435 // implementing the distributed version (using ConvexCell can save
3436 // points tranfers).
3437
3438 if(update_periodic_v_to_cell_) {
3439 periodic_v_to_cell_rowptr_.resize(nb_vertices_non_periodic_ + 1);
3440 periodic_v_to_cell_rowptr_[0] = 0;
3441 index_t cur = 0;
3442 for(index_t v=0; v<nb_vertices_non_periodic_; ++v) {
3443 cur += pop_count(vertex_instances_[v])-1;
3444 periodic_v_to_cell_rowptr_[v+1] = cur;
3445 }
3446 periodic_v_to_cell_data_.assign(cur, NO_INDEX);
3447 }
3448
3449 if(keeps_infinite()) {
3450 geo_assert(!periodic_);
3451 v_to_cell_.assign(nb_vertices()+1, NO_INDEX);
3452 for(index_t c = 0; c < nb_cells(); c++) {
3453 for(index_t lv = 0; lv < 4; lv++) {
3454 index_t v = cell_vertex(c, lv);
3455 if(v == NO_INDEX) {
3456 v = nb_vertices();
3457 }
3458 v_to_cell_[v] = c;
3459 }
3460 }
3461 } else {
3462 v_to_cell_.assign(nb_vertices(), NO_INDEX);
3463 for(index_t c = 0; c < nb_cells(); c++) {
3464 for(index_t lv = 0; lv < 4; lv++) {
3465 index_t v = cell_vertex(c, lv);
3466 if(v < nb_vertices_non_periodic_) {
3467 v_to_cell_[v] = c;
3468 } else if(
3469 update_periodic_v_to_cell_ &&
3470 v != NO_INDEX &&
3471 v != PeriodicDelaunay3dThread::VERTEX_OF_DELETED_TET
3472 ) {
3473 index_t v_real = periodic_vertex_real(v);
3474 index_t v_instance = periodic_vertex_instance(v);
3475
3476 geo_debug_assert(
3477 (vertex_instances_[v_real] & (1u << v_instance)) != 0
3478 );
3479
3480 index_t slot = pop_count(
3481 vertex_instances_[v_real] & ((1u << v_instance)-1)
3482 ) - 1;
3483
3484 periodic_v_to_cell_data_[
3485 periodic_v_to_cell_rowptr_[v_real] + slot
3486 ] = c;
3487 }
3488 }
3489 }
3490 }
3491
3492 is_locked_ = false;
3493 }
3494
3495 void PeriodicDelaunay3d::update_cicl() {
3496 // Note: updates CICL information only for the
3497 // corners that correspond to real vertices
3498 // (anyway, in the API we will be always
3499 // starting from a real vertex !)
3500
3501 geo_assert(!is_locked_); // Not thread-safe
3502 is_locked_ = true;
3503 cicl_.resize(4 * nb_cells());
3504
3505 for(index_t v = 0; v < nb_vertices_non_periodic_; ++v) {
3506 index_t t = v_to_cell_[v];
3507 if(t != NO_INDEX) {
3508 index_t lv = index(t, v);
3509 set_next_around_vertex(t, lv, t);
3510 }
3511 }
3512
3513 if(keeps_infinite()) {
3514
3515 {
3516 // Process the infinite vertex at index nb_vertices().
3517 index_t t = v_to_cell_[nb_vertices()];
3518 if(t != NO_INDEX) {
3519 index_t lv = index(t, NO_INDEX);
3520 set_next_around_vertex(t, lv, t);
3521 }
3522 }
3523
3524 for(index_t t = 0; t < nb_cells(); ++t) {
3525 for(index_t lv = 0; lv < 4; ++lv) {
3526 index_t v = cell_vertex(t, lv);
3527 index_t vv = (v == NO_INDEX) ? nb_vertices() : v;
3528 if(v_to_cell_[vv] != t) {
3529 index_t t1 = v_to_cell_[vv];
3530 index_t lv1 = index(t1, v);
3531 index_t t2 = next_around_vertex(t1, lv1);
3532 set_next_around_vertex(t1, lv1, t);
3533 set_next_around_vertex(t, lv, t2);
3534 }
3535 }
3536 }
3537
3538
3539 } else {
3540 for(index_t t = 0; t < nb_cells(); ++t) {
3541 for(index_t lv = 0; lv < 4; ++lv) {
3542 index_t v = cell_vertex(t, lv);
3543 if(v < nb_vertices_non_periodic_ && v_to_cell_[v] != t) {
3544 index_t t1 = v_to_cell_[v];
3545 index_t lv1 = index(t1, v);
3546 index_t t2 = next_around_vertex(t1, lv1);
3547 set_next_around_vertex(t1, lv1, t);
3548 set_next_around_vertex(t, lv, t2);
3549 }
3550 }
3551 }
3552 }
3553
3554 is_locked_ = false;
3555 }
3556
3557 void PeriodicDelaunay3d::get_incident_tets(
3558 index_t v, IncidentTetrahedra& W
3559 ) const {
3560
3561 geo_debug_assert(
3562 periodic_ || v < nb_vertices_non_periodic_
3563 );
3564
3565 W.clear_incident_tets();
3566
3567 index_t t = NO_INDEX;
3568 if(v < nb_vertices_non_periodic_) {
3569 t = v_to_cell_[v];
3570 } else {
3571 index_t v_real = periodic_vertex_real(v);
3572 index_t v_instance = periodic_vertex_instance(v);
3573
3574 geo_debug_assert(
3575 (vertex_instances_[v_real] & (1u << v_instance))!=0
3576 );
3577
3578 index_t slot = pop_count(
3579 vertex_instances_[v_real] & ((1u << v_instance)-1)
3580 ) - 1;
3581
3582 t = periodic_v_to_cell_data_[
3583 periodic_v_to_cell_rowptr_[v_real] + slot
3584 ];
3585 }
3586
3587 // Can happen: empty power cell.
3588 if(t == NO_INDEX) {
3589 return;
3590 }
3591
3592 // TODO: different version if CICL is stored ?
3593 {
3594 W.add_incident_tet(t);
3595 W.S.push(t);
3596 while(!W.S.empty()) {
3597 t = W.S.top();
3598 W.S.pop();
3599 const index_t* T = &(cell_to_v_store_[4 * t]);
3600 index_t lv = PeriodicDelaunay3dThread::find_4(T,v);
3601 index_t neigh = cell_to_cell_store_[4*t + (lv + 1)%4];
3602 if(neigh != NO_INDEX && !W.has_incident_tet(neigh)) {
3603 W.add_incident_tet(neigh);
3604 W.S.push(neigh);
3605 }
3606 neigh = cell_to_cell_store_[4*t + (lv + 2)%4];
3607 if(neigh != NO_INDEX && !W.has_incident_tet(neigh)) {
3608 W.add_incident_tet(neigh);
3609 W.S.push(neigh);
3610 }
3611 neigh = cell_to_cell_store_[4*t + (lv + 3)%4];
3612 if(neigh != NO_INDEX && !W.has_incident_tet(neigh)) {
3613 W.add_incident_tet(neigh);
3614 W.S.push(neigh);
3615 }
3616 }
3617 }
3618 }
3619
3620 inline double dist2(const double* p, const double* q) {
3621 return
3622 geo_sqr(p[0]-q[0]) +
3623 geo_sqr(p[1]-q[1]) +
3624 geo_sqr(p[2]-q[2]) ;
3625 }
3626
3627 /**
3628 * \brief Copies a Laguerre cell from the triangulation.
3629 * \param[in] i the index of the vertex of which the Laguerre cell
3630 * should be computed.
3631 * \param[out] C the Laguerre cell.
3632 * \param[out] W the vector of neighbor vertices indices.
3633 */
3634 void PeriodicDelaunay3d::copy_Laguerre_cell_from_Delaunay(
3635 GEO::index_t i,
3636 ConvexCell& C,
3637 IncidentTetrahedra& W
3638 ) const {
3639 // Create global vertex indices if not present.
3640 C.create_vglobal();
3641 C.clear();
3642
3643 // Create the vertex at infinity.
3644 C.create_vertex(vec4(0.0, 0.0, 0.0, 0.0), NO_INDEX);
3645
3646 GEO::vec3 Pi = vertex(i);
3647 double wi = weight(i);
3648 double Pi_len2 = Pi[0]*Pi[0] + Pi[1]*Pi[1] + Pi[2]*Pi[2];
3649
3650 if(stores_cicl()) {
3651 // Get neighbors and initialize cell from the tetrahedra in
3652 // 1-ring neighborhood of vertex.
3653 GEO::index_t t = GEO::index_t(vertex_cell(i));
3654 // Special case: Laguerre cell is empty (vertex has
3655 // no incident tet).
3656 if(t == NO_INDEX) {
3657 return;
3658 }
3659 do {
3660 GEO::index_t f = copy_Laguerre_cell_facet_from_Delaunay(
3661 i, Pi, wi, Pi_len2, t, C, W
3662 );
3663 t = GEO::index_t(next_around_vertex(t,f));
3664 } while(t != GEO::index_t(vertex_cell(i)));
3665 } else {
3666 get_incident_tets(i,W);
3667 for(index_t t: W) {
3668 copy_Laguerre_cell_facet_from_Delaunay(
3669 i, Pi, wi, Pi_len2, t, C, W
3670 );
3671 }
3672 }
3673 C.connect_triangles();
3674 }
3675
3676 GEO::index_t PeriodicDelaunay3d::copy_Laguerre_cell_facet_from_Delaunay(
3677 GEO::index_t i,
3678 const GEO::vec3& Pi,
3679 double wi,
3680 double Pi_len2,
3681 GEO::index_t t,
3682 ConvexCell& C,
3683 IncidentTetrahedra& W
3684 ) const {
3685 geo_argused(W);
3686
3687 // Local tet vertex indices from facet
3688 // and vertex in facet indices.
3689 static GEO::index_t fv[4][3] = {
3690 {2,3,1},
3691 {3,2,0},
3692 {0,1,3},
3693 {2,1,0}
3694 };
3695
3696 GEO::index_t f = index(t,GEO::index_t(i));
3697 GEO::index_t jkl[3]; // Global index (in Delaunay) of triangle vertices
3698 VBW::index_t l_jkl[3];// Local index (in C) of triangle vertices
3699
3700
3701 // Find or create the three vertices of the facet.
3702 for(int lfv=0; lfv<3; ++lfv) {
3703
3704 jkl[lfv] = GEO::index_t(cell_vertex(t, fv[f][lfv]));
3705 l_jkl[lfv] = VBW::index_t(-1);
3706
3707 // Vertex already created in C (note:
3708 // also works for vertex at infinity)
3709 for(VBW::index_t u=0; u<C.nb_v(); ++u) {
3710 if(C.v_global_index(u) == jkl[lfv]) {
3711 l_jkl[lfv] = VBW::index_t(u);
3712 break;
3713 }
3714 }
3715
3716 // vertex not found, create vertex in C
3717 if(l_jkl[lfv] == VBW::index_t(-1)) {
3718 l_jkl[lfv] = C.nb_v();
3719 vec3 Pj = vertex(jkl[lfv]);
3720 double Pj_len2 = length2(Pj);
3721 double wj = weight(jkl[lfv]);
3722 double a = 2.0 * (Pi[0] - Pj[0]);
3723 double b = 2.0 * (Pi[1] - Pj[1]);
3724 double c = 2.0 * (Pi[2] - Pj[2]);
3725 double d = ((wi - Pi_len2) - (wj - Pj_len2));
3726 C.create_vertex(vec4(a,b,c,d), jkl[lfv]);
3727 }
3728 }
3729
3730 C.create_triangle(l_jkl[0], l_jkl[1], l_jkl[2]);
3731
3732 return f;
3733 }
3734
3735 /*************************************************************************/
3736
3737 void PeriodicDelaunay3d::insert_vertices(
3738 const char* phase, index_t b, index_t e
3739 ) {
3740
3741 Stopwatch W(phase,detailed_benchmark_mode_);
3742
3743 if(detailed_benchmark_mode_) {
3744 Logger::out(phase) << "Inserting " << (e-b)
3745 << " additional vertices" << std::endl;
3746 }
3747
3748 has_empty_cells_ = false;
3749
3750 nb_vertices_ = reorder_.size();
3751 vector<index_t> levels;
3752
3753 compute_BRIO_order_periodic(
3754 nb_vertices_non_periodic_ * 27, // nb of possible periodic vertices
3755 vertex_ptr(0),
3756 3, dimension(),
3757 reorder_,
3758 reorder_.begin() + long(b),
3759 reorder_.begin() + long(e),
3760 period_,
3761 64, 0.125,
3762 &levels
3763 );
3764
3765
3766 #ifdef GEO_DEBUG
3767 // Check that the same vertex was not inserted twice
3768 for(index_t i=b; i+1<e; ++i) {
3769 geo_debug_assert(reorder_[i] != reorder_[i+1]);
3770 }
3771 #endif
3772
3773 insert_vertices_with_BRIO(phase, levels);
3774 if(has_empty_cells_) {
3775 return;
3776 }
3777 PeriodicDelaunay3dThread* thread0 = thread(0);
3778 nb_vertices_ = reorder_.size();
3779 set_arrays(
3780 thread0->max_t(),
3781 cell_to_v_store_.data(),
3782 cell_to_cell_store_.data()
3783 );
3784
3785 if(!strcmp(phase, "insert-I")) {
3786 stats_.phase_I_insert_t_ = W.elapsed_time();
3787 stats_.phase_I_insert_nb_ = e-b;
3788 } else if(!strcmp(phase, "insert-II")) {
3789 stats_.phase_II_insert_t_ = W.elapsed_time();
3790 stats_.phase_II_insert_nb_ = e-b;
3791 }
3792 }
3793
3794 void PeriodicDelaunay3d::insert_vertices_with_BRIO(
3795 const char* phase, const vector<index_t>& levels
3796 ) {
3797
3798 for(index_t t=0; t<threads_.size(); ++t) {
3799 thread(t)->reset_stats();
3800 }
3801
3802 PeriodicDelaunay3dThread* thread0 = thread(0);
3803
3804 index_t lvl = 1;
3805 while(lvl < (levels.size() - 1) && (levels[lvl] - levels[0]) < 1000) {
3806 ++lvl;
3807 }
3808
3809 if(detailed_benchmark_mode_) {
3810 Logger::out(phase)
3811 << "Using " << levels.size()-1 << " levels" << std::endl;
3812 Logger::out(phase)
3813 << "Levels 0 - " << lvl-1
3814 << ": bootstraping with first levels in sequential mode"
3815 << std::endl;
3816 }
3817
3818 thread0->set_work(levels[0], levels[lvl]);
3819 thread0->run();
3820
3821 if(thread0->has_empty_cells()) {
3822 has_empty_cells_ = true;
3823 return;
3824 }
3825
3826 index_t nb_sequential_points = 0;
3827 index_t first_lvl = lvl;
3828
3829 // Insert points in all BRIO levels
3830 for(; lvl<levels.size()-1; ++lvl) {
3831
3832 index_t lvl_b = levels[lvl];
3833 index_t lvl_e = levels[lvl+1];
3834
3835 if(detailed_benchmark_mode_) {
3836 Logger::out(phase) << "Level "
3837 << lvl << " : start "
3838 << " nbv = "
3839 << (lvl_e - lvl_b)
3840 << std::endl;
3841 }
3842
3843 index_t work_size = (lvl_e - lvl_b)/index_t(threads_.size());
3844
3845 // Initialize threads
3846 index_t b = lvl_b;
3847 for(index_t t=0; t<threads_.size(); ++t) {
3848 index_t e = (t == threads_.size()-1) ? lvl_e : b+work_size;
3849
3850 // Copy the indices of the first created tetrahedron
3851 // and the maximum valid tetrahedron index max_t_
3852 if(lvl == first_lvl && t!=0) {
3853 thread(t)->set_max_t(thread0->max_t());
3854 }
3855 thread(t)->set_work(b,e);
3856 b = e;
3857 }
3858
3859 check_max_t();
3860 Process::run_threads(threads_);
3861
3862 for(index_t t=0; t<this->nb_threads(); ++t) {
3863 if(thread(t)->has_empty_cells()) {
3864 has_empty_cells_ = true;
3865 return;
3866 }
3867 }
3868
3869 // Run threads sequentialy, to insert missing points if
3870 // memory overflow was encountered (in sequential mode,
3871 // dynamic memory growing works)
3872
3873 index_t this_level_nb_sequential_points = 0;
3874
3875 for(index_t t=0; t<threads_.size(); ++t) {
3876 PeriodicDelaunay3dThread* t1 = thread(t);
3877 this_level_nb_sequential_points += t1->work_size();
3878 if(t != 0) {
3879 // We need to copy max_t_ from previous thread,
3880 // since the memory pool may have grown.
3881 PeriodicDelaunay3dThread* t2 = thread(t-1);
3882 t1->set_max_t(t2->max_t());
3883 }
3884 t1->run();
3885 if(t1->has_empty_cells()) {
3886 has_empty_cells_ = true;
3887 return;
3888 }
3889 }
3890
3891 // If some tetrahedra were created in sequential mode, then
3892 // the maximum valid tetrahedron index was increased by all
3893 // the threads in increasing number, so we copy it from the
3894 // last thread into thread0 since we use thread0 afterwards
3895 // to get max_t()
3896
3897 if(this_level_nb_sequential_points != 0) {
3898 PeriodicDelaunay3dThread* t0 = thread(0);
3899 PeriodicDelaunay3dThread* tn = thread(
3900 this->nb_threads()-1
3901 );
3902 t0->set_max_t(tn->max_t());
3903 }
3904
3905 if(has_empty_cells_) {
3906 return;
3907 }
3908
3909 nb_sequential_points += this_level_nb_sequential_points;
3910 }
3911
3912 if(detailed_benchmark_mode_) {
3913 index_t tot_rollbacks = 0 ;
3914 index_t tot_failed_locate = 0 ;
3915 for(index_t t=0; t<threads_.size(); ++t) {
3916 Logger::out(phase)
3917 << "thread " << std::setw(3) << t << " : "
3918 << std::setw(3) << thread(t)->nb_rollbacks()
3919 << " rollbacks "
3920 << std::setw(3) << thread(t)->nb_failed_locate()
3921 << " restarted locate"
3922 << std::endl;
3923 tot_rollbacks += thread(t)->nb_rollbacks();
3924 tot_failed_locate += thread(t)->nb_failed_locate();
3925 }
3926 Logger::out(phase) << "------------------" << std::endl;
3927 Logger::out(phase) << "total: "
3928 << tot_rollbacks << " rollbacks "
3929 << tot_failed_locate << " restarted locate"
3930 << std::endl;
3931 if(nb_sequential_points == 0) {
3932 Logger::out(phase) << "All points where inserted in parallel"
3933 << std::endl;
3934 } else {
3935 Logger::out(phase) << nb_sequential_points
3936 << " points inserted in sequential mode"
3937 << std::endl;
3938 }
3939 }
3940
3941 nb_vertices_ = reorder_.size();
3942 index_t nb_tets = thread0->max_t();
3943
3944 for(index_t i=0; i<nb_threads(); ++i) {
3945 geo_assert(thread(i)->max_t() <= nb_tets);
3946 }
3947 }
3948
3949 bool PeriodicDelaunay3d::Laguerre_vertex_is_in_conflict_with_plane(
3950 index_t t, vec4 P
3951 ) const {
3952
3953 // Note: facet orientations and signs follow:
3954 // - ConvexCell
3955 // - copy_Laguerre_facet_from()
3956
3957 // Local tet vertex indices from facet and vertex in facet indices.
3958 // Carefully chosen in such a way that f[(lv+1)%4][2] == lv
3959 // This is used in the code block below, that handles tets with
3960 // a vertex at infinity
3961 static GEO::index_t fv[4][3] = {
3962 {1,2,3},
3963 {3,2,0},
3964 {3,0,1},
3965 {1,0,2}
3966 };
3967
3968 // Particular case, vertex at infinity
3969 for(index_t lv=0; lv<4; ++lv) {
3970 if(cell_vertex(t,lv) == NO_INDEX) {
3971 index_t li = (lv + 1) % 4; // this vertex is not at infty
3972 // li is also the index of a facet with the vertex at infty
3973 // as the last vertex (fv[][] was constructed so), so we
3974 // are sure that vi, vj, vk are the not at infty
3975 index_t vi = cell_vertex(t, li);
3976 index_t vj = cell_vertex(t, fv[li][0]);
3977 index_t vk = cell_vertex(t, fv[li][1]);
3978 geo_debug_assert(fv[li][2] == lv); // we know that l == -1
3979 vec3 pi = vertex(vi);
3980 vec3 pj = vertex(vj);
3981 vec3 pk = vertex(vk);
3982 Sign s = PCK::det_3d(
3983 pi-pj,
3984 pi-pk,
3985 vec3(P.x,P.y,P.z)
3986 );
3987 return (s <= 0);
3988 }
3989 }
3990
3991 index_t v1 = cell_vertex(t,0);
3992 index_t v2 = cell_vertex(t,1);
3993 index_t v3 = cell_vertex(t,2);
3994 index_t v4 = cell_vertex(t,3);
3995
3996 vec3 p1 = vertex(v1);
3997 vec3 p2 = vertex(v2);
3998 vec3 p3 = vertex(v3);
3999 vec3 p4 = vertex(v4);
4000
4001 double l1 = weight(v1) - length2(p1);
4002 double l2 = weight(v2) - length2(p2);
4003 double l3 = weight(v3) - length2(p3);
4004 double l4 = weight(v4) - length2(p4);
4005
4006 Sign s = PCK::det_4d(
4007 vec4(2.0*(p1.x-p2.x), 2.0*(p1.y-p2.y), 2.0*(p1.z-p2.z), l1-l2),
4008 vec4(2.0*(p1.x-p3.x), 2.0*(p1.y-p3.y), 2.0*(p1.z-p3.z), l1-l3),
4009 vec4(2.0*(p1.x-p4.x), 2.0*(p1.y-p4.y), 2.0*(p1.z-p4.z), l1-l4),
4010 P
4011 );
4012
4013 return (s >= 0);
4014 }
4015
4016
4017 void PeriodicDelaunay3d::handle_periodic_boundaries_phase_I() {
4018 Stopwatch W_classify_I("classify-I", detailed_benchmark_mode_);
4019 PeriodicDelaunay3dThread* thread0 = thread(0);
4020
4021 // Lag_cell_status:
4022 //
4023 // each bit k in 0..5 indicate that cell has at least one vertex
4024 // in conflict with plane Pk (outside central cube)
4025 // each bit k in 6..11 indicate that cell has all its vertices
4026 // in conflict with plane Pk (outside central cube)
4027 // status = 0 -> cell is contained by central cube
4028 // (status & conflict_mask) != 0 -> cell straddles bndr of central cube
4029 // (status & all_conflict_mask) != 0 -> cell is outside central cube
4030
4031 // static Numeric::uint16 conflict_mask = Numeric::uint16(63u);
4032 static Numeric::uint16 all_conflict_mask = Numeric::uint16(63u << 6);
4033
4034 std::atomic<Numeric::uint16>* Lag_cell_status
4035 = new std::atomic<Numeric::uint16>[nb_vertices_non_periodic_];
4036
4037 for(index_t i=0; i<nb_vertices_non_periodic_; ++i) {
4038 Lag_cell_status[i] = all_conflict_mask;
4039 }
4040
4041 {
4042 vec4 cube_face[6] = {
4043 vec4( 1.0, 0.0, 0.0, 0.0),
4044 vec4(-1.0, 0.0, 0.0, period_.x),
4045 vec4( 0.0, 1.0, 0.0, 0.0),
4046 vec4( 0.0,-1.0, 0.0, period_.y),
4047 vec4( 0.0, 0.0, 1.0, 0.0),
4048 vec4( 0.0, 0.0,-1.0, period_.z),
4049 };
4050
4051 parallel_for(0, thread0->max_t(), [&](index_t t) {
4052 if(thread0->tet_is_free(t)) {
4053 return;
4054 }
4055 for(index_t k=0; k<6; ++k) {
4056 bool conflict = Laguerre_vertex_is_in_conflict_with_plane(
4057 t, cube_face[k]
4058 );
4059 for(index_t lv=0; lv<4; ++lv) {
4060 index_t v = cell_vertex(t,lv);
4061 if(v == NO_INDEX) {
4062 continue;
4063 }
4064 if(conflict) {
4065 // set 'conflict' bit k
4066 Lag_cell_status[v].fetch_or(
4067 Numeric::uint16(1u << k),
4068 std::memory_order_relaxed
4069 );
4070 } else {
4071 // reset 'all conflict' bit k
4072 Lag_cell_status[v].fetch_and(
4073 Numeric::uint16(~(1u << (k+6))),
4074 std::memory_order_relaxed
4075 );
4076 }
4077 }
4078 }
4079 }
4080 );
4081 }
4082
4083 // Count cells inside, crossing, outside
4084 {
4085 stats_.phase_I_nb_inside_ = 0;
4086 stats_.phase_I_nb_cross_ = 0;
4087 stats_.phase_I_nb_outside_ = 0;
4088 for(index_t v=0; v<nb_vertices_non_periodic_; ++v) {
4089 Numeric::uint16 status = Lag_cell_status[v];
4090 if(status == 0) {
4091 ++stats_.phase_I_nb_inside_;
4092 } else if((status & all_conflict_mask) != 0) {
4093 ++stats_.phase_I_nb_outside_;
4094 } else {
4095 ++stats_.phase_I_nb_cross_;
4096 }
4097 }
4098
4099 if(detailed_benchmark_mode_) {
4100 Logger::out("classify-I") << "Nb cells inside cube: "
4101 << stats_.phase_I_nb_inside_
4102 << std::endl;
4103 Logger::out("classify-I") << "Nb cells on boundary: "
4104 << stats_.phase_I_nb_cross_
4105 << std::endl;
4106 Logger::out("classify-I") << "Nb cells outside cube: "
4107 << stats_.phase_I_nb_outside_
4108 << std::endl;
4109 }
4110 }
4111
4112 // Indicates for each real vertex the instances it has.
4113 // Each bit of vertex_instances_[v] indicates which instance
4114 // is used.
4115 vertex_instances_.assign(nb_vertices_non_periodic_,1);
4116
4117 for(index_t v=0; v<nb_vertices_non_periodic_; ++v) {
4118 Numeric::uint16 status = Lag_cell_status[v];
4119
4120 bool status_inside = (status == 0);
4121
4122 // In the distributed version, we might need to distinguish
4123 // also the following two cases:
4124 // bool status_outside = ((status & all_conflict_mask) != 0);
4125 // bool status_crossing = !status_inside && !status_outside;
4126
4127 // if cell is inside cube, no instance to create
4128 if(status_inside) {
4129 continue;
4130 }
4131
4132 // Integer translations associated with the six plane equations
4133 static int T[6][3]= {
4134 {-1, 0, 0},
4135 { 1, 0, 0},
4136 { 0,-1, 0},
4137 { 0, 1, 0},
4138 { 0, 0,-1},
4139 { 0, 0, 1}
4140 };
4141
4142 // Detect the bounds of the sub-(rubic's) cube overlapped
4143 // by the cell.
4144
4145 int TXmin = 2, TXmax = -2,
4146 TYmin = 2, TYmax = -2,
4147 TZmin = 2, TZmax = -2;
4148
4149 FOR(i,6) {
4150 if((status & Numeric::uint8(1u << i)) != 0) {
4151 TXmin = std::min(TXmin, T[i][0]);
4152 TXmax = std::max(TXmax, T[i][0]);
4153 TYmin = std::min(TYmin, T[i][1]);
4154 TYmax = std::max(TYmax, T[i][1]);
4155 TZmin = std::min(TZmin, T[i][2]);
4156 TZmax = std::max(TZmax, T[i][2]);
4157 }
4158 }
4159
4160 for(int TX = TXmin; TX <= TXmax; ++TX) {
4161 for(int TY = TYmin; TY <= TYmax; ++TY) {
4162 for(int TZ = TZmin; TZ <= TZmax; ++TZ) {
4163 index_t instance = T_to_instance(-TX,-TY,-TZ);
4164 // Skip instance 0 (it was already inserted !)
4165 if(instance != 0) {
4166 vertex_instances_[v] |= (1u << instance);
4167 reorder_.push_back(make_periodic_vertex(v,instance));
4168 }
4169 }
4170 }
4171 }
4172 }
4173 delete[] Lag_cell_status;
4174 stats_.phase_I_classify_t_ = W_classify_I.elapsed_time();
4175 }
4176
4177 void PeriodicDelaunay3d::handle_periodic_boundaries_phase_II() {
4178 Stopwatch W_classify_II("classify-II", detailed_benchmark_mode_);
4179 PeriodicDelaunay3dThread* thread0 = thread(0);
4180
4181 // Computes translation_table[][]
4182 // translation_table[instance2][instance1] transforms
4183 // instance2 into the frame of instance1
4184 Numeric::int8 translation_table[27][27];
4185 for(index_t instance1=0; instance1<27; ++instance1) {
4186 int Tx1 = translation[instance1][0];
4187 int Ty1 = translation[instance1][1];
4188 int Tz1 = translation[instance1][2];
4189 for(index_t instance2=0; instance2<27; ++instance2) {
4190 int Tx2 = translation[instance2][0];
4191 int Ty2 = translation[instance2][1];
4192 int Tz2 = translation[instance2][2];
4193
4194 translation_table[instance2][instance1] =
4195 Numeric::int8(instance2);
4196
4197 if(instance1 == instance2) {
4198 continue;
4199 }
4200
4201 if(
4202 std::abs(Tx2 - Tx1) >= 2 ||
4203 std::abs(Ty2 - Ty1) >= 2 ||
4204 std::abs(Tz2 - Tz1) >= 2
4205 ) {
4206 // Here we could use -1 to encode large displacements,
4207 // and issue an error message (for now they are ignored)
4208 continue;
4209 }
4210
4211 translation_table[instance2][instance1] =
4212 Numeric::int8(T_to_instance(Tx2-Tx1,Ty2-Ty1,Tz2-Tz1));
4213 }
4214 }
4215
4216 std::atomic<Numeric::uint32>* new_vertex_instances =
4217 new std::atomic<Numeric::uint32>[vertex_instances_.size()];
4218
4219 for(index_t i=0; i<vertex_instances_.size(); ++i) {
4220 new_vertex_instances[i] = vertex_instances_[i];
4221 }
4222
4223 Process::spinlock perio_lock = GEOGRAM_SPINLOCK_INIT;
4224 parallel_for(0, thread0->max_t(), [&,this](index_t t) {
4225 if(!thread0->tet_is_real(t)) {
4226 return;
4227 }
4228 // Find the edges v1,v2 such that:
4229 // v1 is a vertex that was inserted in phase-I (instance != 0)
4230 // v2 is a vertex in an instance different from v1
4231 for(index_t lv=0; lv<4; ++lv) {
4232 index_t v1 = thread0->finite_tet_vertex(t, lv);
4233 index_t v1_instance = periodic_vertex_instance(v1);
4234 if(v1_instance == 0) {
4235 continue;
4236 }
4237 for(index_t dlv=1; dlv<4; ++dlv) {
4238 index_t v2 = thread0->finite_tet_vertex(t, (lv + dlv)%4);
4239 index_t v2_real = periodic_vertex_real(v2);
4240 index_t v2_instance = periodic_vertex_instance(v2);
4241
4242 // transform v2_instance into the local frame of v1
4243 v2_instance = index_t(
4244 translation_table[v2_instance][v1_instance]
4245 );
4246 if(v2_instance == v1_instance) {
4247 continue;
4248 }
4249
4250 // create the transformed v2_instance if it does not
4251 // already exist, and memorize it in the new list of
4252 // vertices to create
4253
4254 Numeric::uint32 mask = (1u << v2_instance);
4255 Numeric::uint32 prev_instances =
4256 new_vertex_instances[v2_real].fetch_or(
4257 mask, std::memory_order_relaxed // only need atomic
4258 );
4259
4260 if((prev_instances & mask) == 0) {
4261 Process::acquire_spinlock(perio_lock);
4262 reorder_.push_back(
4263 make_periodic_vertex(v2_real, v2_instance)
4264 );
4265 Process::release_spinlock(perio_lock);
4266 }
4267 }
4268 }
4269 });
4270 for(index_t i=0; i<vertex_instances_.size(); ++i) {
4271 vertex_instances_[i] = new_vertex_instances[i];
4272 }
4273 delete[] new_vertex_instances;
4274 stats_.phase_II_classify_t_ = W_classify_II.elapsed_time();
4275 }
4276
4277 void PeriodicDelaunay3d::handle_periodic_boundaries() {
4278
4279 // Update pointers so that queries function will work (even in our
4280 // "transient state").
4281 PeriodicDelaunay3dThread* thread0 = thread(0);
4282
4283 set_arrays(
4284 thread0->max_t(),
4285 cell_to_v_store_.data(),
4286 cell_to_cell_store_.data()
4287 );
4288
4289 // Test for empty cells
4290 // TODO: I tested, it really occurs that there is still an empty
4291 // cell here, why is it not detected before ? To be understood.
4292 update_v_to_cell();
4293 for(index_t v=0; v<nb_vertices_non_periodic_; ++v) {
4294 if(v_to_cell_[v] == NO_INDEX) {
4295 has_empty_cells_ = true;
4296 return;
4297 }
4298 }
4299
4300 // Phase I: find the cells that intersect the boundaries, and
4301 // create periodic vertices for each possible translation.
4302 {
4303 Stopwatch W_phase_I("phase-I",false);
4304 handle_periodic_boundaries_phase_I();
4305 insert_vertices(
4306 "insert-I", nb_vertices_non_periodic_, reorder_.size()
4307 );
4308 stats_.phase_I_t_ = W_phase_I.elapsed_time();
4309 }
4310
4311 // Phase II: Insert the real neighbors of the virtual vertices,
4312 // back-translated to the original position.
4313 {
4314 index_t nb_vertices_phase_I = reorder_.size();
4315 Stopwatch W_phase_II("phase-II",false);
4316 handle_periodic_boundaries_phase_II();
4317 insert_vertices("insert-II", nb_vertices_phase_I, reorder_.size());
4318 stats_.phase_II_t_ = W_phase_II.elapsed_time();
4319 }
4320 }
4321
4322 void PeriodicDelaunay3d::check_volume() {
4323 ConvexCell C;
4324 C.use_exact_predicates(convex_cell_exact_predicates_);
4325
4326 Logger::out("Periodic") << "Checking total volume..." << std::endl;
4327 double sumV = 0;
4328 IncidentTetrahedra W;
4329
4330 FOR(v, nb_vertices_non_periodic_) {
4331 copy_Laguerre_cell_from_Delaunay(v, C, W);
4332 #ifdef GEO_DEBUG
4333 for(
4334 VBW::ushort t = C.first_triangle();
4335 t!=VBW::END_OF_LIST; t=C.next_triangle(t)
4336 ) {
4337 for(index_t lv=0; lv<3; ++lv) {
4338 // Make sure there is no vertex at infinity
4339 geo_debug_assert(
4340 C.triangle_v_local_index(t,VBW::index_t(lv)) != 0
4341 );
4342 }
4343 }
4344 #endif
4345 C.compute_geometry();
4346 sumV += C.volume();
4347 }
4348
4349 double expectedV = period_.x*period_.y*period_.z;
4350
4351 Logger::out("Periodic") << "Sum volumes = " << sumV << std::endl;
4352 Logger::out("Periodic") << " (expected " << expectedV << ")"
4353 << std::endl;
4354
4355 if(::fabs(sumV - expectedV) / expectedV >= 1.0 / 10000.0) {
4356 Logger::err("Periodic") << "FATAL, volume error is too large"
4357 << std::endl;
4358 exit(-1);
4359 }
4360
4361 }
4362
4363 void PeriodicDelaunay3d::save_cells(
4364 const std::string& basename, bool clipped
4365 ) {
4366 static int index = 1;
4367
4368 std::string index_string = String::to_string(index);
4369 while(index_string.length() < 3) {
4370 index_string = "0" + index_string;
4371 }
4372
4373 std::ofstream out((basename+index_string+".obj").c_str());
4374 index_t v_off = 1;
4375
4376 ConvexCell C;
4377 C.use_exact_predicates(convex_cell_exact_predicates_);
4378 IncidentTetrahedra W;
4379 for(index_t vv=0; vv<nb_vertices_non_periodic_; ++vv) {
4380 copy_Laguerre_cell_from_Delaunay(vv, C, W);
4381 if(clipped) {
4382 C.clip_by_plane(vec4( 1.0, 0.0, 0.0, 0.0));
4383 C.clip_by_plane(vec4(-1.0, 0.0, 0.0, period_.x));
4384 C.clip_by_plane(vec4( 0.0, 1.0, 0.0, 0.0));
4385 C.clip_by_plane(vec4( 0.0,-1.0, 0.0, period_.y));
4386 C.clip_by_plane(vec4( 0.0, 0.0, 1.0, 0.0));
4387 C.clip_by_plane(vec4( 0.0, 0.0,-1.0, period_.z));
4388 }
4389 v_off += C.save(out, v_off, 0.1);
4390 }
4391 ++index;
4392 }
4393
4394 void PeriodicDelaunay3d::check_max_t() {
4395 index_t max_t = 0;
4396 for(index_t i=0; i<nb_threads(); ++i) {
4397 max_t = std::max(max_t, thread(i)->max_t());
4398 }
4399 for(index_t i=0; i<nb_threads(); ++i) {
4400 geo_assert(thread(i)->max_t() == max_t);
4401 }
4402 }
4403
4404 /***********************************************************/
4405
4406 PeriodicDelaunay3d::Stats::Stats() {
4407 reset();
4408 }
4409
4410 void PeriodicDelaunay3d::Stats::reset() {
4411 Memory::clear(this, sizeof(Stats));
4412 raw_ = CmdLine::get_arg_bool("dbg:raw_logs");
4413 }
4414
4415 std::string PeriodicDelaunay3d::Stats::to_string_raw() const {
4416 return String::format(
4417 "%.1f %.1f %.1f %.1f %d %d %d %.1f %d %.1f %.1f %.1f %d",
4418 total_t_,
4419
4420 phase_0_t_,
4421
4422 phase_I_t_, phase_I_classify_t_,
4423
4424 int(phase_I_nb_inside_), int(phase_I_nb_cross_),
4425 int(phase_I_nb_outside_),
4426
4427 phase_I_insert_t_, int(phase_I_insert_nb_),
4428
4429 phase_II_t_, phase_II_classify_t_, phase_II_insert_t_,
4430 int(phase_II_insert_nb_)
4431 );
4432 }
4433
4434
4435 std::string PeriodicDelaunay3d::Stats::to_string_pretty() const {
4436 return String::format(
4437 "total | t:%.1f\n"
4438 "phase0 | t:%.1f\n"
4439 "phaseI | t:%.1f t_cls:%.1f t_ins:%.1f nb_ins:%d\n"
4440 " | in:%d bndry:%d out:%d\n"
4441 "phaseII | t:%.1f t_cls:%.1f t_ins:%.1f nb_ins:%d",
4442 total_t_,
4443
4444 phase_0_t_,
4445
4446 phase_I_t_, phase_I_classify_t_,
4447 phase_I_insert_t_, int(phase_I_insert_nb_),
4448
4449 int(phase_I_nb_inside_), int(phase_I_nb_cross_),
4450 int(phase_I_nb_outside_),
4451
4452 phase_II_t_, phase_II_classify_t_,
4453 phase_II_insert_t_, int(phase_II_insert_nb_)
4454 );
4455 }
4456 }
4457