78 template <
class triangulationType = AbstractTriangulation>
80 const triangulationType *triangulation);
82 template <
class triangulationType = AbstractTriangulation>
84 const triangulationType *triangulation);
86 template <
class triangulationType = AbstractTriangulation>
88 const triangulationType *triangulation);
90 template <
class triangulationType>
93 template <
class triangulationType = AbstractTriangulation>
97 const triangulationType *triangulation,
98 bool &isLowerOnBoundary,
99 bool &isUpperOnBoundary,
100 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
101 std::vector<std::vector<ttk::SimplexId>> *lowerComponents)
const;
103 template <
class triangulationType = AbstractTriangulation>
107 const triangulationType *triangulation,
108 std::vector<std::vector<ttk::SimplexId>> *upperComponents
110 std::vector<std::vector<ttk::SimplexId>> *lowerComponents
115 const std::vector<std::pair<SimplexId, SimplexId>>
116 &vertexLinkEdgeList)
const;
123 setOutput(std::vector<std::pair<SimplexId, char>> *criticalPoints) {
141 const std::vector<std::vector<std::pair<SimplexId, SimplexId>>>
163 const std::vector<std::vector<std::pair<SimplexId, SimplexId>>>
226 const SimplexId *
const offsets,
const triangulationType *triangulation) {
229#ifndef TTK_ENABLE_KAMIKAZE
230 if((!dimension_) && ((!triangulation) || (triangulation->isEmpty())))
232 if((!vertexNumber_) && ((!triangulation) || (triangulation->isEmpty())))
234 if((!vertexLinkEdgeLists_) && (!triangulation))
241 vertexNumber_ = triangulation->getNumberOfVertices();
242 dimension_ = triangulation->getCellVertexNumber(0) - 1;
245 printMsg(
"Extracting critical points...");
251#ifdef TTK_ENABLE_OPENMP
253 = std::max(1000, (
int)vertexNumber_ / (threadNumber_ * 100));
258#ifdef TTK_ENABLE_OPENMP
259#pragma omp parallel for schedule(dynamic, chunkSize) num_threads(threadNumber_)
263 if(!isRunningWithMPI()
264 || (isRunningWithMPI()
268 vertexTypes[i] = getCriticalType(i, offsets, triangulation);
271 }
else if(vertexLinkEdgeLists_) {
273#ifdef TTK_ENABLE_OPENMP
274#pragma omp parallel for schedule(dynamic, chunkSize) num_threads(threadNumber_)
278 vertexTypes[i] = getCriticalType(i, offsets, (*vertexLinkEdgeLists_)[i]);
282 SimplexId minimumNumber = 0, maximumNumber = 0, saddleNumber = 0,
283 oneSaddleNumber = 0, twoSaddleNumber = 0, monkeySaddleNumber = 0;
287 if(dimension_ == 3) {
288 for(
SimplexId i = 0; i < vertexNumber_; i++) {
289 switch(vertexTypes[i]) {
308 monkeySaddleNumber++;
312 }
else if(dimension_ == 2) {
313 for(
SimplexId i = 0; i < vertexNumber_; i++) {
314 switch(vertexTypes[i]) {
329 monkeySaddleNumber++;
336 std::vector<std::vector<std::string>> stats;
337 stats.push_back({
" #Minima", std::to_string(minimumNumber)});
338 if(dimension_ == 3) {
339 stats.push_back({
" #1-saddles", std::to_string(oneSaddleNumber)});
340 stats.push_back({
" #2-saddles", std::to_string(twoSaddleNumber)});
342 if(dimension_ == 2) {
343 stats.push_back({
" #Saddles", std::to_string(saddleNumber)});
345 stats.push_back({
" #Multi-saddles", std::to_string(monkeySaddleNumber)});
346 stats.push_back({
" #Maxima", std::to_string(maximumNumber)});
353 criticalPoints_->clear();
354 criticalPoints_->reserve(vertexNumber_);
355 for(
SimplexId i = 0; i < vertexNumber_; i++) {
357 criticalPoints_->emplace_back(i, vertexTypes[i]);
361 printMsg(
"Processed " + std::to_string(vertexNumber_) +
" vertices", 1,
371 const triangulationType *triangulation,
372 bool &isLowerOnBoundary,
373 bool &isUpperOnBoundary,
374 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
375 std::vector<std::vector<ttk::SimplexId>> *lowerComponents)
const {
378 = triangulation->getVertexNeighborNumber(vertexId);
379 std::vector<SimplexId> lowerNeighbors, upperNeighbors;
381 for(
SimplexId i = 0; i < neighborNumber; i++) {
383 triangulation->getVertexNeighbor(vertexId, i, neighborId);
385 if(offsets[neighborId] < offsets[vertexId]) {
386 lowerNeighbors.push_back(neighborId);
387 if(dimension_ == 3) {
388 if(triangulation->isVertexOnBoundary(neighborId))
389 isLowerOnBoundary =
true;
394 if(offsets[neighborId] > offsets[vertexId]) {
395 upperNeighbors.push_back(neighborId);
396 if(dimension_ == 3) {
397 if(triangulation->isVertexOnBoundary(neighborId))
398 isUpperOnBoundary =
true;
404 std::vector<UnionFind> lowerSeeds(lowerNeighbors.size());
405 std::vector<UnionFind *> lowerList(lowerNeighbors.size());
406 std::vector<UnionFind> upperSeeds(upperNeighbors.size());
407 std::vector<UnionFind *> upperList(upperNeighbors.size());
410 lowerList[i] = &(lowerSeeds[i]);
413 upperList[i] = &(upperSeeds[i]);
416 SimplexId const vertexStarSize = triangulation->getVertexStarNumber(vertexId);
418 for(
SimplexId i = 0; i < vertexStarSize; i++) {
420 triangulation->getVertexStar(vertexId, i, cellId);
422 SimplexId const cellSize = triangulation->getCellVertexNumber(cellId);
423 for(
SimplexId j = 0; j < cellSize; j++) {
425 triangulation->getCellVertex(cellId, j, neighborId0);
427 if(neighborId0 != vertexId) {
430 bool const lower0 = offsets[neighborId0] < offsets[vertexId];
433 for(
SimplexId k = j + 1; k < cellSize; k++) {
436 triangulation->getCellVertex(cellId, k, neighborId1);
438 if((neighborId1 != neighborId0) && (neighborId1 != vertexId)) {
440 bool const lower1 = offsets[neighborId1] < offsets[vertexId];
442 std::vector<SimplexId> *neighbors = &lowerNeighbors;
443 std::vector<UnionFind *> *seeds = &lowerList;
446 neighbors = &upperNeighbors;
450 if(lower0 == lower1) {
454 if((*neighbors)[l] == neighborId0) {
457 if((*neighbors)[l] == neighborId1) {
461 if((lowerId0 != -1) && (lowerId1 != -1)) {
463 (*seeds)[lowerId0], (*seeds)[lowerId1]);
464 (*seeds)[lowerId1] = (*seeds)[lowerId0];
477 lowerList[i] = lowerList[i]->find();
479 upperList[i] = upperList[i]->find();
481 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>::iterator it;
482 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>
484 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>
489 it = upperComponentId.find(upperList[i]);
490 if(it != upperComponentId.end()) {
491 upperComponentId[upperList[i]].push_back(upperNeighbors[i]);
493 upperComponentId[upperList[i]]
494 = std::vector<ttk::SimplexId>(1, upperNeighbors[i]);
497 for(
auto elt : upperComponentId) {
498 upperComponents->push_back(std::vector<ttk::SimplexId>());
500 upperComponents->back().push_back(elt.second.at(i));
505 it = lowerComponentId.find(lowerList[i]);
506 if(it != lowerComponentId.end()) {
507 lowerComponentId[lowerList[i]].push_back(lowerNeighbors[i]);
509 lowerComponentId[lowerList[i]]
510 = std::vector<ttk::SimplexId>(1, lowerNeighbors[i]);
513 for(
auto elt : lowerComponentId) {
514 lowerComponents->push_back(std::vector<ttk::SimplexId>());
516 lowerComponents->back().push_back(elt.second.at(i));
521 printMsg(
"Vertex #" + std::to_string(vertexId)
522 +
": lowerLink-#CC=" + std::to_string(lowerComponentId.size())
523 +
" upperLink-#CC=" + std::to_string(upperComponentId.size()),
534 const triangulationType *triangulation,
535 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
536 std::vector<std::vector<ttk::SimplexId>> *lowerComponents)
const {
538 bool isLowerOnBoundary =
false, isUpperOnBoundary =
false;
539 std::vector<std::vector<ttk::SimplexId>> localUpperComponents;
540 std::vector<std::vector<ttk::SimplexId>> localLowerComponents;
541 if(upperComponents ==
nullptr) {
542 upperComponents = &localUpperComponents;
544 if(lowerComponents ==
nullptr) {
545 lowerComponents = &localLowerComponents;
547 getLowerUpperComponents(vertexId, offsets, triangulation, isLowerOnBoundary,
548 isUpperOnBoundary, upperComponents, lowerComponents);
549 ttk::SimplexId const lowerComponentNumber = lowerComponents->size();
550 ttk::SimplexId const upperComponentNumber = upperComponents->size();
552 if(dimension_ == 1) {
553 if(lowerComponentNumber == 0 && upperComponentNumber != 0) {
555 }
else if(lowerComponentNumber != 0 && upperComponentNumber == 0) {
557 }
else if(lowerComponentNumber == 1 && upperComponentNumber == 1) {
563 if(lowerComponentNumber == 0 && upperComponentNumber == 1) {
565 }
else if(lowerComponentNumber == 1 && upperComponentNumber == 0) {
567 }
else if(lowerComponentNumber == 1 && upperComponentNumber == 1) {
569 if((dimension_ == 3) && (triangulation->isVertexOnBoundary(vertexId))) {
571 if((isUpperOnBoundary) && (!isLowerOnBoundary))
573 if((!isUpperOnBoundary) && (isLowerOnBoundary))
581 if(dimension_ == 2 || dimension_ == 1) {
582 if((lowerComponentNumber == 2 && upperComponentNumber == 1)
583 || (lowerComponentNumber == 1 && upperComponentNumber == 2)
584 || (lowerComponentNumber == 2 && upperComponentNumber == 2)) {
595 }
else if(dimension_ == 3) {
596 if(lowerComponentNumber == 2 && upperComponentNumber == 1) {
598 }
else if(lowerComponentNumber == 1 && upperComponentNumber == 2) {