Geogram Version 1.9.6-rc
A programming library of geometric algorithms
Loading...
Searching...
No Matches
generic_RVD.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40#ifndef GEOGRAM_VORONOI_GENERIC_RVD
41#define GEOGRAM_VORONOI_GENERIC_RVD
42
48#include <geogram/mesh/index.h>
53
54#include <deque>
55#include <algorithm>
56#include <iostream>
57
67namespace GEOGen {
68
77 template <index_t DIM>
79
82
83 public:
87 static coord_index_t dimension() {
88 return DIM;
89 }
90
95
100
105
110
111 /********************************************************************/
112
121 ) :
122 mesh_(mesh),
123 delaunay_(delaunay),
124 intersections_(DIM),
125 symbolic_(false),
126 check_SR_(true),
127 exact_(false)
128 {
129 delaunay_nn_ = dynamic_cast<GEO::Delaunay_NearestNeighbors*>(
130 delaunay_
131 );
132 dimension_ = DIM;
133 facets_begin_ = UNSPECIFIED_RANGE;
134 facets_end_ = UNSPECIFIED_RANGE;
135 tets_begin_ = UNSPECIFIED_RANGE;
136 tets_end_ = UNSPECIFIED_RANGE;
137 connected_components_priority_ = false;
138 facet_seed_marking_ = nullptr;
139 connected_component_changed_ = false;
140 current_connected_component_ = 0;
141 cur_stamp_ = NO_INDEX;
142 current_facet_ = GEO::max_index_t();
143 current_seed_ = GEO::max_index_t();
144 current_polygon_ = nullptr;
145 current_tet_ = GEO::max_index_t();
146 current_polyhedron_ = nullptr;
147 }
148
157 connected_components_priority_ = x;
158 }
159
160
172 return connected_components_priority_;
173 }
174
178 const GEO::Mesh* mesh() const {
179 return mesh_;
180 }
181
186 return mesh_;
187 }
188
192 const Delaunay* delaunay() const {
193 return delaunay_;
194 }
195
200 return delaunay_;
201 }
202
207 delaunay_ = delaunay;
208 delaunay_nn_ = dynamic_cast<GEO::Delaunay_NearestNeighbors*>(
209 delaunay_
210 );
211 }
212
217 mesh_ = mesh;
218 }
219
226 void set_facets_range(index_t facets_begin, index_t facets_end) {
227 geo_debug_assert(facets_end >= facets_begin);
228 facets_begin_ = facets_begin;
229 facets_end_ = facets_end;
230 }
231
240 void set_tetrahedra_range(index_t tets_begin, index_t tets_end) {
241 geo_debug_assert(tets_end >= tets_begin);
242 tets_begin_ = tets_begin;
243 tets_end_ = tets_end;
244 }
245
250 index_t nb_facets_in_range() const {
251 return facets_end_ - facets_begin_;
252 }
253
258 index_t nb_tetrahedra_in_range() const {
259 return tets_end_ - tets_begin_;
260 }
261
267 index_t current_facet() const {
268 return current_facet_;
269 }
270
276 index_t current_seed() const {
277 return current_seed_;
278 }
279
287 const Polygon& current_polygon() const {
288 return *current_polygon_;
289 }
290
296 index_t current_tet() const {
297 return current_tet_;
298 }
299
309 return *current_polyhedron_;
310 }
311
318 void set_symbolic(bool x) {
319 symbolic_ = x;
320 // exact mode requires symbolic mode.
321 if(exact_) {
322 symbolic_ = true;
323 }
324 }
325
329 bool symbolic() const {
330 return symbolic_;
331 }
332
338 void set_exact_predicates(bool x) {
339 exact_ = x;
340 // exact mode requires symbolic mode.
341 if(exact_) {
342 symbolic_ = true;
343 }
344 }
345
349 bool exact_predicates() const {
350 return exact_;
351 }
352
356 void set_check_SR(bool x) {
357 check_SR_ = x;
358 }
359
365 bool check_SR() const {
366 return check_SR_;
367 }
368
376 return &intersections_;
377 }
378
379 protected:
391 template <class ACTION>
393 public:
398 PolygonAction(const ACTION& do_it) :
399 do_it_(do_it) {
400 }
401
411 index_t v,
412 index_t f,
413 const Polygon& P
414 ) const {
416 const_cast<ACTION&> ( do_it_)(v, P);
417 }
418
419 protected:
420 const ACTION& do_it_;
421 };
422
430 template <class ACTION>
432 public:
438 TriangleAction(const ACTION& do_it) :
439 do_it_(do_it) {
440 }
441
453 index_t v,
454 index_t f,
455 const Polygon& P
456 ) const {
458 for(index_t i = 1; i + 1 < P.nb_vertices(); i++) {
459 const_cast<ACTION&> (do_it_)(
460 v, P.vertex(0), P.vertex(i), P.vertex(i + 1)
461 );
462 }
463 }
464
465 protected:
466 const ACTION& do_it_;
467 };
468
476 template <class ACTION>
478 public:
484 HalfedgeAction(const ACTION& do_it) :
485 do_it_(do_it) {
486 }
487
498 index_t v,
499 index_t f,
500 const Polygon& P
501 ) const {
504 for(index_t i = 0; i < P.nb_vertices(); i++) {
505 if(P.vertex(i).check_flag(INTERSECT)) {
506 index_t j = P.next_vertex(i);
507 const_cast<ACTION&> (do_it_)(
508 P.vertex(i), P.vertex(j)
509 );
510 }
511 }
512 }
513
514 protected:
515 const ACTION& do_it_;
516 };
517
525 template <class ACTION>
527 public:
533 BorderHalfedgeAction(const ACTION& do_it) :
534 do_it_(do_it) {
535 }
536
548 index_t v,
549 index_t f,
550 const Polygon& P
551 ) const {
553 for(index_t i = 0; i < P.nb_vertices(); i++) {
554 if(P.vertex(i).check_flag(ORIGINAL)) {
555 if(P.vertex(i).adjacent_facet() == -1) {
556 index_t j = P.next_vertex(i);
557 const_cast<ACTION&> (do_it_)(
558 v, P.vertex(i), P.vertex(j)
559 );
560 }
561 }
562 }
563 }
564
565 private:
566 const ACTION& do_it_;
567 };
568
575 template <class ACTION>
577 public:
583 PrimalTriangleAction(const ACTION& do_it) :
584 do_it_(do_it) {
585 }
586
595 index_t iv1,
596 index_t f,
597 const Polygon& P
598 ) const {
600 for(index_t i = 0; i < P.nb_vertices(); i++) {
601 const Vertex& ve = P.vertex(i);
602 // Primal triangles correspond to vertices of
603 // the RVD that are on two bisectors.
604 if(ve.sym().nb_bisectors() == 2) {
605 index_t iv2 = ve.sym().bisector(0);
606 index_t iv3 = ve.sym().bisector(1);
607 // This test generates triangle (iv1,iv2,iv3)
608 // only once (i.e. if iv1 is the vertex with
609 // the smallest index).
610 if(iv1 < iv2 && iv1 < iv3) {
611 const_cast<ACTION&> (do_it_)(iv1, iv2, iv3);
612 }
613 }
614 }
615 }
616
617 protected:
618 const ACTION& do_it_;
619 };
620
634 template <class ACTION>
636 public:
642 PolyhedronAction(const ACTION& do_it) :
643 do_it_(do_it) {
644 }
645
655 index_t v,
656 index_t t,
657 const Polyhedron& C
658 ) const {
659 const_cast<ACTION&> ( do_it_)(v, t, C);
660 }
661
662 protected:
663 const ACTION& do_it_;
664 };
665
688 template <class ACTION>
690 public:
708 const ACTION& do_it,
709 bool visit_inner_tets = false,
710 bool coherent_triangles = false
711 ) :
712 do_it_(do_it),
713 visit_inner_tets_(visit_inner_tets),
714 coherent_triangles_(coherent_triangles)
715 {
716 }
717
726 void operator() (index_t v, index_t t, const Polyhedron& C) const {
727 for(index_t cv = 0; cv < C.max_v(); ++cv) {
728 signed_index_t ct = C.vertex_triangle(cv);
729 if(ct == -1) {
730 continue;
731 }
732 geo_debug_assert(C.triangle_is_used(index_t(ct)));
733
734 signed_index_t adjacent = C.vertex_id(cv);
735 signed_index_t v_adj = -1;
736 signed_index_t t_adj = -1;
737
738 if(adjacent < 0) {
739 // Negative adjacent indices correspond to
740 // tet-tet links (ignored when we want to triangulate
741 // the border of the restricted Voronoi cell while
742 // ignoring internal structures).
743 if(!visit_inner_tets_) {
744 continue;
745 }
746 t_adj = -adjacent - 1;
747 } else if(adjacent > 0) {
748 // Positive adjacent indices correspond to
749 // Voronoi seed - Voronoi seed link
750 v_adj = adjacent - 1;
751 }
752 // and adjacent indicex equal to zero corresponds
753 // to tet on border.
754
756 index_t(ct),
757 index_t(C.find_triangle_vertex(index_t(ct), cv))
758 );
759
760 // If required, ensure that two polygonal facets
761 // seen from two different volumetric cells will
762 // be triangulated coherently.
763 if(coherent_triangles_) {
765 }
766
767 const Vertex& v1 = C.triangle_dual(c1.t);
768
769 Polyhedron::Corner c2 = c1;
771 geo_debug_assert(c2 != c1);
772
773 Polyhedron::Corner c3 = c2;
775 geo_debug_assert(c3 != c1);
776 do {
777 const Vertex& v2 = C.triangle_dual(c2.t);
778 const Vertex& v3 = C.triangle_dual(c3.t);
779 const_cast<ACTION&> (do_it_)(
780 v, index_t(v_adj), t, index_t(t_adj), v1, v2, v3
781 );
782 c2 = c3;
784 } while(c3 != c1);
785 }
786 }
787
805 const Polyhedron& C, Polyhedron::Corner& c,
806 index_t center_vertex_id
807 ) const {
808 Polyhedron::Corner first = c;
809 Polyhedron::Corner cur = c;
810 do {
812 C.triangle_dual(cur.t),
813 C.triangle_dual(c.t),
814 center_vertex_id
815 )) {
816 c = cur;
817 }
819 } while(cur != first);
820 }
821
836 static bool symbolic_compare(
837 const Vertex& p1, const Vertex& p2, index_t center_vertex_id
838 ) {
839 GEO::signed_quadindex K1(
840 signed_index_t(center_vertex_id),
841 p1.sym()[0], p1.sym()[1], p1.sym()[2]
842 );
843 GEO::signed_quadindex K2(
844 signed_index_t(center_vertex_id),
845 p2.sym()[0], p2.sym()[1], p2.sym()[2]
846 );
847 return K1 < K2;
848 }
849
850 protected:
851 const ACTION& do_it_;
852 bool visit_inner_tets_;
853 bool coherent_triangles_;
854 };
855
878 template <class ACTION>
880 public:
887 const ACTION& do_it
888 ) :
889 do_it_(do_it)
890 {
891 }
892
902 index_t v,
903 index_t t,
904 const Polyhedron& C
905 ) const {
906
907 // Find a vertex of the current cell,
908 // that will be used as the 'origin'
909 // vertex
910 const Vertex* v0 = nullptr;
911 index_t t0;
912 for(t0 = 0; t0 < C.max_t(); ++t0) {
913 if(C.triangle_is_used(t0)) {
914 v0 = &C.triangle_dual(t0);
915 break;
916 }
917 }
918
919 // If current cell is empty, return
920 if(v0 == nullptr) {
921 return;
922 }
923
924 for(index_t cv = 0; cv < C.max_v(); ++cv) {
925 signed_index_t ct = C.vertex_triangle(cv);
926 if(ct == -1) {
927 continue;
928 }
929 geo_debug_assert(C.triangle_is_used(index_t(ct)));
930
931 signed_index_t adjacent = C.vertex_id(cv);
932 signed_index_t v_adj = -1;
933 signed_index_t t_adj = -1;
934
935 if(adjacent < 0) {
936 // Negative adjacent indices correspond to
937 // tet-tet links
938 t_adj = -adjacent - 1;
939 } else if(adjacent > 0) {
940 // Positive adjacent indices correspond to
941 // Voronoi seed - Voroni seed link
942 v_adj = adjacent - 1;
943 }
944 // and adjacent indicex equal to zero corresponds
945 // to tet on border.
946
948 index_t(ct), C.find_triangle_vertex(index_t(ct), cv)
949 );
950
951 // If the current facet is incident to
952 // the origin vertex, then skip it (else
953 // it would generate flat tetrahedra)
954 if(facet_is_incident_to_vertex(C, c1, t0)) {
955 continue;
956 }
957
958 const Vertex& v1 = C.triangle_dual(c1.t);
959
960 Polyhedron::Corner c2 = c1;
962 geo_debug_assert(c2 != c1);
963
964 Polyhedron::Corner c3 = c2;
966 geo_debug_assert(c3 != c1);
967 do {
968 const Vertex& v2 = C.triangle_dual(c2.t);
969 const Vertex& v3 = C.triangle_dual(c3.t);
970 const_cast<ACTION&> (do_it_)(
971 v, index_t(v_adj), t, index_t(t_adj),
972 *v0, v1, v2, v3
973 );
974 c2 = c3;
976 } while(c3 != c1);
977 }
978 }
979
980 protected:
992 const Polyhedron& C, Polyhedron::Corner& c, index_t t
993 ) const {
994 Polyhedron::Corner first = c;
995 Polyhedron::Corner cur = c;
996 do {
997 if(cur.t == t) {
998 return true;
999 }
1001 } while(cur != first);
1002 return false;
1003 }
1004
1005 protected:
1006 const ACTION& do_it_;
1007 };
1008
1016 template <class ACTION>
1018 public:
1023 PrimalTetrahedronAction(const ACTION& do_it) :
1024 do_it_(do_it) {
1025 }
1026
1036 index_t v,
1037 index_t t,
1038 const Polyhedron& C
1039 ) const {
1041 for(index_t it = 0; it < C.max_t(); ++it) {
1042 if(C.triangle_is_used(it)) {
1043 const SymbolicVertex& sym = C.triangle_dual(it).sym();
1044 if(sym.nb_bisectors() == 3) {
1045 index_t v1 = sym.bisector(0);
1046 index_t v2 = sym.bisector(1);
1047 index_t v3 = sym.bisector(2);
1048 // This test ensures that the tet (v,v1,v2,v3)
1049 // is generated only once.
1050 if(v < v1 && v < v2 && v < v3) {
1051 const_cast<ACTION&> (do_it_)(v, v1, v2, v3);
1052 }
1053 }
1054 }
1055 }
1056 }
1057
1058 protected:
1059 const ACTION& do_it_;
1060 };
1061
1062 public:
1079 template <class ACTION>
1080 inline void for_each_polygon(const ACTION& action) {
1081 this->template compute_surfacic<PolygonAction<ACTION> >(
1082 PolygonAction<ACTION>(action)
1083 );
1084 }
1085
1094 template <class TRIACTION>
1095 inline void for_each_triangle(const TRIACTION& action) {
1096 this->template compute_surfacic<TriangleAction<TRIACTION> >(
1098 );
1099 }
1100
1110 template <class HEACTION>
1111 inline void for_each_halfedge(const HEACTION& action) {
1112 this->template compute_surfacic<HalfedgeAction<HEACTION> >(
1114 );
1115 }
1116
1126 template <class BOACTION>
1127 inline void for_each_border_halfedge(const BOACTION& action) {
1128 this->template compute_surfacic<BorderHalfedgeAction<BOACTION> >(
1130 );
1131 }
1132
1142 template <class PRIMTRIACTION>
1143 inline void for_each_primal_triangle(const PRIMTRIACTION& action) {
1144 bool sym_backup = symbolic();
1145 set_symbolic(true);
1146 this->template compute_surfacic<PrimalTriangleAction<PRIMTRIACTION>>(
1148 );
1149 set_symbolic(sym_backup);
1150 }
1151
1162 template <class ACTION>
1163 inline void for_each_polyhedron(const ACTION& action) {
1164 this->template compute_volumetric<PolyhedronAction<ACTION> >(
1166 );
1167 }
1168
1203 template <class ACTION>
1205 const ACTION& action,
1206 bool visit_inner_tets = false, bool coherent_triangles = false
1207 ) {
1208 this->template compute_volumetric<
1210 >(
1212 action, visit_inner_tets, coherent_triangles
1213 )
1214 );
1215 }
1216
1240 template <class ACTION>
1242 const ACTION& action
1243 ) {
1244 this->template compute_volumetric<TetrahedronAction<ACTION> >(
1246 action
1247 )
1248 );
1249 }
1250
1263 template <class ACTION>
1264 inline void for_each_primal_tetrahedron(const ACTION& action) {
1265 bool sym_backup = symbolic();
1266 set_symbolic(true);
1267 this->template compute_volumetric<PrimalTetrahedronAction<ACTION> >(
1269 action
1270 )
1271 );
1272 set_symbolic(sym_backup);
1273 }
1274
1275 protected:
1293 template <class ACTION>
1294 inline void compute_surfacic(const ACTION& action) {
1295 if(connected_components_priority_) {
1296 this->template compute_surfacic_with_cnx_priority<ACTION>(
1297 action
1298 );
1299 } else {
1300 this->template compute_surfacic_with_seeds_priority<ACTION>(
1301 action
1302 );
1303 }
1304 }
1305
1318 template <class ACTION>
1319 inline void compute_surfacic_with_seeds_priority(const ACTION& action) {
1320 if(
1321 facets_begin_ == UNSPECIFIED_RANGE &&
1322 facets_end_ == UNSPECIFIED_RANGE
1323 ) {
1324 facets_begin_ = 0;
1325 facets_end_ = mesh_->facets.nb();
1326 }
1327 current_polygon_ = nullptr;
1328 GEO::vector<index_t> seed_stamp(
1329 delaunay_->nb_vertices(), index_t(-1)
1330 );
1331 GEO::vector<bool> facet_is_marked(facets_end_-facets_begin_, false);
1333
1334 FacetSeedStack adjacent_facets;
1335 SeedStack adjacent_seeds;
1336 Polygon F;
1337 GEO::Attribute<double> vertex_weight;
1338 vertex_weight.bind_if_is_defined(
1339 mesh_->vertices.attributes(), "weight"
1340 );
1341
1342 // The algorithm propagates along both the facet-graph of
1343 // the surface and the 1-skeleton of the Delaunay triangulation,
1344 // and computes all the relevant intersections between
1345 // each Voronoi cell and facet.
1346 for(index_t f = facets_begin_; f < facets_end_; f++) {
1347 if(!facet_is_marked[f-facets_begin_]) {
1348 // Propagate along the facet-graph.
1349 facet_is_marked[f-facets_begin_] = true;
1350 adjacent_facets.push(
1352 );
1353 while(!adjacent_facets.empty()) {
1354 current_facet_ = adjacent_facets.top().f;
1355 current_seed_ = adjacent_facets.top().seed;
1356 adjacent_facets.pop();
1357
1358 // Copy the current facet from the Mesh into
1359 // RestrictedVoronoiDiagram's Polygon data structure
1360 // (gathers all the necessary information)
1362 mesh_, current_facet_, symbolic_, vertex_weight
1363 );
1364
1365 // Propagate along the Delaunay 1-skeleton
1366 // This will traverse all the seeds such that their
1367 // Voronoi cell has a non-empty intersection with
1368 // the current facet.
1369 seed_stamp[current_seed_] = current_facet_;
1370 adjacent_seeds.push(current_seed_);
1371
1372 while(!adjacent_seeds.empty()) {
1373 current_seed_ = adjacent_seeds.top();
1374 adjacent_seeds.pop();
1375
1376 current_polygon_ = intersect_cell_facet(
1377 current_seed_, F
1378 );
1379
1380 action(
1381 current_seed_, current_facet_, current_polygon()
1382 );
1383
1384 // Propagate to adjacent facets and adjacent seeds
1385 for(index_t v = 0;
1386 v < current_polygon().nb_vertices(); v++
1387 ) {
1388 const Vertex& ve = current_polygon().vertex(v);
1389 signed_index_t neigh_f = ve.adjacent_facet();
1390 if(
1391 neigh_f >= signed_index_t(facets_begin_) &&
1392 neigh_f < signed_index_t(facets_end_) &&
1393 neigh_f != signed_index_t(current_facet_)
1394 ) {
1395 if(!facet_is_marked[
1396 index_t(neigh_f)-facets_begin_
1397 ]) {
1398 facet_is_marked[
1399 index_t(neigh_f)-facets_begin_
1400 ] = true;
1401 adjacent_facets.push(
1402 FacetSeed(
1403 index_t(neigh_f),
1404 current_seed_
1405 )
1406 );
1407 }
1408 }
1409 signed_index_t neigh_s = ve.adjacent_seed();
1410 if(neigh_s != -1) {
1411 if(
1412 seed_stamp[neigh_s] != current_facet_
1413 ) {
1414 seed_stamp[neigh_s] = current_facet_;
1415 adjacent_seeds.push(index_t(neigh_s));
1416 }
1417 }
1418 }
1419 }
1420 }
1421 }
1422 }
1423 current_polygon_ = nullptr;
1424 }
1425
1439 template <class ACTION>
1440 inline void compute_volumetric(const ACTION& action) {
1441 if(connected_components_priority_) {
1442 this->template compute_volumetric_with_cnx_priority<ACTION>(
1443 action
1444 );
1445 } else {
1446 this->template compute_volumetric_with_seeds_priority<ACTION>(
1447 action
1448 );
1449 }
1450 }
1451
1464 template <class ACTION>
1465 inline void compute_volumetric_with_seeds_priority(const ACTION& action){
1466 if(
1467 tets_begin_ == UNSPECIFIED_RANGE &&
1468 tets_end_ == UNSPECIFIED_RANGE
1469 ) {
1470 tets_begin_ = 0;
1471 tets_end_ = mesh_->cells.nb();
1472 }
1473
1474 geo_assert(tets_begin_ != UNSPECIFIED_RANGE);
1475 geo_assert(tets_end_ != UNSPECIFIED_RANGE);
1476
1477 GEO::vector<index_t> seed_stamp(
1478 delaunay_->nb_vertices(), index_t(-1)
1479 );
1480 GEO::vector<bool> tet_is_marked(tets_end_-tets_begin_, false);
1482
1483 TetSeedStack adjacent_tets;
1484 SeedStack adjacent_seeds;
1485 Polyhedron C(dimension());
1486 GEO::Attribute<double> vertex_weight;
1487 vertex_weight.bind_if_is_defined(
1488 mesh_->vertices.attributes(), "weight"
1489 );
1490
1491 current_polyhedron_ = &C;
1492 // The algorithm propagates along both the facet-graph of
1493 // the surface and the 1-skeleton of the Delaunay triangulation,
1494 // and computes all the relevant intersections between
1495 // each Voronoi cell and facet.
1496 for(index_t t = tets_begin_; t < tets_end_; ++t) {
1497 if(!tet_is_marked[t-tets_begin_]) {
1498 // Propagate along the tet-graph.
1499 tet_is_marked[t-tets_begin_] = true;
1500 adjacent_tets.push(
1502 );
1503 while(!adjacent_tets.empty()) {
1504 current_tet_ = adjacent_tets.top().f;
1505 current_seed_ = adjacent_tets.top().seed;
1506 adjacent_tets.pop();
1507
1508 // Note: current cell could be looked up here,
1509 // (from current_tet_) if we chose to keep it
1510 // and copy it right before clipping (I am
1511 // not sure that it is worth it, lookup time
1512 // will be probably fast enough)
1513
1514 // Propagate along the Delaunay 1-skeleton
1515 // This will traverse all the seeds such that their
1516 // Voronoi cell has a non-empty intersection with
1517 // the current facet.
1518 seed_stamp[current_seed_] = current_tet_;
1519 adjacent_seeds.push(current_seed_);
1520
1521 while(!adjacent_seeds.empty()) {
1522 current_seed_ = adjacent_seeds.top();
1523 adjacent_seeds.pop();
1524
1526 mesh_, current_tet_, symbolic_, vertex_weight
1527 );
1528
1530 current_seed_, C
1531 );
1532
1533 action(
1534 current_seed_, current_tet_,
1536 );
1537
1538 // Propagate to adjacent tets and adjacent seeds
1539 // Iterate on the vertices of the cell (remember:
1540 // the cell is represented in dual form)
1541 for(index_t v = 0;
1542 v < current_polyhedron().max_v(); ++v
1543 ) {
1544
1545 // Skip clipping planes that are no longer
1546 // connected to a cell facet.
1547 if(
1549 == -1
1550 ) {
1551 continue;
1552 }
1553
1554 signed_index_t id =
1556 if(id > 0) {
1557 // Propagate to adjacent seed
1558 index_t neigh_s = index_t(id - 1);
1559 if(seed_stamp[neigh_s] != current_tet_) {
1560 seed_stamp[neigh_s] = current_tet_;
1561 adjacent_seeds.push(neigh_s);
1562 }
1563 } else if(id < 0) {
1564 // id==0 corresponds to facet on boundary
1565 // (skipped)
1566 // id<0 corresponds to adjacent tet index
1567
1568 // Propagate to adjacent tet
1569 signed_index_t neigh_t = -id - 1;
1570 if(
1571 neigh_t >=
1572 signed_index_t(tets_begin_) &&
1573 neigh_t < signed_index_t(tets_end_) &&
1574 neigh_t != signed_index_t(current_tet_)
1575 ) {
1576 if(!tet_is_marked[
1577 index_t(neigh_t)-tets_begin_
1578 ]) {
1579 tet_is_marked[
1580 index_t(neigh_t)-tets_begin_
1581 ] = true;
1582 adjacent_tets.push(
1583 TetSeed(
1584 index_t(neigh_t),
1585 current_seed_
1586 )
1587 );
1588 }
1589 }
1590 }
1591 }
1592 }
1593 }
1594 }
1595 }
1596 current_polyhedron_ = nullptr;
1597 }
1598
1599
1619 template <class ACTION>
1621 const ACTION& action
1622 ) {
1623
1624 if(
1625 tets_begin_ == UNSPECIFIED_RANGE &&
1626 tets_end_ == UNSPECIFIED_RANGE
1627 ) {
1628 tets_begin_ = 0;
1629 tets_end_ = mesh_->cells.nb();
1630 }
1631
1632 geo_assert(tets_begin_ != UNSPECIFIED_RANGE);
1633 geo_assert(tets_end_ != UNSPECIFIED_RANGE);
1634
1635 current_polyhedron_ = nullptr;
1637
1638 std::deque<TetSeed> adjacent_seeds;
1639 std::stack<index_t> adjacent_tets;
1640
1641 static constexpr index_t NO_STAMP = index_t(-1);
1642 GEO::vector<index_t> tet_stamp(
1643 tets_end_ - tets_begin_, NO_STAMP
1644 );
1645
1646 TetSeedMarking visited(
1647 tets_end_ - tets_begin_, delaunay_->nb_vertices()
1648 );
1649
1650 // Yes, facet_seed_marking_ points to the TetSeedMarking,
1651 // (TetSeedMarking is typedef-ed as FacetSeedMarking),
1652 // ugly I know... to be revised.
1653 facet_seed_marking_ = &visited;
1654 Polyhedron C(dimension());
1655 current_polyhedron_ = &C;
1656
1657 current_connected_component_ = 0;
1658 // index_t C_index = tets_end_ + 1; // Unused (see comment later)
1659
1660 GEO::Attribute<double> vertex_weight;
1661 vertex_weight.bind_if_is_defined(
1662 mesh_->vertices.attributes(),"weight"
1663 );
1664
1665 // The algorithm propagates along both the facet-graph of
1666 // the surface and the 1-skeleton of the Delaunay triangulation,
1667 // and computes all the relevant intersections between
1668 // each Voronoi cell and facet.
1669 for(index_t t = tets_begin_; t < tets_end_; ++t) {
1670 if(tet_stamp[t - tets_begin_] == NO_STAMP) {
1671 current_tet_ = t;
1672 current_seed_ = find_seed_near_tet(t);
1673
1674 adjacent_seeds.push_back(
1675 TetSeed(current_tet_, current_seed_)
1676 );
1677
1678 // Propagate along the Delaunay-graph.
1679 while(!adjacent_seeds.empty()) {
1680 // Yes, f, because TetSeed is typedef-ed as FacetSeed
1681 current_tet_ = adjacent_seeds.front().f;
1682 current_seed_ = adjacent_seeds.front().seed;
1683 adjacent_seeds.pop_front();
1684 if(
1685 tet_stamp[current_tet_ - tets_begin_] ==
1686 current_seed_
1687 ) {
1688 continue;
1689 }
1690
1691 if(visited.is_marked(current_tet_, current_seed_)) {
1692 continue;
1693 }
1694
1695 connected_component_changed_ = true;
1696 adjacent_tets.push(current_tet_);
1697 tet_stamp[current_tet_ - tets_begin_] =
1698 current_seed_;
1699 while(!adjacent_tets.empty()) {
1700 current_tet_ = adjacent_tets.top();
1701 adjacent_tets.pop();
1702
1703 // Copy the current tet from the Mesh into
1704 // RestrictedVoronoiDiagram's Polyhedron
1705 // data structure (gathers all the necessary
1706 // information)
1707
1709 mesh_, current_tet_, symbolic_, vertex_weight
1710 );
1711
1712 // Note: difference with
1713 // compute_surfacic_with_cnx_priority():
1714 // Since intersect_cell_cell() overwrites C, we
1715 // need to initialize C from the mesh for each
1716 // visited (tet,seed) pair (and the test for
1717 // current_tet_ change with C_index is not
1718 // used here).
1719 // C_index = current_tet_;
1720
1721 intersect_cell_cell(current_seed_, C);
1722 action(
1723 current_seed_, current_tet_, current_polyhedron()
1724 );
1725 connected_component_changed_ = false;
1726
1727 bool touches_RVC_border = false;
1728
1729 // Propagate to adjacent tets and adjacent seeds
1730 for(index_t v = 0;
1731 v < current_polyhedron().max_v(); ++v
1732 ) {
1733
1734 // Skip clipping planes that are no longer
1735 // connected to a cell facet.
1736 if(
1738 == -1
1739 ) {
1740 continue;
1741 }
1742
1743 signed_index_t id =
1745 if(id < 0) {
1746 // id == 0 corresponds to facet on boundary
1747 // (skipped)
1748 // id < 0 corresponds to adjacent tet index
1749 signed_index_t s_neigh_t = -id-1;
1750 if(
1751 s_neigh_t >= signed_index_t(tets_begin_)
1752 &&
1753 s_neigh_t < signed_index_t(tets_end_)
1754 ) {
1756 s_neigh_t !=
1757 signed_index_t(current_tet_)
1758 );
1759 index_t neigh_t = index_t(s_neigh_t);
1760 if(
1761 tet_stamp[neigh_t - tets_begin_] !=
1762 current_seed_
1763 ) {
1764 tet_stamp[neigh_t - tets_begin_] =
1765 current_seed_;
1766 adjacent_tets.push(neigh_t);
1767 }
1768 }
1769 } else if(id > 0) {
1770 index_t neigh_s = index_t(id-1);
1771 touches_RVC_border = true;
1772 TetSeed ts(current_tet_, neigh_s);
1773 if(!visited.is_marked(ts)) {
1774 adjacent_seeds.push_back(ts);
1775 }
1776 }
1777
1778 }
1779 if(touches_RVC_border) {
1780 visited.mark(
1781 TetSeed(current_tet_, current_seed_),
1782 current_connected_component_
1783 );
1784 }
1785 }
1786 ++current_connected_component_;
1787 }
1788 }
1789 }
1790 facet_seed_marking_ = nullptr;
1791 }
1792
1793
1794 public:
1800 bool facet_seed_is_visited(index_t f, index_t s) const {
1801 geo_debug_assert(facet_seed_marking_ != nullptr);
1802 return facet_seed_marking_->is_marked(FacetSeed(f, s));
1803 }
1804
1813 index_t get_facet_seed_connected_component(index_t f, index_t s) const {
1814 geo_debug_assert(facet_seed_marking_ != nullptr);
1815 return facet_seed_marking_->get_connected_component(
1816 FacetSeed(f, s)
1817 );
1818 }
1819
1824 return connected_component_changed_;
1825 }
1826
1831 return current_connected_component_;
1832 }
1833
1834 protected:
1855 template <class ACTION>
1857 const ACTION& action
1858 ) {
1859
1860 if(
1861 facets_begin_ == UNSPECIFIED_RANGE &&
1862 facets_end_ == UNSPECIFIED_RANGE
1863 ) {
1864 facets_begin_ = 0;
1865 facets_end_ = mesh_->facets.nb();
1866 }
1867
1868 current_polygon_ = nullptr;
1870
1871 std::deque<FacetSeed> adjacent_seeds;
1872 std::stack<index_t> adjacent_facets;
1873
1874 static constexpr index_t NO_STAMP = index_t(-1);
1875 GEO::vector<index_t> facet_stamp(
1876 facets_end_ - facets_begin_, NO_STAMP
1877 );
1878
1879 FacetSeedMarking visited(
1880 facets_end_ - facets_begin_, delaunay_->nb_vertices()
1881 );
1882
1883 facet_seed_marking_ = &visited;
1884 Polygon F;
1885 current_connected_component_ = 0;
1886 index_t F_index = facets_end_ + 1;
1887
1888 GEO::Attribute<double> vertex_weight;
1889 vertex_weight.bind_if_is_defined(
1890 mesh_->vertices.attributes(),"weight"
1891 );
1892
1893 // The algorithm propagates along both the facet-graph of
1894 // the surface and the 1-skeleton of the Delaunay triangulation,
1895 // and computes all the relevant intersections between
1896 // each Voronoi cell and facet.
1897 for(index_t f = facets_begin_; f < facets_end_; ++f) {
1898 if(facet_stamp[f - facets_begin_] == NO_STAMP) {
1899 current_facet_ = f;
1900 current_seed_ = find_seed_near_facet(f);
1901
1902 adjacent_seeds.push_back(
1903 FacetSeed(current_facet_, current_seed_)
1904 );
1905
1906 // Propagate along the Delaunay-graph.
1907 while(
1908 !adjacent_seeds.empty()
1909 ) {
1910 current_facet_ = adjacent_seeds.front().f;
1911 current_seed_ = adjacent_seeds.front().seed;
1912 adjacent_seeds.pop_front();
1913 if(
1914 facet_stamp[current_facet_ - facets_begin_] ==
1915 current_seed_
1916 ) {
1917 continue;
1918 }
1919
1920 if(visited.is_marked(current_facet_, current_seed_)) {
1921 continue;
1922 }
1923
1924 connected_component_changed_ = true;
1925 adjacent_facets.push(current_facet_);
1926 facet_stamp[current_facet_ - facets_begin_] =
1927 current_seed_;
1928 while(!adjacent_facets.empty()) {
1929 current_facet_ = adjacent_facets.top();
1930 adjacent_facets.pop();
1931
1932 // Copy the current facet from the Mesh into
1933 // RestrictedVoronoiDiagram's Polygon data structure
1934 // (gathers all the necessary information)
1935 if(F_index != current_facet_) {
1937 mesh_, current_facet_, symbolic_,
1938 vertex_weight
1939 );
1940 F_index = current_facet_;
1941 }
1942
1943 current_polygon_ = intersect_cell_facet(
1944 current_seed_, F
1945 );
1946 action(
1947 current_seed_, current_facet_, current_polygon()
1948 );
1949 connected_component_changed_ = false;
1950
1951 bool touches_RVC_border = false;
1952
1953 // Propagate to adjacent facets and adjacent seeds
1954 for(index_t v = 0;
1955 v < current_polygon().nb_vertices(); v++
1956 ) {
1957 const Vertex& ve = current_polygon().vertex(v);
1958 signed_index_t s_neigh_f = ve.adjacent_facet();
1959 if(
1960 s_neigh_f >= signed_index_t(facets_begin_)
1961 &&
1962 s_neigh_f < signed_index_t(facets_end_)
1963 ) {
1965 s_neigh_f !=
1966 signed_index_t(current_facet_)
1967 );
1968 index_t neigh_f = index_t(s_neigh_f);
1969 if(
1970 facet_stamp[neigh_f - facets_begin_] !=
1971 current_seed_
1972 ) {
1973 facet_stamp[neigh_f - facets_begin_] =
1974 current_seed_;
1975 adjacent_facets.push(neigh_f);
1976 }
1977 }
1978 signed_index_t neigh_s = ve.adjacent_seed();
1979 if(neigh_s != -1) {
1980 touches_RVC_border = true;
1981 FacetSeed fs(
1982 current_facet_, index_t(neigh_s)
1983 );
1984 if(!visited.is_marked(fs)) {
1985 adjacent_seeds.push_back(fs);
1986 }
1987 }
1988 }
1989 if(touches_RVC_border) {
1990 visited.mark(
1991 FacetSeed(current_facet_, current_seed_),
1992 current_connected_component_
1993 );
1994 }
1995 }
1996 ++current_connected_component_;
1997 }
1998 }
1999 }
2000 facet_seed_marking_ = nullptr;
2001 }
2002
2010 index_t find_seed_near_facet(index_t f) {
2011 const double* p = mesh_->vertices.point_ptr(
2012 mesh_->facets.vertex(f,0)
2013 );
2014 return find_seed_near_point(p);
2015 }
2016
2024 index_t find_seed_near_tet(index_t t) {
2025 index_t v = mesh_->cells.tet_vertex(t, 0);
2026 const double* p = mesh_->vertices.point_ptr(v);
2027 return find_seed_near_point(p);
2028 }
2029
2036 index_t find_seed_near_point(const double* p) {
2037 // In order to be compatible with the symbolic
2038 // perturbation, if the nearest neighbor is
2039 // non-unique, we need to return the one of
2040 // lowest index (because in case of several seeds
2041 // at equal distance, the one of lowest index
2042 // is guaranteed to have the facet in its Voronoi
2043 // cell from the point of view of symbolic
2044 // perturbation).
2045 if(exact_ && delaunay_nn_ != nullptr) {
2046 // TODO: may need more than 10
2047 index_t neighbors[10];
2048 double neighbors_sq_dist[10];
2049 index_t nb = 10;
2050 if(delaunay_nn_->nb_vertices() < nb) {
2051 nb = delaunay_nn_->nb_vertices();
2052 }
2053 delaunay_nn_->nn_search()->get_nearest_neighbors(
2054 nb, p, neighbors, neighbors_sq_dist
2055 );
2056 index_t nearest = neighbors[0];
2057 double min_d = neighbors_sq_dist[0];
2058 for(index_t i = 1; i < nb; ++i) {
2059 if(neighbors_sq_dist[i] != min_d) {
2060 break;
2061 }
2062 if(neighbors[i] < nearest) {
2063 nearest = neighbors[i];
2064 }
2065 }
2066 return nearest;
2067 }
2068
2069 return delaunay_->nearest_vertex(p);
2070 }
2071
2082 void swap_polygons(Polygon*& ping, Polygon*& pong) {
2083 if(ping != &P1 && ping != &P2) {
2084 // First clipping operation, ping points to F
2085 // (current facet copied)
2086 ping = &P2;
2087 pong = &P1;
2088 } else {
2089 std::swap(ping, pong);
2090 }
2091 }
2092
2101 intersections_.clear();
2102
2103 // Initialize ping-pong pointers for Sutherland-Hodgman
2104 // re-entrant clipping and copy current facet into 'ping' buffer.
2105 Polygon* ping = &F;
2106 Polygon* pong = &P2;
2107
2108 // Clip current facet by current Voronoi cell (associated with seed)
2109 if(delaunay_nn_ != nullptr) {
2110 clip_by_cell_SR(seed, ping, pong); // "Security Radius" mode.
2111 } else {
2112 clip_by_cell(seed, ping, pong); // Standard mode.
2113 }
2114
2115 return ping; // Yes, 'ping', and not 'pong'
2116 // see comments in clip_by_cell()
2117 }
2118
2134 void clip_by_cell_SR(index_t i, Polygon*& ping, Polygon*& pong) {
2135 // 'Security radius' mode.
2136 // Note: the vertices of the neighborhood are returned in
2137 // increasing distance to pi. We stop the clippings as soon as the
2138 // 'security radius' is reached.
2139 const double* geo_restrict pi = delaunay_->vertex_ptr(i);
2141
2142 index_t jj = 0;
2143 index_t prev_nb_neighbors = 0;
2144 neighbors_.resize(0);
2145 while(neighbors_.size() < delaunay_nn_->nb_vertices() - 1) {
2146
2147 delaunay_nn_->get_neighbors(i, neighbors_);
2148 if(neighbors_.size() == 0) {
2149 return;
2150 }
2151 if(prev_nb_neighbors == neighbors_.size()) {
2152 return;
2153 }
2154
2155 for(; jj < neighbors_.size(); jj++) {
2156 index_t j = neighbors_[jj];
2157 double R2 = 0.0;
2158 for(index_t k = 0; k < ping->nb_vertices(); k++) {
2159 geo_decl_aligned(double dik);
2160 const double* geo_restrict pk = ping->vertex(k).point();
2162 dik = GEO::Geom::distance2(pi, pk, dimension());
2163 R2 = std::max(R2, dik);
2164 }
2165 geo_decl_aligned(double dij);
2166 const double* geo_restrict pj = delaunay_->vertex_ptr(j);
2168 dij = GEO::Geom::distance2(pi, pj, dimension());
2169 // A little bit more than 4, because when
2170 // exact predicates are used, we need to
2171 // include tangent bisectors in the computation.
2172 if(dij > 4.1 * R2) {
2173 return;
2174 }
2175 clip_by_plane(*ping, *pong, i, j);
2176 swap_polygons(ping, pong);
2177 }
2178
2179 if(!check_SR_) {
2180 return;
2181 }
2182
2183 index_t nb_neighbors = neighbors_.size();
2184 prev_nb_neighbors = nb_neighbors;
2185
2186 if(nb_neighbors > 8) {
2187 nb_neighbors += nb_neighbors / 8;
2188 } else {
2189 nb_neighbors++;
2190 }
2191
2192 nb_neighbors = std::min(
2193 nb_neighbors,
2194 delaunay_nn_->nb_vertices() - 1
2195 );
2196
2197 delaunay_nn_->enlarge_neighborhood(i, nb_neighbors);
2198 }
2199 }
2200
2217 void clip_by_cell(index_t i, Polygon*& ping, Polygon*& pong) {
2218 get_neighbors(i);
2219 for(index_t jj = 0; jj < neighbors_.size(); jj++) {
2220 index_t j = neighbors_[jj];
2221 clip_by_plane(*ping, *pong, i, j);
2222 swap_polygons(ping, pong);
2223 }
2224 }
2225
2241 Polygon& ping, Polygon& pong,
2242 index_t i, index_t j
2243 ) {
2244 ping.clip_by_plane<DIM>(
2245 pong, intersections_, mesh_, delaunay_, i, j, exact_, symbolic_
2246 );
2247 }
2248
2255 public:
2262 void intersect_cell_cell(index_t seed, Polyhedron& C) {
2263 // Clip current facet by current Voronoi cell (associated with seed)
2264 if(delaunay_nn_ != nullptr) {
2265 clip_by_cell_SR(seed, C); // "Security Radius" mode.
2266 } else {
2267 clip_by_cell(seed, C); // Standard mode.
2268 }
2269 }
2270
2271 protected:
2278 void clip_by_cell_SR(index_t seed, Polyhedron& C) {
2279
2280 // 'Security radius' mode.
2281 // Note: the vertices of the neighborhood are returned in
2282 // increasing distance to pi. We stop the clippings as soon as the
2283 // 'security radius' is reached.
2284 const double* geo_restrict pi = delaunay_->vertex_ptr(seed);
2286
2287 index_t jj = 0;
2288 index_t prev_nb_neighbors = 0;
2289 neighbors_.resize(0);
2290
2291 while(neighbors_.size() < delaunay_nn_->nb_vertices() - 1) {
2292
2293 delaunay_nn_->get_neighbors(seed, neighbors_);
2294 if(neighbors_.size() == 0) {
2295 return;
2296 }
2297 if(prev_nb_neighbors == neighbors_.size()) {
2298 return;
2299 }
2300
2301 for(; jj < neighbors_.size(); jj++) {
2302 index_t j = neighbors_[jj];
2303 double R2 = 0.0;
2304 for(index_t k = 0; k < C.max_t(); ++k) {
2305 if(!C.triangle_is_used(k)) {
2306 continue;
2307 }
2308 geo_decl_aligned(double dik);
2309 const double* geo_restrict pk =
2310 C.triangle_dual(k).point();
2312 dik = GEO::Geom::distance2(pi, pk, dimension());
2313 R2 = std::max(R2, dik);
2314 }
2315 geo_decl_aligned(double dij);
2316 const double* geo_restrict pj = delaunay_->vertex_ptr(j);
2318 dij = GEO::Geom::distance2(pi, pj, dimension());
2319 // A little bit more than 4, because when
2320 // exact predicates are used, we need to
2321 // include tangent bisectors in the computation.
2322 if(dij > 4.1 * R2) {
2323 return;
2324 }
2325 clip_by_plane(C, seed, j);
2326 }
2327
2328 if(!check_SR_) {
2329 return;
2330 }
2331
2332 index_t nb_neighbors = neighbors_.size();
2333 prev_nb_neighbors = nb_neighbors;
2334
2335 if(nb_neighbors > 8) {
2336 nb_neighbors += nb_neighbors / 8;
2337 } else {
2338 nb_neighbors++;
2339 }
2340
2341 nb_neighbors = std::min(
2342 nb_neighbors,
2343 delaunay_nn_->nb_vertices() - 1
2344 );
2345 delaunay_nn_->enlarge_neighborhood(seed, nb_neighbors);
2346 }
2347 }
2348
2355 void clip_by_cell(index_t seed, Polyhedron& C) {
2356 get_neighbors(seed);
2357 // Check whether cell is empty (may happen with
2358 // power diagrams)
2359 if(neighbors_.size() == 0) {
2360 C.clear();
2361 }
2362 for(index_t jj = 0; jj < neighbors_.size(); jj++) {
2363 index_t j = neighbors_[jj];
2364 clip_by_plane(C, seed, j);
2365 }
2366 }
2367
2375 void clip_by_plane(Polyhedron& C, index_t i, index_t j) {
2376 C.clip_by_plane<DIM>(
2377 mesh_, delaunay_, i, j, exact_, symbolic_
2378 );
2379 }
2380
2396 // In dimension 3 (and if we do not used the ANN-based algorithm),
2397 // we can use the faster 'stamp-based' algorithm for finding the
2398 // neighbors.
2399 if(delaunay_->dimension() == 3 && delaunay_->nb_cells() != 0) {
2400 cur_stamp_ = 0;
2401 stamp_.assign(delaunay_->nb_vertices(), NO_INDEX);
2402 }
2403 }
2404
2412 void get_neighbors(index_t v) {
2413 if(stamp_.size() == 0) {
2414 // Used in ANN mode and with higher dimensions.
2415 delaunay_->get_neighbors(v, neighbors_);
2416 } else {
2417 // Used in 3D mode with standard Delaunay.
2418 // The following loop replaces
2419 // delaunay_->get_neighbors(v,neighbors_) ;
2420 // (and makes the overall algorithm 10 to 30% more efficient)
2421 neighbors_.resize(0);
2422 index_t t = index_t(delaunay_->vertex_cell(v));
2423 do {
2424 index_t lv = delaunay_->index(t, v);
2425 for(index_t lw = 0; lw < delaunay_->cell_size(); lw++) {
2426 if(lw != lv) {
2427 index_t w = index_t(delaunay_->cell_vertex(t, lw));
2428 if(stamp_[w] != cur_stamp_) {
2429 stamp_[w] = cur_stamp_;
2430 neighbors_.push_back(w);
2431 }
2432 }
2433 }
2434 t = index_t(delaunay_->next_around_vertex(t, lv));
2435 } while(t != index_t(delaunay_->vertex_cell(v)));
2436 cur_stamp_++;
2437 }
2438 }
2439
2442 protected:
2443 GEO::Mesh* mesh_;
2444 Delaunay* delaunay_;
2445 GEO::Delaunay_NearestNeighbors* delaunay_nn_;
2446
2447 PointAllocator intersections_;
2448 Polygon* current_polygon_;
2449 Polygon P1, P2;
2450 GEO::vector<index_t> neighbors_;
2451 index_t current_facet_;
2452 index_t current_seed_;
2453 Polyhedron* current_polyhedron_;
2454 index_t current_tet_;
2455
2456 // For optimized get_neighbors().
2457 index_t cur_stamp_;
2458 GEO::vector<index_t> stamp_;
2459
2460 bool symbolic_;
2461 bool check_SR_;
2462 bool exact_;
2463
2464 coord_index_t dimension_;
2465
2466 static constexpr index_t UNSPECIFIED_RANGE = index_t(-1);
2467
2468 index_t facets_begin_;
2469 index_t facets_end_;
2470
2471 index_t tets_begin_;
2472 index_t tets_end_;
2473
2474 bool connected_components_priority_;
2475 FacetSeedMarking* facet_seed_marking_;
2476 bool connected_component_changed_;
2477 index_t current_connected_component_;
2478
2483
2487 thisclass& operator= (const thisclass& rhs) = delete;
2488 };
2489}
2490
2491namespace GEO {
2492
2497}
2498
2499#endif
Declaration of base types for implementing user-defined code that queries the cells of a restricted V...
A function to suppress unused parameters compilation warnings.
#define geo_assert(x)
Verifies that a condition is met.
Definition assert.h:149
#define geo_debug_assert(x)
Verifies that a condition is met.
Definition assert.h:196
Generic mechanism for attributes.
A Corner corresponds to a vertex seen from a triangle.
Computes the intersection between a set of halfspaces.
const GEOGen::Vertex & triangle_dual(index_t t) const
Gets the dual vertex that corresponds to a triangle.
index_t max_v() const
Gets the maximum valid vertex index plus one.
void initialize_from_mesh_tetrahedron(const Mesh *mesh, index_t t, bool symbolic, const GEO::Attribute< double > &vertex_weight)
Assigns a mesh tetrahedron to this ConvexCell.
signed_index_t clip_by_plane(const Mesh *mesh, const Delaunay *delaunay, index_t i, index_t j, bool exact, bool symbolic)
Clips this ConvexCell with a plane.
signed_index_t vertex_id(index_t v) const
Gets the id of a vertex.
void clear()
Clears this ConvexCell.
index_t find_triangle_vertex(index_t t, index_t v) const
Finds the local index of a triangle vertex.
bool triangle_is_used(index_t t) const
Tests whether a given triangle is used.
void move_to_next_around_vertex(Corner &c) const
Replaces a corner by the next corner obtained by turing around the vertex.
signed_index_t vertex_triangle(index_t v) const
Gets one of the triangles incident to a vertex.
index_t max_t() const
Gets the maximum valid triangle index plus one.
Stores associations between (facet,seed) pairs and the index of a connected component.
index_t get_connected_component(const FacetSeed &fs) const
Gets the index of the connected component associated with a given FacetSeed.
void mark(const FacetSeed &fs, index_t conn_comp)
Marks a FacetSeed and sets the associated connected component index.
bool is_marked(index_t facet, index_t seed) const
Tests whether a given facet,seed couple is marked.
An allocator for points that are created from intersections in GenericVoronoiDiagram.
void clear()
Clears this PointAllocator.
Internal representation of polygons for GenericVoronoiDiagram.
void initialize_from_mesh_facet(const Mesh *mesh, index_t f, bool symbolic, const GEO::Attribute< double > &vertex_weight)
Assigns a mesh facet to this Polygon.
index_t nb_vertices() const
Gets the number of vertices.
const Vertex & vertex(index_t i) const
Gets a vertex by index.
index_t next_vertex(index_t i) const
Gets the index of the successor of a Vertex.
void clip_by_plane(Polygon &target, PointAllocator &target_intersections, const Mesh *mesh, const Delaunay *delaunay, index_t i, index_t j, bool exact, bool symbolic)
Clips a polygon with a plane.
Adapter class used internally to implement for_each_border_halfedge().
BorderHalfedgeAction(const ACTION &do_it)
Creates a new BorderHalfedgeAction that wraps a user ACTION instance.
void operator()(index_t v, index_t f, const Polygon &P) const
Callback called for each integration simplex.
Adapter class used internally to implement for_each_halfedge().
void operator()(index_t v, index_t f, const Polygon &P) const
Callback called for each integration simplex.
HalfedgeAction(const ACTION &do_it)
Creates a new HalfedgeAction that wraps a user ACTION instance.
Adapter class used internally to implement for_each_polygon()
void operator()(index_t v, index_t f, const Polygon &P) const
Callback called for each polygon.
PolygonAction(const ACTION &do_it)
Creates a new PolygonAction around a user ACTION instance.
Adapter class used internally to implement for_each_polyhedron()
void operator()(index_t v, index_t t, const Polyhedron &C) const
Callback called for each polyhedron.
PolyhedronAction(const ACTION &do_it)
Creates a new PolyhedronAction that wraps a user ACTION instance.
Adapter class used internally to implement for_each_primal_tetrahedron()
PrimalTetrahedronAction(const ACTION &do_it)
Constructs a new PrimalTetrahedronAction.
void operator()(index_t v, index_t t, const Polyhedron &C) const
Callback called for each polyhedron.
Adapter class used internally to implement for_each_primal_triangle()
void operator()(index_t iv1, index_t f, const Polygon &P) const
Callback called for each primal triangle.
PrimalTriangleAction(const ACTION &do_it)
Creates a new PrimalTriangleAction that wraps a user ACTION instance.
Adapter class used internally to implement for_each_tetrahedron()
TetrahedronAction(const ACTION &do_it)
Creates a new TetrahedronAction that wraps a user ACTION instance.
bool facet_is_incident_to_vertex(const Polyhedron &C, Polyhedron::Corner &c, index_t t) const
Tests whether a Polyhedron facet is incident to a vertex.
void operator()(index_t v, index_t t, const Polyhedron &C) const
Callback called for each polyhedron.
Adapter class used internally to implement for_each_triangle().
void operator()(index_t v, index_t f, const Polygon &P) const
Callback called for each integration simplex.
TriangleAction(const ACTION &do_it)
Creates a new TriangleAction that wraps a user ACTION instance.
Adapter class used internally to implement for_each_volumetric_integration_simplex()
VolumetricIntegrationSimplexAction(const ACTION &do_it, bool visit_inner_tets=false, bool coherent_triangles=false)
Creates a new VolumetricIntegrationSimplexAction that wraps a user ACTION instance.
void operator()(index_t v, index_t t, const Polyhedron &C) const
Callback called for each polyhedron.
void move_to_first_corner_of_facet(const Polyhedron &C, Polyhedron::Corner &c, index_t center_vertex_id) const
Finds the first corner of a facet in a Polyhedron.
static bool symbolic_compare(const Vertex &p1, const Vertex &p2, index_t center_vertex_id)
Compares the symbolic information of two vertices in such a way that a global order is defined.
Computes the intersection between a surface (Mesh) and a Voronoi diagram (dual of a Delaunay).
Definition generic_RVD.h:78
GEOGen::Vertex Vertex
Internal representation of vertices.
Definition generic_RVD.h:99
index_t nb_facets_in_range() const
Gets the number of facets in the current range.
Polygon * intersect_cell_facet(index_t seed, Polygon &F)
Computes the intersection between the Voronoi cell of a seed and a facet.
void clip_by_cell(index_t seed, Polyhedron &C)
Computes the intersection between a Voronoi cell and a cell in plain mode.
void set_exact_predicates(bool x)
Specifies whether exact predicates should be used.
void for_each_primal_triangle(const PRIMTRIACTION &action)
Iterates on the triangles of the Restricted Delaunay Triangulation.
static coord_index_t dimension()
Gets the dimension.
Definition generic_RVD.h:87
Delaunay * delaunay()
Gets the Delaunay triangulation.
index_t find_seed_near_point(const double *p)
Finds a seed near a given point.
GEOGen::ConvexCell Polyhedron
Internal representation of volumetric cells.
GEOGen::Polygon Polygon
Internal representation of polygons.
void set_mesh(GEO::Mesh *mesh)
Sets the input mesh.
void for_each_polyhedron(const ACTION &action)
Iterates on the polyhedra of this RVD.
void compute_volumetric_with_cnx_priority(const ACTION &action)
Low-level API of Restricted Voronoi Diagram traversal with connected components priority.
void for_each_triangle(const TRIACTION &action)
Iterates on the facets of this RVD, triangulated on the fly.
const Polyhedron & current_polyhedron() const
Gets the current cell.
void clip_by_plane(Polyhedron &C, index_t i, index_t j)
Computes the intersection between a Voronoi cell and a half-space determined by a bisector.
void for_each_tetrahedron(const ACTION &action)
Iterates on the polyhedra of this RVD decomposed on the fly into tetrahedra.
index_t current_seed() const
Gets the index of the Delaunay vertex currently processed.
void clip_by_cell(index_t i, Polygon *&ping, Polygon *&pong)
Computes the intersection between a Voronoi cell and a polygon.
bool symbolic() const
Tests whether symbolic mode is active.
void for_each_border_halfedge(const BOACTION &action)
Iterates on the halfedges on the borders of the restricted Voronoi cells that are on the boundary of ...
void for_each_halfedge(const HEACTION &action)
Iterates on the halfedges on the borders of the restricted Voronoi cells.
void compute_volumetric(const ACTION &action)
Low-level API of Restricted Voronoi Diagram traversal .
index_t current_connected_component() const
Gets the index of the current connected component.
GEO::Mesh * mesh()
Gets the input mesh.
void clip_by_cell_SR(index_t seed, Polyhedron &C)
Computes the intersection between a Voronoi cell and a cell in radius-of-security mode.
void for_each_polygon(const ACTION &action)
Iterates on the facets of this RVD.
index_t current_facet() const
Gets the index of the mesh facet currently processed.
const Delaunay * delaunay() const
Gets the Delaunay triangulation.
const Polygon & current_polygon() const
Gets the current polygon.
index_t current_tet() const
Gets the undex of the mesh tetrahedron currently processed.
void intersect_cell_cell(index_t seed, Polyhedron &C)
Computes the intersection between a Voronoi cell and a cell with radius of security or plain mode.
thisclass & operator=(const thisclass &rhs)=delete
Forbids assignment.
void clip_by_cell_SR(index_t i, Polygon *&ping, Polygon *&pong)
Computes the intersection between the Voronoi cell of a vertex and the Mesh 'ping'.
void get_neighbors(index_t v)
Caches the neighbors of a Delaunay vertex.
GEOGen::PointAllocator PointAllocator
Used to allocate the generated points.
Definition generic_RVD.h:94
void compute_volumetric_with_seeds_priority(const ACTION &action)
Low-level API of Restricted Voronoi Diagram traversal with seeds priority in volumetric mode.
void set_check_SR(bool x)
Specifies whether radius of security should be enforced.
PointAllocator * point_allocator()
Gets the PointAllocator.
RestrictedVoronoiDiagram(Delaunay *delaunay, GEO::Mesh *mesh)
Constructs a new RestrictedVoronoiDiagram.
bool connected_component_changed() const
Tests whether the current connected component changed.
void set_delaunay(Delaunay *delaunay)
Sets the Delaunay triangulation.
bool connected_components_priority() const
Tests whether connected components priority is set.
bool exact_predicates() const
Tests whether exact predicates are used.
index_t find_seed_near_facet(index_t f)
Finds a seed near a given facet.
void swap_polygons(Polygon *&ping, Polygon *&pong)
Swaps two pointers between two polygons.
void set_connected_components_priority(bool x)
Sets traveral priority.
void for_each_primal_tetrahedron(const ACTION &action)
Iterates on the primal tetrahedra of this RVD.
index_t get_facet_seed_connected_component(index_t f, index_t s) const
Gets the index of the connected component associated with a (facet,seed).
void set_symbolic(bool x)
Sets symbolic mode.
void init_get_neighbors()
Creates the data structure for optimized get_neighbors() function.
RestrictedVoronoiDiagram(const thisclass &rhs)=delete
Forbids construction from copy.
bool facet_seed_is_visited(index_t f, index_t s) const
Tests whether a (facet,seed) couple was visited.
void compute_surfacic(const ACTION &action)
Low-level API of Restricted Voronoi Diagram traversal.
void set_facets_range(index_t facets_begin, index_t facets_end)
Sets the facets range.
void compute_surfacic_with_seeds_priority(const ACTION &action)
Low-level API of Restricted Voronoi Diagram traversal with seeds priority in surfacic mode.
index_t nb_tetrahedra_in_range() const
Gets the number of tetrahedra in the current range.
bool check_SR() const
Tests whether radius of security is enforced.
void compute_surfacic_with_cnx_priority(const ACTION &action)
Low-level API of Restricted Voronoi Diagram traversal with connected components priority.
const GEO::Mesh * mesh() const
Gets the input mesh.
index_t find_seed_near_tet(index_t t)
Finds a seed near a given tetrahedron.
void clip_by_plane(Polygon &ping, Polygon &pong, index_t i, index_t j)
Computes the intersection between a polygon and a half-space.
void for_each_volumetric_integration_simplex(const ACTION &action, bool visit_inner_tets=false, bool coherent_triangles=false)
Iterates on the polyhedra of this RVD decomposed on the fly into tetrahedra.
void set_tetrahedra_range(index_t tets_begin, index_t tets_end)
Sets the tetrahedra range.
A set of three integers that encodes the equation of a vertex in GenericVoronoiDiagram.
index_t nb_bisectors() const
Gets the number of bisectors in the symbolic representation.
index_t bisector(signed_index_t i) const
Gets a bisector.
Internal representation of vertices in GenericVoronoiDiagram.
const double * point() const
Gets the geometric location at this Vertex.
const SymbolicVertex & sym() const
Gets the symbolic representation.
signed_index_t adjacent_seed() const
Gets the adjacent seed.
signed_index_t adjacent_facet() const
Gets the adjacent facet.
bool check_flag(EdgeFlag f) const
Tests an EdgeFlag in this Vertex.
bool bind_if_is_defined(AttributesManager &manager, const std::string &name)
Binds this Attribute to an AttributesManager if it already exists in the AttributesManager.
Manages an attribute attached to a set of object.
Delaunay interface for NearestNeighbors search.
Definition delaunay_nn.h:63
NearestNeighborSearch * nn_search()
Gets the NearestNeighborSearch used internally.
Definition delaunay_nn.h:92
virtual void enlarge_neighborhood(index_t i, index_t nb)
Stores nb neighbors with vertex i.
Abstract interface for Delaunay triangulation in Nd.
Definition delaunay.h:71
virtual index_t nearest_vertex(const double *p) const
Computes the nearest vertex from a query point.
index_t nb_cells() const
Gets the number of cells.
Definition delaunay.h:315
void get_neighbors(index_t v, vector< index_t > &neighbors) const
Gets the one-ring neighbors of vertex v.
Definition delaunay.h:477
index_t vertex_cell(index_t v) const
Gets an incident cell index by a vertex index.
Definition delaunay.h:445
coord_index_t dimension() const
Gets the dimension of this Delaunay.
Definition delaunay.h:171
index_t cell_vertex(index_t c, index_t lv) const
Gets a vertex index by cell index and local vertex index.
Definition delaunay.h:361
index_t cell_size() const
Gets the number of vertices in each cell.
Definition delaunay.h:180
index_t nb_vertices() const
Gets the number of vertices.
Definition delaunay.h:238
index_t next_around_vertex(index_t c, index_t lv) const
Traverses the list of cells incident to a vertex.
Definition delaunay.h:461
const double * vertex_ptr(index_t i) const
Gets a pointer to a vertex by its global index.
Definition delaunay.h:229
index_t index(index_t c, index_t v) const
Retrieves a local vertex index from cell index and global vertex index.
Definition delaunay.h:407
index_t vertex(index_t f, index_t lv) const
Gets a vertex by facet and local vertex index.
Definition mesh.h:1143
AttributesManager & attributes() const
Gets the attributes manager.
Definition mesh.h:109
index_t nb() const
Gets the number of (sub-)elements.
Definition mesh.h:98
const double * point_ptr(index_t v) const
Gets a point.
Definition mesh.h:477
Represents a mesh.
Definition mesh.h:3050
virtual void get_nearest_neighbors(index_t nb_neighbors, const double *query_point, index_t *neighbors, double *neighbors_sq_dist) const =0
Finds the nearest neighbors of a point given by coordinates.
Vector with aligned memory allocation.
Definition memory.h:660
index_t size() const
Gets the number of elements.
Definition memory.h:706
Some utilities for implementing surfacic and volumetric restricted Voronoi diagrams.
std::stack< TetSeed > TetSeedStack
A stack of TetSeed.
std::stack< FacetSeed > FacetSeedStack
A stack of FacetSeed.
std::stack< index_t > SeedStack
A stack of seed indices (index_t).
Common include file, providing basic definitions. Should be included before anything else by all head...
Geometric functions in arbitrary dimension.
Classes for managing tuples of indices.
#define geo_dim_alignment(dim)
Gets a point alignment.
Definition memory.h:270
#define geo_assume_aligned(var, alignment)
Informs the compiler that a given pointer is memory-aligned.
Definition memory.h:375
double distance2(const COORD_T *p1, const COORD_T *p2, coord_index_t dim)
Computes the squared distance between two nd points.
Definition geometry_nd.h:65
Global Vorpaline namespace.
Definition basic.h:55
index_t max_index_t()
Gets the maximum positive value of type index_t.
Definition numeric.h:334
void geo_argused(const T &)
Suppresses compiler warnings about unused parameters.
Definition argused.h:60
Types and functions for numbers manipulation.
Filtered exact predicates for restricted Voronoi diagrams.
Function and classes for process manipulation.
A (facet,seed) pair.