TTK
Loading...
Searching...
No Matches
MorseSmaleQuadrangulation.h
Go to the documentation of this file.
1
18
19#pragma once
20
21// base code includes
23#include <Dijkstra.h>
24#include <Geometry.h>
25#include <Triangulation.h>
26
27#include <array>
28#include <cmath>
29#include <map>
30#include <numeric>
31#include <queue>
32#include <set>
33
34namespace ttk {
35
36 class MorseSmaleQuadrangulation : virtual public Debug {
37 public:
39 this->setDebugMsgPrefix("MorseSmaleQuadrangulation");
40 }
41
42 inline void setCriticalPoints(const unsigned int number,
43 void *const points,
44 void *const ids,
45 void *const cellIds,
46 void *const type) {
47 criticalPointsNumber_ = number;
48 criticalPoints_ = static_cast<float *>(points);
49 criticalPointsIdentifier_ = static_cast<SimplexId *>(ids);
50 criticalPointsCellIds_ = static_cast<SimplexId *>(cellIds);
51 criticalPointsType_ = static_cast<unsigned char *>(type);
52 }
53
54 inline void setSeparatrices(const unsigned int number,
55 void *const cellIds,
56 void *const cellDims,
57 void *const mask,
58 void *const points) {
59 separatriceNumber_ = number;
60 sepCellIds_ = static_cast<SimplexId *>(cellIds);
61 sepCellDims_ = static_cast<unsigned char *>(cellDims);
62 sepMask_ = static_cast<unsigned char *>(mask);
63 sepPoints_ = static_cast<float *>(points);
64 }
65
66 inline void setDualQuadrangulation(const bool input) {
67 DualQuadrangulation = input;
68 }
69 inline void setShowResError(const bool value) {
70 ShowResError = value;
71 }
72 inline void
74 if(triangl != nullptr) {
78 verticesNumber_ = triangl->getNumberOfVertices();
80 }
81 }
82
83 template <typename triangulationType>
84 int execute(const triangulationType &triangulation);
85
86 private:
95 template <typename triangulationType>
96 size_t findSeparatrixMiddle(const size_t a,
97 const size_t b,
98 const triangulationType &triangulation);
99
109 int findSepsVertices(const std::vector<size_t> &seps,
110 std::vector<LongSimplexId> &srcs,
111 std::vector<LongSimplexId> &dsts) const;
112
122 template <typename triangulationType>
123 int quadrangulate(size_t &ndegen, const triangulationType &triangulation);
124
133 int dualQuadrangulate();
134
143 template <typename triangulationType>
144 int subdivise(const triangulationType &triangulation);
145
155 template <typename triangulationType>
156 int detectCellSeps(const triangulationType &triangulation);
157
164 template <typename triangulationType>
165 bool checkSurfaceCloseness(const triangulationType &triangulation) const;
166
170 void clearData();
171
175 using Quad = std::array<LongSimplexId, 4>;
176
184 template <typename triangulationType>
185 int subdiviseDegenerateQuads(std::vector<Quad> &outputSubd,
186 const triangulationType &triangulation);
187
188 // number of vertices in triangulation
189 SimplexId verticesNumber_{};
190
191 // number of critical points from the Morse-Smale complex
192 SimplexId criticalPointsNumber_{};
193 // critical points 3d coordinates
194 float *criticalPoints_{};
195 // mapping points id -> cells id
196 SimplexId *criticalPointsCellIds_{};
197 // mapping point id -> TTK identifier
198 SimplexId *criticalPointsIdentifier_{};
199 // critical point type: 0 minimum, 1 saddle point, 2 maximum
200 unsigned char *criticalPointsType_{};
201
202 // number of separatrices data
203 SimplexId separatriceNumber_{};
204 // separatrices points cellIds (to be linked to critical points cellIds)
205 SimplexId *sepCellIds_{};
206 // separatrices mask scalar field (0 for critical points, 1 otherwise)
207 unsigned char *sepMask_{};
208 // separatrices cell dimension: 0 for vertices, 1 for edges, 2 for triangles
209 unsigned char *sepCellDims_{};
210 // separatrices points
211 float *sepPoints_{};
212
213 // index of separatrices beginnings in separatrices arrays
214 std::vector<size_t> sepBegs_{};
215 // index of separatrices endings in separatrices arrays
216 std::vector<size_t> sepEnds_{};
217 // separatrices middles index in output points array
218 std::vector<SimplexId> sepMids_{};
219 // sub-segmentation of Morse-Smale cells
220 std::vector<SimplexId> morseSeg_{};
221 // morseSeg_ id -> separatrices that border quads id
222 std::vector<std::pair<SimplexId, std::vector<size_t>>> quadSeps_{};
223
224 protected:
225 // array of output quads
226 std::vector<Quad> outputCells_{};
227 // array of output vertices (generated middles of duplicated separatrices)
228 std::vector<float> outputPoints_{};
229 // array of output vertices identifiers
230 std::vector<SimplexId> outputPointsIds_{};
231 // 0: critical points, 1: edge middle, 2: quad barycenter
232 std::vector<SimplexId> outputPointsTypes_{};
233 // for critical points, their id, for sep middles, the sep id and
234 // for barycenters the parent quad id
235 std::vector<SimplexId> outputPointsCells_{};
236 // triangulation subdivision to detect cells
238
239 // if dual quadrangulation
241 // display result despite error
242 bool ShowResError{false};
243 };
244} // namespace ttk
245
246template <typename triangulationType>
247int ttk::MorseSmaleQuadrangulation::detectCellSeps(
248 const triangulationType &triangulation) {
249
250 ExplicitTriangulation newT{};
251 bs.execute(triangulation, newT);
252
253 newT.setDebugLevel(this->debugLevel_);
254 newT.setThreadNumber(this->threadNumber_);
255 newT.preconditionEdges();
256 newT.preconditionVertexNeighbors();
257 newT.preconditionVertexEdges();
258 newT.preconditionVertexTriangles();
259 newT.preconditionEdgeTriangles();
260 newT.preconditionTriangleEdges();
261
262 auto nEdges = triangulation.getNumberOfEdges();
263
264 // store separatrix index on subdivised triangulation edges
265 std::vector<SimplexId> edgeOnSep(newT.getNumberOfEdges(), -1);
266 // count the number of critical points encountered
267 size_t critPoints{0};
268 if(sepMask_[0] == 0) {
269 critPoints++;
270 }
271
272 // id of critical point in new triangulation
273 auto critPointId = [&](const SimplexId a) -> SimplexId {
274 if(sepCellDims_[a] == 0) {
275 return sepCellIds_[a];
276 }
277 if(sepCellDims_[a] == 1) {
278 return verticesNumber_ + sepCellIds_[a];
279 }
280 if(sepCellDims_[a] == 2) {
281 return verticesNumber_ + nEdges + sepCellIds_[a];
282 }
283 return -1;
284 };
285
286 // init first vertex id
287 SimplexId prev{critPointId(0)};
288
289 // put separatrices indices onto edges
290 // begin loop at one to get the first edge
291 for(SimplexId i = 1; i < separatriceNumber_; ++i) {
292
293 // for computing the separatrix id
294 if(sepMask_[i] == 0) {
295 critPoints++;
296 // beginning of a new separatrix
297 if(critPoints % 2 == 1) {
298 prev = critPointId(i);
299 continue;
300 }
301 }
302
303 // current separatrix id is critPoints // 2
304 auto currSepId
305 = (critPoints % 2 == 0) ? critPoints / 2 - 1 : critPoints / 2;
306
307 // current point
308 SimplexId const curr{critPointId(i)};
309
310 // get edge id
311 auto ne = newT.getVertexEdgeNumber(curr);
312 for(SimplexId j = 0; j < ne; ++j) {
313 SimplexId e{};
314 newT.getVertexEdge(curr, j, e);
315 SimplexId e0{}, e1{};
316 newT.getEdgeVertex(e, 0, e0);
317 newT.getEdgeVertex(e, 1, e1);
318
319 if((e0 == prev && e1 == curr) || (e1 == prev && e0 == curr)) {
320 edgeOnSep[e] = currSepId;
321 break;
322 }
323 }
324
325 prev = curr;
326 }
327
328 // if vertex is saddle in the subdivised triangulation
329 std::vector<bool> isSaddle(newT.getNumberOfVertices(), false);
330
331 for(SimplexId i = 0; i < criticalPointsNumber_; ++i) {
332 // keep only saddle points
333 if(criticalPointsType_[i] != 1) {
334 continue;
335 }
336 isSaddle[verticesNumber_ + criticalPointsCellIds_[i]] = true;
337 }
338
339 auto getTriangleEdges
340 = [&](const SimplexId tr, SimplexId &e0, SimplexId &e1, SimplexId &e2) {
341 newT.getTriangleEdge(tr, 0, e0);
342 newT.getTriangleEdge(tr, 1, e1);
343 newT.getTriangleEdge(tr, 2, e2);
344 };
345
346 // get the indices of the two separatrices around a triangle
347 // (having a saddle point as vertex)
348 auto sepIdAroundTriangle = [&](const SimplexId tr) {
349 SimplexId e0{}, e1{}, e2{};
350 getTriangleEdges(tr, e0, e1, e2);
351
352 std::set<SimplexId> sepId{};
353 if(edgeOnSep[e0] != -1 && edgeOnSep[e1] != -1) {
354 sepId.emplace(edgeOnSep[e0]);
355 sepId.emplace(edgeOnSep[e1]);
356 } else if(edgeOnSep[e1] != -1 && edgeOnSep[e2] != -1) {
357 sepId.emplace(edgeOnSep[e1]);
358 sepId.emplace(edgeOnSep[e2]);
359 } else if(edgeOnSep[e0] != -1 && edgeOnSep[e2] != -1) {
360 sepId.emplace(edgeOnSep[e0]);
361 sepId.emplace(edgeOnSep[e2]);
362 }
363 return sepId;
364 };
365
366 auto hasTriangleSaddle = [&](const SimplexId tr) {
367 SimplexId a, b, c;
368 newT.getTriangleVertex(tr, 0, a);
369 newT.getTriangleVertex(tr, 1, b);
370 newT.getTriangleVertex(tr, 2, c);
371 return (isSaddle[a] || isSaddle[b] || isSaddle[c]);
372 };
373
374 // propagate from triangles around saddle
375 std::vector<SimplexId> processed(newT.getNumberOfTriangles(), -1);
376
377 // store the saddles id
378 std::vector<SimplexId> saddles{};
379
380 for(SimplexId i = 0; i < criticalPointsNumber_; ++i) {
381 // keep only saddle points
382 if(criticalPointsType_[i] == 1) {
383 saddles.emplace_back(verticesNumber_ + criticalPointsCellIds_[i]);
384 }
385 }
386
387 // look around the saddle points
388 for(size_t i = 0; i < saddles.size(); ++i) {
389 SimplexId const saddle = saddles[i];
390
391 auto sadtri = newT.getVertexTriangleNumber(saddle);
392
393#ifdef TTK_ENABLE_OPENMP
394#pragma omp parallel for num_threads(threadNumber_)
395#endif // TTK_ENABLE_OPENMP
396 for(SimplexId j = 0; j < sadtri; ++j) {
397 std::queue<SimplexId> toProcess{};
398 SimplexId tr;
399 newT.getVertexTriangle(saddle, j, tr);
400
401 std::set<SimplexId> const sepIdBeg = sepIdAroundTriangle(tr);
402 // current iteration id
403 SimplexId const iter = i * sadtri + j;
404
405 toProcess.push(tr);
406
407 while(!toProcess.empty()) {
408 auto curr = toProcess.front();
409 toProcess.pop();
410
411 // mark current triangle, skip already processed
412#ifdef TTK_ENABLE_OPENMP
413 if(processed[curr] == -1 || processed[curr] > iter) {
414#pragma omp atomic write
415 processed[curr] = iter;
416 }
417 // skip if curr marked by thread
418 else if(processed[curr] == iter) {
419 continue;
420 }
421 // stop BFS if thread with higher iteration id has reached curr
422 else if(processed[curr] < iter) {
423 break;
424 }
425#else
426 if(processed[curr] != -1) {
427 continue;
428 }
429 processed[curr] = 0;
430#endif // TTK_ENABLE_OPENMP
431
432 // check for saddle at vertices
433 bool const hasSaddle = hasTriangleSaddle(curr);
434
435 // check for separatrices on edges
436 auto sepIdEnd = sepIdAroundTriangle(curr);
437
438 if(hasSaddle && sepIdEnd.size() == 2) {
439 // check that seps are different from beginning
440 std::vector<size_t> cellSeps{};
441 std::set_union(sepIdBeg.begin(), sepIdBeg.end(), sepIdEnd.begin(),
442 sepIdEnd.end(), std::back_inserter(cellSeps));
443 if(cellSeps.size() > 2) {
444 // found it
445#ifdef TTK_ENABLE_OPENMP
446#pragma omp critical
447#endif // TTK_ENABLE_OPENMP
448 {
449 // keep indices in sync
450 quadSeps_.emplace_back(iter, cellSeps);
451 }
452 }
453 }
454
455 // mark vertices from the original mesh with cell id
456 for(SimplexId k = 0; k < 3; ++k) {
457 SimplexId vert{};
458 newT.getTriangleVertex(curr, k, vert);
459 if(vert >= verticesNumber_) {
460 continue;
461 }
462 bool vertOnSep = false;
463 for(SimplexId l = 0; l < newT.getVertexEdgeNumber(vert); ++l) {
464 SimplexId e{};
465 newT.getVertexEdge(vert, l, e);
466 if(edgeOnSep[e] != -1) {
467 vertOnSep = true;
468 break;
469 }
470 }
471 if(!vertOnSep) {
472#ifdef TTK_ENABLE_OPENMP
473#pragma omp atomic write
474#endif // TTK_ENABLE_OPENMP
475 morseSeg_[vert] = iter;
476 }
477 }
478
479 // look for neighboring triangles
480 std::array<SimplexId, 3> edges{};
481 getTriangleEdges(curr, edges[0], edges[1], edges[2]);
482 for(const auto e : edges) {
483 // do not cross separatrices
484 if(edgeOnSep[e] != -1) {
485 continue;
486 }
487 for(SimplexId k = 0; k < newT.getEdgeTriangleNumber(e); ++k) {
488 SimplexId neigh{};
489 newT.getEdgeTriangle(e, k, neigh);
490 // push only non processed triangles
491 if(processed[neigh] == -1) {
492 toProcess.push(neigh);
493 }
494#ifdef TTK_ENABLE_OPENMP
495 // push if neigh marked during a previous iteration
496 else if(processed[neigh] > iter) {
497 toProcess.push(neigh);
498 }
499 // stop pushing neighbors if curr marked in a newer iteration
500 else if(processed[neigh] < iter) {
501 break;
502 }
503#endif // TTK_ENABLE_OPENMP
504 }
505 }
506 }
507 }
508 }
509
510 if(this->threadNumber_ > 1) {
511 // sort quadSeps_ according to cellIds_ to get a deterministic
512 // output when filled in parallel
513 TTK_PSORT(this->threadNumber_, quadSeps_.begin(), quadSeps_.end());
514 // (by default, pairs are sorted by their first element)
515 }
516
517 return 0;
518}
519
520template <typename triangulationType>
521int ttk::MorseSmaleQuadrangulation::quadrangulate(
522 size_t &ndegen, const triangulationType &triangulation) {
523 // quadrangle vertices are either extrema or saddle points
524
525 // separatrices bounds indices and cell ids
526 std::vector<size_t> sepFlatEdges{};
527
528 for(SimplexId i = 0; i < separatriceNumber_; ++i) {
529 if(sepMask_[i] == 1) {
530 continue;
531 }
532 sepFlatEdges.emplace_back(i);
533 }
534
535 // number of separatrices
536 auto numSeps = sepFlatEdges.size() / 2;
537
538 // clear data members
539 sepBegs_.resize(numSeps);
540 sepEnds_.resize(numSeps);
541 morseSeg_.resize(verticesNumber_);
542 std::fill(morseSeg_.begin(), morseSeg_.end(), -1);
543 quadSeps_.clear();
544
545 // fill in data arrays
546 for(size_t i = 0; i < numSeps; ++i) {
547 // separatrices bounds
548 sepBegs_[i] = sepFlatEdges[2 * i];
549 sepEnds_[i] = sepFlatEdges[2 * i + 1];
550 }
551
552 detectCellSeps(triangulation);
553
554 outputCells_.reserve(quadSeps_.size());
555
556 for(const auto &qsp : quadSeps_) {
557
558 const auto &qs{qsp.second};
559
560 std::vector<LongSimplexId> srcs{};
561 std::vector<LongSimplexId> dsts{};
562
563 findSepsVertices(qs, srcs, dsts);
564
565 // remove duplicates
566 std::set<LongSimplexId> const srcs_set(srcs.begin(), srcs.end());
567 std::set<LongSimplexId> const dsts_set(dsts.begin(), dsts.end());
568 srcs.assign(srcs_set.begin(), srcs_set.end());
569 dsts.assign(dsts_set.begin(), dsts_set.end());
570
571 // filter out separatrices whose sources are not in contact with
572 // the current cell
573
574 bool found = true;
575
576 if(dsts.size() != 2) {
577 found = false;
578 }
579
580 if(srcs.size() == 2) {
581 outputCells_.emplace_back(Quad{dsts[0], srcs[0], dsts[1], srcs[1]});
582 } else if(srcs.size() == 1) {
583 outputCells_.emplace_back(Quad{dsts[0], srcs[0], dsts[1], srcs[0]});
584 ndegen++;
585 } else {
586 found = false;
587 }
588
589 if(!found) {
590 this->printMsg("Missing quadrangle", ttk::debug::Priority::DETAIL);
591 }
592 }
593
594 return 0;
595}
596
597template <typename triangulationType>
598size_t ttk::MorseSmaleQuadrangulation::findSeparatrixMiddle(
599 const size_t a, const size_t b, const triangulationType &triangulation) {
600
601 const int dim = 3;
602
603 std::vector<float> distFromA(b - a + 1);
604 std::array<float, dim> prev{}, curr{};
605
606 if(distFromA.empty()) {
607 return 0;
608 }
609
610 curr[0] = sepPoints_[dim * a];
611 curr[1] = sepPoints_[dim * a + 1];
612 curr[2] = sepPoints_[dim * a + 2];
613
614 // integrate distances at every point of this separatrix
615 for(size_t i = 1; i < b - a + 1; ++i) {
616 std::swap(curr, prev);
617 curr[0] = sepPoints_[dim * (a + i)];
618 curr[1] = sepPoints_[dim * (a + i) + 1];
619 curr[2] = sepPoints_[dim * (a + i) + 2];
620 distFromA[i]
621 = distFromA[i - 1] + ttk::Geometry::distance(&curr[0], &prev[0]);
622 }
623
624 auto distAB = distFromA.back();
625 for(auto &el : distFromA) {
626 el = std::abs(el - distAB / 2.0);
627 }
628
629 // index in separatrices point data array of separatrix middle
630 auto pos = a
631 + (std::min_element(distFromA.begin(), distFromA.end())
632 - distFromA.begin());
633
634 // new point!
635 outputPoints_.emplace_back(sepPoints_[dim * pos]);
636 outputPoints_.emplace_back(sepPoints_[dim * pos + 1]);
637 outputPoints_.emplace_back(sepPoints_[dim * pos + 2]);
638
639 SimplexId id = pos;
640
641 // new point identifier (on the triangular mesh)
642 switch(sepCellDims_[pos]) {
643 case 0:
644 outputPointsIds_.emplace_back(sepCellIds_[pos]);
645 break;
646 case 1: {
647 // take the first vertex of the edge
648 triangulation.getEdgeVertex(sepCellIds_[pos], 0, id);
649 outputPointsIds_.emplace_back(id);
650 break;
651 }
652 case 2: {
653 // take the first vertex of the triangle
654 triangulation.getTriangleVertex(sepCellIds_[pos], 0, id);
655 outputPointsIds_.emplace_back(id);
656 break;
657 }
658 default:
659 break;
660 }
661
662 outputPointsTypes_.emplace_back(1);
663
664 return id;
665}
666
667template <typename triangulationType>
668int ttk::MorseSmaleQuadrangulation::subdiviseDegenerateQuads(
669 std::vector<Quad> &outputSubd, const triangulationType &triangulation) {
670
671 for(size_t i = 0; i < outputCells_.size(); ++i) {
672 auto q = outputCells_[i];
673 auto seps = quadSeps_[i].second;
674
675 // don't deal with normal quadrangles
676 if(q[1] != q[3]) {
677 continue;
678 }
679
680 std::vector<LongSimplexId> srcs{};
681 std::vector<LongSimplexId> dsts{};
682
683 findSepsVertices(seps, srcs, dsts);
684
685 // identify the extremum that is twice dest
686 int count_vi = 0, count_vk = 0;
687 for(const auto &s : dsts) {
688 if(s == q[0]) {
689 count_vi++;
690 }
691 if(s == q[2]) {
692 count_vk++;
693 }
694 }
695 // extremum index
696 LongSimplexId const vert2Seps = count_vi > count_vk ? q[0] : q[2];
697 LongSimplexId const vert1Sep = count_vi > count_vk ? q[2] : q[0];
698 // the two seps from j to vert2Seps
699 std::vector<size_t> borderseps{};
700 for(size_t j = 0; j < seps.size(); ++j) {
701 if(dsts[j] == vert2Seps && srcs[j] == q[1]) {
702 borderseps.emplace_back(seps[j]);
703 }
704 }
705
706 if(borderseps.size() < 2) {
707 continue;
708 }
709
710 // find a midpoint between the two extrema on the triangulation
711
712 std::vector<SimplexId> const boundi{criticalPointsIdentifier_[q[0]]};
713 std::vector<SimplexId> const boundk{criticalPointsIdentifier_[q[2]]};
714 std::array<std::vector<float>, 6> outputDists{};
715
717 criticalPointsIdentifier_[q[0]], triangulation, outputDists[0], boundk);
719 criticalPointsIdentifier_[q[2]], triangulation, outputDists[1], boundi);
720
721 auto inf = std::numeric_limits<float>::infinity();
722 std::vector<float> sum(outputDists[0].size(), inf);
723
724 for(size_t j = 0; j < sum.size(); ++j) {
725 auto m = outputDists[0][j];
726 auto n = outputDists[1][j];
727 if(m == inf || n == inf) {
728 continue;
729 }
730 // cost to minimize
731 sum[j] = m + n + std::abs(m - n);
732 }
733
734 auto insertNewPoint
735 = [&](const SimplexId a, const size_t idx, const SimplexId type) {
736 float x, y, z;
737 triangulation.getVertexPoint(a, x, y, z);
738 outputPoints_.emplace_back(x);
739 outputPoints_.emplace_back(y);
740 outputPoints_.emplace_back(z);
741 outputPointsIds_.emplace_back(a);
742 outputPointsTypes_.emplace_back(type);
743 outputPointsCells_.emplace_back(idx);
744 return outputPointsIds_.size() - 1;
745 };
746
747 auto v0 = std::min_element(sum.begin(), sum.end()) - sum.begin();
748 auto v0Pos = static_cast<LongSimplexId>(insertNewPoint(v0, i, 3));
749
750 // find two other points
751
752 std::vector<SimplexId> const bounds{criticalPointsIdentifier_[q[0]],
753 criticalPointsIdentifier_[q[1]],
754 criticalPointsIdentifier_[q[2]]};
755
756 auto m0Pos = sepMids_[borderseps[0]];
757 auto m1Pos = sepMids_[borderseps[1]];
758 auto m0 = outputPointsIds_[m0Pos];
759 auto m1 = outputPointsIds_[m1Pos];
760
761 Dijkstra::shortestPath(criticalPointsIdentifier_[vert1Sep], triangulation,
762 outputDists[2], bounds);
763 Dijkstra::shortestPath(v0, triangulation, outputDists[3], bounds);
764 Dijkstra::shortestPath(m0, triangulation, outputDists[4], bounds);
765 Dijkstra::shortestPath(m1, triangulation, outputDists[5], bounds);
766
767 std::fill(sum.begin(), sum.end(), inf);
768
769 for(size_t j = 0; j < sum.size(); ++j) {
770 auto m = outputDists[2][j];
771 auto n = outputDists[3][j];
772 auto o = outputDists[4][j];
773 if(m == inf || n == inf || o == inf) {
774 continue;
775 }
776 // cost to minimize
777 sum[j] = m + n + o + std::abs(m - n) + std::abs(m - o) + std::abs(n - o);
778 }
779
780 auto v1 = std::min_element(sum.begin(), sum.end()) - sum.begin();
781 auto v1Pos = static_cast<LongSimplexId>(insertNewPoint(v1, i, 4));
782 std::fill(sum.begin(), sum.end(), inf);
783
784 for(size_t j = 0; j < sum.size(); ++j) {
785 auto m = outputDists[2][j];
786 auto n = outputDists[3][j];
787 auto o = outputDists[5][j];
788 if(m == inf || n == inf || o == inf) {
789 continue;
790 }
791 // cost to minimize
792 sum[j] = m + n + o + std::abs(m - n) + std::abs(m - o) + std::abs(n - o);
793 }
794
795 auto v2 = std::min_element(sum.begin(), sum.end()) - sum.begin();
796 auto v2Pos = static_cast<LongSimplexId>(insertNewPoint(v2, i, 4));
797
798 outputSubd.emplace_back(Quad{vert2Seps, m0Pos, v1Pos, v0Pos});
799 outputSubd.emplace_back(Quad{vert2Seps, m1Pos, v2Pos, v0Pos});
800 outputSubd.emplace_back(Quad{q[1], m0Pos, v1Pos, vert1Sep});
801 outputSubd.emplace_back(Quad{q[1], m1Pos, v2Pos, vert1Sep});
802 outputSubd.emplace_back(Quad{vert1Sep, v1Pos, v0Pos, v2Pos});
803 }
804 return 0;
805}
806
807template <typename triangulationType>
808int ttk::MorseSmaleQuadrangulation::subdivise(
809 const triangulationType &triangulation) {
810
811 // separatrices middles index in output points array
812 sepMids_.resize(sepBegs_.size());
813
814 for(size_t i = 0; i < sepMids_.size(); ++i) {
815 // separatrices middles
816 sepMids_[i] = outputPoints_.size() / 3; // before insertion at next line
817 findSeparatrixMiddle(sepBegs_[i], sepEnds_[i], triangulation);
818 outputPointsCells_.emplace_back(i);
819 }
820
821 // for each output quad, its barycenter position in outputPoints_
822 std::vector<size_t> const cellBary(outputCells_.size());
823
824 std::array<std::vector<float>, 4> outputDists{};
825
826 // hold quad subdivision
827 decltype(outputCells_) outputSubd{};
828 outputSubd.reserve(4 * outputCells_.size());
829
830 for(size_t i = 0; i < outputCells_.size(); ++i) {
831 auto q = outputCells_[i];
832 auto seps = quadSeps_[i].second;
833
834 // skip degenerate case here
835 if(q[1] == q[3]) {
836 continue;
837 }
838
839 std::vector<LongSimplexId> sepMids(seps.size());
840 std::vector<SimplexId> midsNearestVertex(seps.size());
841 for(size_t j = 0; j < seps.size(); ++j) {
842 sepMids[j] = sepMids_[seps[j]];
843 midsNearestVertex[j] = outputPointsIds_[sepMids_[seps[j]]];
844 }
845
846 // find barycenter of current cell (c.f. QuadrangulationSubdivision.cpp)
847
848 // bound Dijkstra by parent quad vertices
849 std::vector<SimplexId> const bounds{
850 criticalPointsIdentifier_[q[0]], criticalPointsIdentifier_[q[1]],
851 criticalPointsIdentifier_[q[2]], criticalPointsIdentifier_[q[3]]};
852
853 // Dijkstra propagation mask
854 std::vector<bool> mask(morseSeg_.size(), false);
855 // restrict Dijkstra propagation to current cell
856 for(size_t j = 0; j < morseSeg_.size(); ++j) {
857 if(morseSeg_[j] == quadSeps_[i].first) {
858 mask[j] = true;
859 }
860 }
861 // also allow to propagate on separatrices
862 for(const auto s : seps) {
863 for(size_t j = sepBegs_[s]; j <= sepEnds_[s]; ++j) {
864 if(sepCellDims_[j] == 1) {
865 auto e = sepCellIds_[j];
866 SimplexId e0{}, e1{};
867 triangulation.getEdgeVertex(e, 0, e0);
868 triangulation.getEdgeVertex(e, 1, e1);
869 mask[e0] = true;
870 mask[e1] = true;
871 }
872 }
873 }
874
875#ifdef TTK_ENABLE_OPENMP
876#pragma omp parallel for num_threads(threadNumber_)
877#endif // TTK_ENABLE_OPENMP
878 for(size_t j = 0; j < outputDists.size(); ++j) {
879 Dijkstra::shortestPath(midsNearestVertex[j], triangulation,
880 outputDists.at(j), std::vector<SimplexId>(), mask);
881 }
882
883 auto inf = std::numeric_limits<float>::infinity();
884 std::vector<float> sum(outputDists[0].size(), inf);
885
886#ifdef TTK_ENABLE_OPENMP
887#pragma omp parallel for num_threads(threadNumber_)
888#endif // TTK_ENABLE_OPENMP
889 for(size_t j = 0; j < sum.size(); ++j) {
890 // skip if vertex j not in cell i
891 if(morseSeg_[j] != quadSeps_[i].first) {
892 continue;
893 }
894 auto m = outputDists[0][j];
895 auto n = outputDists[1][j];
896 auto o = outputDists[2][j];
897 auto p = outputDists[3][j];
898 if(m == inf || n == inf || o == inf || p == inf) {
899 continue;
900 }
901 // cost to minimize
902 sum[j] = m + n + o + p + std::abs(m - o) + std::abs(n - p);
903 }
904
905 size_t const verticesInCell
906 = verticesNumber_
907 - std::count(
908 sum.begin(), sum.end(), std::numeric_limits<float>::infinity());
909
910 const size_t thresholdVertsInCell{50};
911 if(verticesInCell <= thresholdVertsInCell) {
912 this->printMsg("Small cell detected");
913 }
914
915 SimplexId baryId{};
916 if(verticesInCell == 0) {
917 this->printMsg("Barycenter of cell " + std::to_string(i) + " not found");
918 // snap bary on sepMids[0]
919 baryId = outputPointsIds_[sepMids[0]];
920 } else {
921 baryId = std::min_element(sum.begin(), sum.end()) - sum.begin();
922 }
923
924 LongSimplexId const baryPos = outputPointsIds_.size();
925 {
926 float x, y, z;
927 triangulation.getVertexPoint(baryId, x, y, z);
928 outputPoints_.emplace_back(x);
929 outputPoints_.emplace_back(y);
930 outputPoints_.emplace_back(z);
931 outputPointsIds_.emplace_back(baryId);
932 outputPointsTypes_.emplace_back(2);
933 outputPointsCells_.emplace_back(i);
934 }
935
936 std::vector<LongSimplexId> srcs{};
937 std::vector<LongSimplexId> dsts{};
938
939 findSepsVertices(seps, srcs, dsts);
940
941 auto sepsQuadVertex = [&](const size_t a, const size_t b) {
942 if(srcs[a] == srcs[b]) {
943 outputSubd.emplace_back(Quad{srcs[a], sepMids[a], baryPos, sepMids[b]});
944 }
945 if(dsts[a] == dsts[b]) {
946 outputSubd.emplace_back(Quad{dsts[a], sepMids[a], baryPos, sepMids[b]});
947 }
948 };
949
950 // test every pair of current quad seps for a common vertex, if yes, use
951 // their middles and the quad barycenter
952 sepsQuadVertex(0, 1);
953 sepsQuadVertex(0, 2);
954 sepsQuadVertex(0, 3);
955 sepsQuadVertex(1, 2);
956 sepsQuadVertex(1, 3);
957 sepsQuadVertex(2, 3);
958 }
959
960 subdiviseDegenerateQuads(outputSubd, triangulation);
961
962 // overwrite old quads
963 outputCells_ = std::move(outputSubd);
964
965 return 0;
966}
967
968template <typename triangulationType>
969bool ttk::MorseSmaleQuadrangulation::checkSurfaceCloseness(
970 const triangulationType &triangulation) const {
971
972 bool triangulationClosed{true};
973 // sweep over all vertices to check if one is on a boundary
974 for(SimplexId i = 0; i < verticesNumber_; ++i) {
975 if(triangulation.isVertexOnBoundary(i)) {
976 triangulationClosed = false;
977 break;
978 }
979 }
980 // quadrangles edges -> quadrangles
981 std::map<std::pair<LongSimplexId, LongSimplexId>, std::set<size_t>>
982 quadEdges{};
983
984 // sweep over quadrangulation edges
985 for(size_t i = 0; i < outputCells_.size(); ++i) {
986 auto q = outputCells_[i];
987 // store edges in order
988 if(q[0] < q[1]) {
989 quadEdges[std::make_pair(q[0], q[1])].emplace(i);
990 } else {
991 quadEdges[std::make_pair(q[1], q[0])].emplace(i);
992 }
993 if(q[1] < q[2]) {
994 quadEdges[std::make_pair(q[1], q[2])].emplace(i);
995 } else {
996 quadEdges[std::make_pair(q[2], q[1])].emplace(i);
997 }
998 if(q[2] < q[3]) {
999 quadEdges[std::make_pair(q[2], q[3])].emplace(i);
1000 } else {
1001 quadEdges[std::make_pair(q[3], q[2])].emplace(i);
1002 }
1003 if(q[3] < q[0]) {
1004 quadEdges[std::make_pair(q[3], q[0])].emplace(i);
1005 } else {
1006 quadEdges[std::make_pair(q[0], q[3])].emplace(i);
1007 }
1008 }
1009
1010 bool quadrangulationClosed{true};
1011 for(const auto &e : quadEdges) {
1012 if(e.second.size() < 2) {
1013 quadrangulationClosed = false;
1014 break;
1015 }
1016 }
1017
1018 // each edge should be shared by two different quadrangles
1019 return triangulationClosed == quadrangulationClosed;
1020}
1021
1022// main routine
1023template <typename triangulationType>
1025 const triangulationType &triangulation) {
1026
1027 Timer tm;
1028
1029 // sanity check
1030 if(separatriceNumber_ == 0) {
1031 this->printErr("Unable to perform quadrangulation without separatrices");
1032 return 1;
1033 }
1034
1035 // clear output
1036 clearData();
1037 outputPoints_.resize(3 * criticalPointsNumber_);
1038 outputPointsIds_.resize(criticalPointsNumber_);
1039 outputPointsTypes_.resize(criticalPointsNumber_);
1040 outputPointsCells_.resize(criticalPointsNumber_);
1041
1042 // fill in critical points 3d coordinates and identifiers
1043 for(SimplexId i = 0; i < criticalPointsNumber_; ++i) {
1044 outputPoints_[3 * i] = criticalPoints_[3 * i];
1045 outputPoints_[3 * i + 1] = criticalPoints_[3 * i + 1];
1046 outputPoints_[3 * i + 2] = criticalPoints_[3 * i + 2];
1047 outputPointsIds_[i] = criticalPointsIdentifier_[i];
1048 outputPointsTypes_[i] = 0;
1049 outputPointsCells_[i] = i;
1050 }
1051
1052 // number of degenerate quadrangles
1053 size_t ndegen = 0;
1054
1055 // direct quadrangulation with saddle points
1056 int const ret = quadrangulate(ndegen, triangulation);
1057
1058 if(ret == 0) {
1059 subdivise(triangulation);
1060 } else {
1061 // clean, log & early return
1062 clearData();
1063 this->printErr("Unable to generate a quadrangulation from the given "
1064 "Morse-Smale complex");
1065 return 1;
1066 }
1067
1068 if(DualQuadrangulation) {
1069 dualQuadrangulate();
1070 }
1071
1072 if(!checkSurfaceCloseness(triangulation)) {
1073 // log, clean & early return
1074 this->printErr("Output surface does not match input surface closeness");
1075 if(!ShowResError) {
1076 clearData();
1077 return 1;
1078 }
1079 }
1080
1081 std::string const s_degen{
1082 ndegen > 0 ? "(" + std::to_string(ndegen) + " degenerated) " : ""};
1083
1084 this->printMsg("Produced " + std::to_string(this->outputCells_.size())
1085 + " quads " + s_degen + "("
1086 + std::to_string(this->outputPointsIds_.size()) + " points)",
1087 1.0, tm.getElapsedTime(), this->threadNumber_);
1088
1089 return 0;
1090}
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
Definition OpenMP.h:46
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
virtual SimplexId getNumberOfVertices() const
Subdivise a triangulation according to triangle barycenter.
void preconditionTriangulation(AbstractTriangulation *const triangulation)
int execute(const triangulationType &inputTriangl, ExplicitTriangulation &outputTriangl)
Minimalist debugging class.
Definition Debug.h:88
int debugLevel_
Definition Debug.h:379
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
TTK processing package for the topological simplification of scalar data.
std::vector< SimplexId > outputPointsCells_
void setDualQuadrangulation(const bool input)
std::vector< SimplexId > outputPointsTypes_
void setSeparatrices(const unsigned int number, void *const cellIds, void *const cellDims, void *const mask, void *const points)
void preconditionTriangulation(AbstractTriangulation *const triangl)
void setCriticalPoints(const unsigned int number, void *const points, void *const ids, void *const cellIds, void *const type)
int execute(const triangulationType &triangulation)
double getElapsedTime()
Definition Timer.h:15
std::string to_string(__int128)
Definition ripserpy.cpp:99
int shortestPath(const SimplexId source, const triangulationType &triangulation, std::vector< T > &outputDists, const std::vector< SimplexId > &bounds=std::vector< SimplexId >(), const std::vector< bool > &mask=std::vector< bool >())
Compute the Dijkstra shortest path from source.
Definition Dijkstra.h:26
T distance(const T *p0, const T *p1, const int &dimension=3)
Definition Geometry.cpp:362
The Topology ToolKit.
long long int LongSimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:15
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)