TTK
Loading...
Searching...
No Matches
FastRipsPersistenceDiagram2.cpp
Go to the documentation of this file.
2
3#ifdef TTK_ENABLE_CGAL
4
5using namespace ttk::rpd;
6
7FastRipsPersistenceDiagram2::FastRipsPersistenceDiagram2(
8 const PointCloud &points)
9 : n_(points.size()) {
10 // inherited from Debug: prefix will be printed at the beginning of every msg
11 this->setDebugMsgPrefix("GeometricRipsPD");
12 tm_.reStart();
13
14 points_ = std::vector<std::pair<Point, unsigned>>(n_);
15 for(unsigned i = 0; i < n_; ++i)
16 points_[i] = std::make_pair(Point(points[i][0], points[i][1]), i);
17 printMsg("Loaded", 0., tm_.getElapsedTime());
18
19 computeDelaunay();
20}
21
22FastRipsPersistenceDiagram2::FastRipsPersistenceDiagram2(float *data, int n)
23 : n_(n) {
24 // inherited from Debug: prefix will be printed at the beginning of every msg
25 this->setDebugMsgPrefix("GeometricRipsPD");
26 tm_.reStart();
27
28 points_ = std::vector<std::pair<Point, unsigned>>(n_);
29 for(unsigned i = 0; i < n_; ++i)
30 points_[i] = std::make_pair(Point(data[2 * i], data[2 * i + 1]), i);
31 printMsg("Loaded", 0., tm_.getElapsedTime());
32
33 computeDelaunay();
34}
35
36template <typename T>
37void FastRipsPersistenceDiagram2::compute0Persistence(T &ph0,
38 bool parallelSort) {
39 ph0 = T(0);
40
41 // keep only the Urquhart edges, maintain polygon structure
42 UnionFind UF(nFaces_);
43 std::vector<FiltratedEdge> max_delaunay(nFaces_, FiltratedEdge{{-1, -1}, 0.});
44 computeUrquhart(UF, max_delaunay, parallelSort);
45
46 // compute EMST with Kruskal algorithm
47 UnionFind UF_p(n_);
48 for(FiltratedQuadEdge const &e : urquhart_) {
49 if(UF_p.find(e.e.first)
50 != UF_p.find(e.e.second)) { // we know e is a EMST edge
51 UF_p.merge(e.e.first, e.e.second);
52 add0Pair(e, ph0);
53 }
54 }
55 if constexpr(std::is_same_v<T, Diagram>)
56 ph0.emplace_back(
57 FiltratedSimplex{{-1}, 0.}, FiltratedSimplex{{-1}, inf}); // infinite pair
58
59 printMsg("MST computed", 0., tm_.getElapsedTime());
60}
61template void
62 FastRipsPersistenceDiagram2::compute0Persistence(Diagram &ph0,
63 bool parallelSort);
64template void
65 FastRipsPersistenceDiagram2::compute0Persistence(EdgeSet &ph0,
66 bool parallelSort);
67
68template <typename T>
69void FastRipsPersistenceDiagram2::computeDelaunayRips0And1Persistence(
70 T &ph, bool parallelSort) {
71 if constexpr(std::is_same_v<T, MultidimensionalDiagram>)
73
74 // keep only the Urquhart edges, maintain polygon structure
75 UnionFind UF(nFaces_);
76 deathPoly_.resize(nFaces_, {{-1, -1}, 0.});
77 computeUrquhart(UF, deathPoly_, parallelSort);
78
79 // compute EMST with Kruskal algorithm
80 UnionFind UF_p(n_);
81 std::vector<FiltratedQuadEdge> critical(0);
82 for(FiltratedQuadEdge const &e : urquhart_) {
83 if(UF_p.find(e.e.first)
84 != UF_p.find(e.e.second)) { // we know e is a EMST edge
85 UF_p.merge(e.e.first, e.e.second);
86 add0Pair(e, ph[0]);
87 } else { // we know e is a UG-EMST edge, i.e., it creates a cycle
88 critical.push_back(e);
89 if constexpr(std::is_same_v<T, EdgeSets3>)
90 ph[1].emplace_back(e.e);
91 }
92 }
93 if constexpr(std::is_same_v<T, MultidimensionalDiagram>)
94 ph[0].emplace_back(
95 FiltratedSimplex{{-1}, 0.}, FiltratedSimplex{{-1}, inf}); // infinite pair
96
97 printMsg("MST computed", 0., tm_.getElapsedTime());
98
99 for(FiltratedQuadEdge &e : urquhart_) {
100 e.f1 = UF.find(e.f1); // final path compression
101 e.f2 = UF.find(e.f2); // final path compression
102 }
103 for(FiltratedQuadEdge &e : critical) {
104 e.f1 = UF.find(e.f1); // final path compression
105 e.f2 = UF.find(e.f2); // final path compression
106 }
107
108 if constexpr(std::is_same_v<T, MultidimensionalDiagram>)
109 compute1PH(critical, UF, ph);
110 else if constexpr(std::is_same_v<T, EdgeSets3>) {
111 for(unsigned poly = 0; poly < deathPoly_.size(); ++poly) {
112 if(UF.isRoot(poly) && deathPoly_[poly].d != inf)
113 ph[2].emplace_back(deathPoly_[poly].e);
114 }
115 }
116}
117template void FastRipsPersistenceDiagram2::computeDelaunayRips0And1Persistence(
118 MultidimensionalDiagram &ph, bool parallelSort);
119template void FastRipsPersistenceDiagram2::computeDelaunayRips0And1Persistence(
120 EdgeSets3 &ph, bool parallelSort);
121
122template <typename T>
123void FastRipsPersistenceDiagram2::computeRips0And1Persistence(
124 T &ph, bool parallelSort, bool parallelMML) {
125 if constexpr(std::is_same_v<T, MultidimensionalDiagram>)
127
128 // compute k-d tree
129 Tree tree;
130 for(auto const &p : points_)
131 tree.insert(p.first);
132 printMsg("k-d tree initialized", 0., tm_.getElapsedTime());
133
134 // keep only the Urquhart edges, maintain polygon structure
135 UnionFind UF(nFaces_);
136 std::vector<FiltratedEdge> max_delaunay(nFaces_, FiltratedEdge{{-1, -1}, 0.});
137 computeUrquhart(UF, max_delaunay, parallelSort);
138
139 // compute EMST with Kruskal algorithm
140 UnionFind UF_p(n_);
141 std::vector<FiltratedQuadEdge> critical(0); // RNG-EMST edges
142
143 for(FiltratedQuadEdge const &e : urquhart_) {
144 if(UF_p.find(e.e.first)
145 != UF_p.find(e.e.second)) { // we know e is a EMST edge
146 UF_p.merge(e.e.first, e.e.second);
147 rng_.push_back(e);
148 add0Pair(e, ph[0]);
149 } else { // we know e is a UG-EMST edge
150 // check if e is RNG
151 if(isLensEmpty(points_[e.e.first].first, points_[e.e.second].first, tree,
152 e.d)) { // RNG edge
153 critical.push_back(e);
154 rng_.push_back(e);
155 if constexpr(std::is_same_v<T,
156 EdgeSets3> || std::is_same_v<T, EdgeSets4>)
157 ph[1].emplace_back(e.e);
158 } else { // not RNG edge : merge neighboring polygons
159 const int poly1 = UF.find(e.f1);
160 const int poly2 = UF.find(e.f2);
161 UF.merge(poly1, poly2);
162 max_delaunay[UF.find(poly1)]
163 = max(FiltratedEdge{e.e, e.d},
164 max(max_delaunay[poly1], max_delaunay[poly2]));
165 }
166 }
167 }
168 if constexpr(std::is_same_v<T, MultidimensionalDiagram>)
169 ph[0].emplace_back(
170 FiltratedSimplex{{-1}, 0.}, FiltratedSimplex{{-1}, inf}); // infinite pair
171
172 printMsg("MST and RNG computed", 0., tm_.getElapsedTime());
173
174 // polygon reindexation
175 std::vector<int> index_polys(0);
176 reindexPolygons(UF, max_delaunay, index_polys);
177 for(FiltratedQuadEdge &e : rng_) {
178 e.f1 = index_polys[UF.find(e.f1)]; // final path compression
179 e.f2 = index_polys[UF.find(e.f2)]; // final path compression
180 }
181 for(FiltratedQuadEdge &e : critical) {
182 e.f1 = index_polys[UF.find(e.f1)]; // final path compression
183 e.f2 = index_polys[UF.find(e.f2)]; // final path compression
184 }
185
186 if constexpr(std::is_same_v<T, MultidimensionalDiagram>) {
187 computePolygonRipsDeath(parallelMML, UF, index_polys);
188 printMsg("MML edges computed", 0., tm_.getElapsedTime());
189 UnionFind UF_poly(deathPoly_.size());
190 compute1PH(critical, UF_poly, ph);
191 } else if constexpr(std::is_same_v<T, EdgeSets3>) {
192 computePolygonRipsDeath(parallelMML, UF, index_polys);
193 printMsg("MML edges computed", 0., tm_.getElapsedTime());
194 for(FiltratedEdge const &poly : deathPoly_) {
195 if(poly.d != inf)
196 ph[2].emplace_back(poly.e);
197 }
198 } else if constexpr(std::is_same_v<T, EdgeSets4>) {
199 executePolygonPairCells(parallelMML, UF, index_polys, ph);
200 printMsg("Local cascades computed", 0., tm_.getElapsedTime());
201 }
202}
203template void FastRipsPersistenceDiagram2::computeRips0And1Persistence(
204 MultidimensionalDiagram &ph, bool parallelSort, bool parallelMML);
205template void FastRipsPersistenceDiagram2::computeRips0And1Persistence(
206 EdgeSets3 &ph, bool parallelSort, bool parallelMML);
207template void FastRipsPersistenceDiagram2::computeRips0And1Persistence(
208 EdgeSets4 &ph, bool parallelSort, bool parallelMML);
209
210void FastRipsPersistenceDiagram2::computeDelaunay() {
211 // compute Delaunay and initialize triangle IDs (including infinite triangles
212 // to deal with the convex hull)
213 delaunay_ = Delaunay(points_.begin(), points_.end());
214 int k = 0;
215 for(auto const &f : delaunay_.all_face_handles())
216 f->id() = k++;
217 nFaces_ = k;
218
219 printMsg("Delaunay triangulation computed", 0., tm_.getElapsedTime());
220}
221
222void FastRipsPersistenceDiagram2::computeUrquhart(
223 UnionFind &UF, std::vector<FiltratedEdge> &maxDelaunay, bool parallelSort) {
224 for(Delaunay::Edge const &e : delaunay_.finite_edges()) {
225 const unsigned a = e.first->vertex((e.second + 1) % 3)->info();
226 const unsigned b = e.first->vertex((e.second + 2) % 3)->info();
227 const double d2
228 = CGAL::squared_distance(points_[a].first, points_[b].first);
229 const double d = sqrt(d2);
230 const Delaunay::Edge e_m = delaunay_.mirror_edge(e);
231
232 // check if edge is Urquhart
233 bool is_urquhart = true;
234 const Point p_k = e.first->vertex(e.second)->point();
235 if(!delaunay_.is_infinite(e.first)
236 && CGAL::squared_distance(points_[a].first, p_k) < d2
237 && CGAL::squared_distance(points_[b].first, p_k) < d2)
238 is_urquhart = false;
239 else {
240 const Point p_l = e_m.first->vertex(e_m.second)->point();
241 if(!delaunay_.is_infinite(e_m.first)
242 && CGAL::squared_distance(points_[a].first, p_l) < d2
243 && CGAL::squared_distance(points_[b].first, p_l) < d2)
244 is_urquhart = false;
245 }
246
247 // maintain polygon structure
248 if(is_urquhart) // UG edge
249 urquhart_.push_back(
250 FiltratedQuadEdge{{a, b}, e.first->id(), e_m.first->id(), d});
251 else { // nUG edge: maintain UF
252 const int poly1 = UF.find(e.first->id());
253 const int poly2 = UF.find(e_m.first->id());
254 maxDelaunay[UF.mergeRet(poly1, poly2)] = max(
255 FiltratedEdge{{a, b}, d}, max(maxDelaunay[poly1], maxDelaunay[poly2]));
256 }
257
258 // detect infinite polygons (going beyond convex hull)
259 if(delaunay_.is_infinite(e.first))
260 maxDelaunay[UF.find(e.first->id())].d = inf;
261 else if(delaunay_.is_infinite(e_m.first))
262 maxDelaunay[UF.find(e_m.first->id())].d = inf;
263 }
264 printMsg("Urquhart graph computed", 0., tm_.getElapsedTime());
265
266 if(parallelSort)
267 TTK_PSORT(
268 globalThreadNumber_, urquhart_.begin(), urquhart_.end(),
269 [](FiltratedQuadEdge e1, FiltratedQuadEdge e2) { return e1.d < e2.d; })
270 else
271 std::sort(
272 urquhart_.begin(), urquhart_.end(),
273 [](FiltratedQuadEdge e1, FiltratedQuadEdge e2) { return e1.d < e2.d; });
274 printMsg("Urquhart graph sorted", 0., tm_.getElapsedTime());
275}
276
277void FastRipsPersistenceDiagram2::compute1PH(
278 std::vector<FiltratedQuadEdge> const &critical,
279 UnionFind &UF,
281 std::vector<int> latest(deathPoly_.size());
282 std::iota(latest.begin(), latest.end(), 0);
283 birthPoly_.resize(deathPoly_.size());
284
285 for(auto it = critical.rbegin(); it != critical.rend(); ++it) {
286 const FiltratedQuadEdge e = *it;
287
288 const int v1 = UF.find(e.f1);
289 const int v2 = UF.find(e.f2);
290 UF.merge(v1, v2);
291
292 const int latest1 = latest[v1];
293 const int latest2 = latest[v2];
294
295 if(deathPoly_[latest1].d < deathPoly_[latest2].d) {
296 ph[1].emplace_back(FiltratedSimplex{{e.e.first, e.e.second}, e.d},
297 FiltratedSimplex{{deathPoly_[latest1].e.first,
298 deathPoly_[latest1].e.second},
299 deathPoly_[latest1].d});
300 birthPoly_[latest1] = e.d;
301 latest[UF.find(v1)] = latest2;
302 } else {
303 ph[1].emplace_back(FiltratedSimplex{{e.e.first, e.e.second}, e.d},
304 FiltratedSimplex{{deathPoly_[latest2].e.first,
305 deathPoly_[latest2].e.second},
306 deathPoly_[latest2].d});
307 birthPoly_[latest2] = e.d;
308 latest[UF.find(v1)] = latest1;
309 }
310 }
311
312 printMsg("Dual Kruskal complete", 0., tm_.getElapsedTime());
313}
314
315bool FastRipsPersistenceDiagram2::isLensEmpty(Point const &p1,
316 Point const &p2,
317 Tree const &tree,
318 double const &d) {
319 const Fuzzy_sphere fs(p1, d);
320 std::vector<Point> ball;
321 tree.search(back_inserter(ball), fs);
322 for(Point const &p : ball) {
323 if(p != p1 && p != p2) {
324 if(CGAL::squared_distance(p, p2) < d * d)
325 return false;
326 }
327 }
328 return true;
329}
330
331bool FastRipsPersistenceDiagram2::isRightSemiLensEmpty(Point const &p1,
332 Point const &p2,
333 Tree const &tree) {
334 const double d2 = CGAL::squared_distance(p1, p2);
335 const Fuzzy_sphere fs(p1, sqrt(d2));
336 std::vector<Point> ball;
337 tree.search(back_inserter(ball), fs);
338 for(Point const &p : ball) {
339 if(CGAL::squared_distance(p, p2) < d2 && CGAL::right_turn(p, p1, p2))
340 return false;
341 }
342 return true;
343}
344
345void FastRipsPersistenceDiagram2::computePolygonRipsDeath(
346 bool parallel, UnionFind &UF, std::vector<int> const &indexPolys) {
347 const unsigned N_polys = deathPoly_.size();
348 std::vector<std::vector<int>> vertices(N_polys);
349
350 // store coarsely vertices in each polygon
351 for(const FiltratedQuadEdge &e : rng_) {
352 vertices[e.f1].push_back(e.e.first);
353 vertices[e.f1].push_back(e.e.second);
354 vertices[e.f2].push_back(e.e.first);
355 vertices[e.f2].push_back(e.e.second);
356 }
357 std::vector<Delaunay::Face_handle> repr_face(N_polys);
358 for(auto const &fh : delaunay_.finite_face_handles()) {
359 if(UF.isRoot(fh->id()))
360 repr_face[indexPolys[fh->id()]] = fh;
361 }
362
363 // loop on polygons
364#ifdef TTK_ENABLE_OPENMP
365#pragma omp parallel for if(parallel)
366#else
367 TTK_FORCE_USE(parallel);
368#endif // TTK_ENABLE_OPENMP
369 for(unsigned poly = 0; poly < N_polys; ++poly) {
370 if(deathPoly_[poly].d != inf) { // deal only with true polygons
371 const double bound_max = deathPoly_[poly].d;
372 const double bound_min = sqrt(3) / 2 * bound_max;
373
374 // find involved vertices
375 sort(vertices[poly].begin(), vertices[poly].end());
376 auto last = unique(vertices[poly].begin(), vertices[poly].end());
377 vertices[poly].erase(last, vertices[poly].end());
378
379 std::vector<Point> pts(0);
380 std::vector<int> global(0);
381 for(int const &v : vertices[poly]) {
382 pts.push_back(points_[v].first);
383 global.push_back(v);
384 }
385 const unsigned N_pts = pts.size();
386 const Tree loc_tree(pts.begin(), pts.end());
387
388 // enumerate edges and do stuff
389 FiltratedEdge death{{-1, -1}, inf};
390 for(unsigned i = 0; i < N_pts - 1; ++i) {
391 for(unsigned j = i + 1; j < N_pts; ++j) {
392 const double d2 = CGAL::squared_distance(pts[i], pts[j]);
393 const double d = sqrt(d2);
394 if(bound_min <= d && d <= bound_max
395 && d < death
396 .d) { // edge length interval allowing to be a candidate
397 const Point p_i = pts[i];
398 const Point p_j = pts[j];
399
400 const Fuzzy_sphere fs(p_i, d);
401 std::vector<Point> ball;
402 loc_tree.search(back_inserter(ball), fs);
403
404 // check if edge is a 2-edge (i.e., both semi-lenses are not empty)
405 std::vector<Point> lens_l;
406 std::vector<Point> lens_r;
407 for(auto const &p : ball) {
408 if(p != p_i && p != p_j) {
409 if(CGAL::squared_distance(p, p_j) < d2) {
410 if(CGAL::right_turn(p_i, p_j, p))
411 lens_r.push_back(p);
412 else
413 lens_l.push_back(p);
414 }
415 }
416 }
417
418 // if 2-edge, check if it is expandable
419 if(!lens_l.empty() && !lens_r.empty()) { // 2-edge
420 bool l_expandable = false, r_expandable = false;
421 for(Point const &p : lens_l) {
422 if(!isRightSemiLensEmpty(p_i, p, loc_tree))
423 continue;
424 else if(isRightSemiLensEmpty(p, p_j, loc_tree)) {
425 l_expandable = true;
426 break;
427 }
428 }
429 if(!l_expandable)
430 continue;
431 for(Point const &p : lens_r) {
432 if(!isRightSemiLensEmpty(p_j, p, loc_tree))
433 continue;
434 else if(isRightSemiLensEmpty(p, p_i, loc_tree)) {
435 r_expandable = true;
436 break;
437 }
438 }
439 if(r_expandable) { // expandable 2-edge, check if it is a diagonal
440 // of the current polygon (costly)
441 if(indexPolys[UF.find(
442 delaunay_
443 .locate(CGAL::midpoint(p_i, p_j), repr_face[poly])
444 ->id())]
445 == int(poly))
446 death = FiltratedEdge{{global[i], global[j]}, d};
447 }
448 }
449 }
450 }
451 }
452 deathPoly_[poly] = death;
453 }
454 }
455}
456
457void FastRipsPersistenceDiagram2::reindexPolygons(
458 UnionFind const &UF,
459 std::vector<FiltratedEdge> const &maxDelaunay,
460 std::vector<int> &indexPolys) {
461 // reindex polygons and find those that are beyond convex hull
462 indexPolys = std::vector<int>(nFaces_, -1);
463 int N_polys = 0;
464 for(unsigned x = 0; x < nFaces_; ++x) {
465 if(UF.isRoot(x)) {
466 indexPolys[x] = N_polys;
467 deathPoly_.push_back(maxDelaunay[x]);
468 N_polys++;
469 }
470 }
471}
472
473void FastRipsPersistenceDiagram2::pComputePolygonRipsDeath(
474 UnionFind &UF, std::vector<int> const &indexPolys) {
475 const unsigned N_polys = deathPoly_.size();
476 std::vector<std::vector<int>> vertices(N_polys);
477
478 // store coarsely vertices in each polygon
479 for(FiltratedQuadEdge const &e : rng_) {
480 vertices[e.f1].push_back(e.e.first);
481 vertices[e.f1].push_back(e.e.second);
482 vertices[e.f2].push_back(e.e.first);
483 vertices[e.f2].push_back(e.e.second);
484 }
485 std::vector<Delaunay::Face_handle> repr_face(N_polys);
486 for(auto const &fh : delaunay_.finite_face_handles()) {
487 if(UF.isRoot(fh->id()))
488 repr_face[indexPolys[fh->id()]] = fh;
489 }
490
491 std::vector<std::pair<FiltratedEdge, int>> candidates;
492 std::vector<Tree> trees(N_polys);
493
494 // loop on polygons
495 for(unsigned poly = 0; poly < N_polys; ++poly) {
496 if(deathPoly_[poly].d != inf) { // deal only with true polygons
497 const double bound_max = deathPoly_[poly].d;
498 const double bound_min = sqrt(3) / 2 * bound_max;
499
500 // find involved vertices
501 sort(vertices[poly].begin(), vertices[poly].end());
502 auto last = unique(vertices[poly].begin(), vertices[poly].end());
503 vertices[poly].erase(last, vertices[poly].end());
504
505 std::vector<Point> pts(0);
506 std::vector<int> global(0);
507 for(int const &v : vertices[poly]) {
508 pts.push_back(points_[v].first);
509 global.push_back(v);
510 }
511 const unsigned N_pts = pts.size();
512 trees[poly].insert(pts.begin(), pts.end());
513
514 // enumerate edges and do stuff
515 for(unsigned i = 0; i < N_pts - 1; ++i) {
516 for(unsigned j = i + 1; j < N_pts; ++j) {
517 const double d2 = CGAL::squared_distance(pts[i], pts[j]);
518 const double d = sqrt(d2);
519 if(bound_min <= d && d <= bound_max) // edge length interval allowing
520 // to be a candidate
521 candidates.emplace_back(
522 FiltratedEdge{{global[i], global[j]}, d}, poly);
523 }
524 }
525 }
526 }
527
528 std::vector<bool> validCandidate(candidates.size());
529#ifdef TTK_ENABLE_OPENMP
530#pragma omp parallel for
531#endif // TTK_ENABLE_OPENMP
532 for(unsigned i = 0; i < candidates.size(); ++i) {
533 const Point p_i = points_[candidates[i].first.e.first].first;
534 const Point p_j = points_[candidates[i].first.e.second].first;
535 const double d = candidates[i].first.d;
536 const int poly = candidates[i].second;
537
538 const Fuzzy_sphere fs(p_i, d);
539 std::vector<Point> ball;
540 trees[poly].search(back_inserter(ball), fs);
541
542 // check if edge is a 2-edge (i.e., both semi-lenses are not empty)
543 std::vector<Point> lens_l;
544 std::vector<Point> lens_r;
545 for(auto const &p : ball) {
546 if(p != p_i && p != p_j) {
547 if(CGAL::squared_distance(p, p_j) < d * d) {
548 if(CGAL::right_turn(p_i, p_j, p))
549 lens_r.push_back(p);
550 else
551 lens_l.push_back(p);
552 }
553 }
554 }
555
556 // if 2-edge, check if it is expandable
557 if(!lens_l.empty() && !lens_r.empty()) { // 2-edge
558 bool l_expandable = false, r_expandable = false;
559 for(Point const &p : lens_l) {
560 if(!isRightSemiLensEmpty(p_i, p, trees[poly]))
561 continue;
562 else if(isRightSemiLensEmpty(p, p_j, trees[poly])) {
563 l_expandable = true;
564 break;
565 }
566 }
567 if(!l_expandable)
568 continue;
569 for(Point const &p : lens_r) {
570 if(!isRightSemiLensEmpty(p_j, p, trees[poly]))
571 continue;
572 else if(isRightSemiLensEmpty(p, p_i, trees[poly])) {
573 r_expandable = true;
574 break;
575 }
576 }
577 if(r_expandable) { // expandable 2-edge, check if it is a diagonal of the
578 // current polygon (costly)
579 if(indexPolys[UF.find(
580 delaunay_.locate(CGAL::midpoint(p_i, p_j), repr_face[poly])->id())]
581 == int(poly))
582 validCandidate[i] = true;
583 }
584 }
585 }
586
587 deathPoly_ = std::vector<FiltratedEdge>(N_polys, {{-1, -1}, inf});
588 for(unsigned i = 0; i < candidates.size(); ++i) {
589 if(validCandidate[i]) {
590 const int poly = candidates[i].second;
591 if(candidates[i].first.d < deathPoly_[poly].d)
592 deathPoly_[poly] = candidates[i].first;
593 }
594 }
595}
596
597void FastRipsPersistenceDiagram2::exportRips1Generators(
598 std::vector<Generator1> &generators) {
599 const unsigned N_polys = deathPoly_.size();
600
601 std::vector<Generator1> generators_(N_polys);
602 for(unsigned i = 0; i < deathPoly_.size(); ++i)
603 generators_[i].second = {birthPoly_[i], deathPoly_[i].d};
604 for(const FiltratedQuadEdge &e : (N_polys == nFaces_) ? urquhart_ : rng_) {
605 if(e.f1 != e.f2) {
606 if(deathPoly_[e.f1].d != inf)
607 generators_[e.f1].first.push_back(e.e);
608 if(deathPoly_[e.f2].d != inf)
609 generators_[e.f2].first.push_back(e.e);
610 }
611 }
612
613 generators.resize(0);
614 for(Generator1 const &g : generators_) {
615 if(!g.first.empty())
616 generators.push_back(g);
617 }
618}
619
620void FastRipsPersistenceDiagram2::executePolygonPairCells(
621 bool parallel,
622 UnionFind &UF,
623 std::vector<int> const &indexPolys,
624 EdgeSets4 &ph) const {
625 std::set<Edge> cascadeSet{};
626
627 const unsigned N_polys = deathPoly_.size();
628 std::vector<std::vector<int>> vertices(N_polys);
629
630 // store coarsely vertices in each polygon
631 for(const FiltratedQuadEdge &e : rng_) {
632 vertices[e.f1].push_back(e.e.first);
633 vertices[e.f1].push_back(e.e.second);
634 vertices[e.f2].push_back(e.e.first);
635 vertices[e.f2].push_back(e.e.second);
636 }
637 std::vector<Delaunay::Face_handle> repr_face(N_polys);
638 for(auto const &fh : delaunay_.finite_face_handles()) {
639 if(UF.isRoot(fh->id()))
640 repr_face[indexPolys[fh->id()]] = fh;
641 }
642
643 // loop on polygons
644#ifdef TTK_ENABLE_OPENMP
645#pragma omp parallel for if(parallel)
646#else
647 TTK_FORCE_USE(parallel);
648#endif // TTK_ENABLE_OPENMP
649 for(unsigned poly = 0; poly < N_polys; ++poly) {
650 if(deathPoly_[poly].d != inf) { // deal only with true polygons
651 const double bound_max = deathPoly_[poly].d;
652
653 // find involved vertices
654 sort(vertices[poly].begin(), vertices[poly].end());
655 auto last = unique(vertices[poly].begin(), vertices[poly].end());
656 vertices[poly].erase(last, vertices[poly].end());
657
658 std::vector<Point> pts(0);
659 std::vector<int> global(0);
660 for(int const &v : vertices[poly]) {
661 pts.push_back(points_[v].first);
662 global.push_back(v);
663 }
664
665 PairCells pc(pts, bound_max);
666 pc.run();
667 pc.enrichCascades(cascadeSet, ph, global);
668 }
669 }
670
671 for(const Edge &e : cascadeSet)
672 ph[3].emplace_back(e);
673}
674
675#endif
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
Definition BaseClass.h:57
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
Definition OpenMP.h:46
boost::tuple< double, double > Point
Definition TopoMap.cpp:56
AtomicUF * find()
Definition FTMAtomicUF.h:38
AtomicUF * UF
Definition FTMTree_MT.h:39
FiltratedEdge max(const FiltratedEdge &a, const FiltratedEdge &b)
std::array< EdgeSet, 4 > EdgeSets4
constexpr value_t inf
std::vector< std::vector< value_t > > PointCloud
std::vector< Edge > EdgeSet
std::vector< Diagram > MultidimensionalDiagram
std::pair< Simplex, value_t > FiltratedSimplex
std::array< EdgeSet, 3 > EdgeSets3
std::vector< PersistencePair > Diagram
T end(std::pair< T, T > &p)
Definition ripser.cpp:503
T begin(std::pair< T, T > &p)
Definition ripser.cpp:499
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)