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