96 std::vector<std::array<float, 3>>
points_{};
167 template <
typename dataType,
typename triangulationType>
172 const dataType *
const scalars,
173 const size_t scalarsMTime,
175 const triangulationType &triangulation);
189 const bool doDescending,
190 const bool doSaddleConnectors) {
200 const bool doDescending) {
209 const bool doDescending,
210 const bool doMorseSmale) {
267 template <
typename triangulationType>
270 std::vector<Separatrix> &separatrices,
271 const triangulationType &triangulation)
const;
277 template <
typename triangulationType>
279 std::vector<Separatrix> &separatrices,
280 const triangulationType &triangulation)
const;
286 template <
typename triangulationType>
288 std::vector<Separatrix> &separatrices,
289 const triangulationType &triangulation)
const;
294 template <
typename triangulationType>
296 const std::vector<Separatrix> &separatrices,
298 const triangulationType &triangulation)
const;
304 template <
typename triangulationType>
306 const std::vector<SimplexId> &saddles2,
307 std::vector<Separatrix> &separatrices,
308 std::vector<std::vector<SimplexId>> &separatricesSaddles,
309 const triangulationType &triangulation)
const;
315 template <
typename triangulationType>
318 const std::vector<Separatrix> &separatrices,
319 const std::vector<std::vector<SimplexId>> &separatricesSaddles,
321 const triangulationType &triangulation)
const;
328 template <
typename triangulationType>
331 const size_t polSize,
332 const triangulationType &triangulation)
const;
337 template <
typename triangulationType>
339 const size_t polSize,
340 const triangulationType &triangulation)
const;
346 template <
typename triangulationType>
348 const std::vector<SimplexId> &saddles1,
349 std::vector<Separatrix> &separatrices,
350 std::vector<std::vector<SimplexId>> &separatricesSaddles,
351 const triangulationType &triangulation)
const;
357 template <
typename triangulationType>
360 const std::vector<Separatrix> &separatrices,
361 const std::vector<std::vector<SimplexId>> &separatricesSaddles,
363 const triangulationType &triangulation)
const;
369 std::vector<std::vector<Separatrix>> &separatrices)
const;
374 template <
typename triangulationType>
377 const triangulationType &triangulation)
const;
382 template <
typename triangulationType>
385 const triangulationType &triangulation)
const;
391 template <
typename triangulationType>
393 const SimplexId *
const ascendingManifold,
394 const SimplexId *
const descendingManifold,
396 const triangulationType &triangulation)
const;
398 template <
typename dataType,
typename triangulationType>
400 const dataType *
const scalars,
402 const triangulationType &triangulation);
427template <
typename dataType,
typename triangulationType>
432 const dataType *
const scalars,
433 const size_t scalarsMTime,
435 const triangulationType &triangulation) {
436#ifndef TTK_ENABLE_KAMIKAZE
437 if(scalars ==
nullptr) {
438 this->
printErr(
"Input scalar field pointer is null.");
442 if(offsets ==
nullptr) {
443 this->
printErr(
"Input offset field pointer is null.");
452 const auto dim = triangulation.getDimensionality();
464 const auto nVerts{triangulation.getNumberOfVertices()};
467 const auto pair{std::minmax_element(offsets, offsets + nVerts)};
469 const auto globmin = std::distance(offsets, pair.first);
470 const auto globmax = std::distance(offsets, pair.second);
471 persistenceThreshold *= (scalars[globmax] - scalars[globmin]);
472 this->
printMsg(
"Absolute saddle connectors persistence threshold is "
473 + std::to_string(persistenceThreshold),
478 persistenceThreshold, scalars, offsets, triangulation);
481 std::array<std::vector<SimplexId>, 4> criticalPoints{};
485 this->
printMsg(
" Critical points extracted", 1.0, tm.getElapsedTime(),
490 std::vector<std::vector<Separatrix>> separatrices1{};
497 separatrices1.emplace_back();
500 criticalPoints[1], separatrices1.back(), triangulation);
502 this->
printMsg(
" Descending 1-separatrices computed", 1.0,
509 separatrices1.emplace_back();
512 criticalPoints[dim - 1], separatrices1.back(), triangulation);
514 this->
printMsg(
" Ascending 1-separatrices computed", 1.0,
522 separatrices1.emplace_back();
539 this->
printMsg(
" 1-separatrices set", 1.0, tmp.getElapsedTime(),
543 this->
printMsg(
"1-separatrices computed", 1.0, tm1sep.getElapsedTime(),
544 this->threadNumber_);
552 std::vector<Separatrix> separatrices;
553 std::vector<std::vector<SimplexId>> separatricesSaddles;
555 criticalPoints[2], separatrices, separatricesSaddles, triangulation);
557 outSeps2, separatrices, separatricesSaddles, offsets, triangulation);
559 this->
printMsg(
" Descending 2-separatrices computed", 1.0,
566 std::vector<Separatrix> separatrices;
567 std::vector<std::vector<SimplexId>> separatricesSaddles;
569 criticalPoints[1], separatrices, separatricesSaddles, triangulation);
571 outSeps2, separatrices, separatricesSaddles, offsets, triangulation);
573 this->
printMsg(
" Ascending 2-separatrices computed", 1.0,
580 this->
printMsg(
"2-separatrices computed", 1.0, tm2sep.getElapsedTime(),
581 this->threadNumber_);
589 criticalPoints[dim], outManifold.
ascending_, triangulation);
593 criticalPoints[0], outManifold.
descending_, triangulation);
603 "Segmentation computed", 1.0, tmp.
getElapsedTime(), this->threadNumber_);
619 + std::to_string(triangulation.getNumberOfVertices())
620 +
" points) processed",
630template <
typename triangulationType>
632 const std::vector<SimplexId> &saddles,
633 std::vector<Separatrix> &separatrices,
634 const triangulationType &triangulation)
const {
636 const SimplexId numberOfSaddles = saddles.size();
639 const SimplexId numberOfSeparatrices = 2 * numberOfSaddles;
640 separatrices.resize(numberOfSeparatrices);
643#ifdef TTK_ENABLE_OPENMP
644#pragma omp parallel for num_threads(threadNumber_)
646 for(
SimplexId i = 0; i < numberOfSaddles; ++i) {
647 const Cell saddle{1, saddles[i]};
651 const Cell &saddle1 = saddle;
653 for(
int j = 0; j < 2; ++j) {
655 triangulation.getEdgeVertex(saddle1.
id_, j, vertexId);
657 std::vector<Cell> vpath;
658 vpath.push_back(saddle1);
659 discreteGradient_.getDescendingPath(
660 Cell(0, vertexId), vpath, triangulation);
662 const Cell &lastCell = vpath.back();
663 if(lastCell.
dim_ == 0 and discreteGradient_.isCellCritical(lastCell)) {
664 separatrices[2 * i + j].source_ = saddle;
665 separatrices[2 * i + j].destination_ = lastCell;
666 separatrices[2 * i + j].geometry_ = std::move(vpath);
675template <
typename triangulationType>
677 const std::vector<SimplexId> &saddles,
678 std::vector<Separatrix> &separatrices,
679 const triangulationType &triangulation)
const {
684 const auto dim{triangulation.getDimensionality()};
687 auto getFaceStarNumber = &triangulationType::getTriangleStarNumber;
688 auto getFaceStar = &triangulationType::getTriangleStar;
691 getFaceStarNumber = &triangulationType::getEdgeStarNumber;
692 getFaceStar = &triangulationType::getEdgeStar;
695 const SimplexId numberOfSaddles = saddles.size();
697 std::vector<std::vector<Separatrix>> sepsPerSaddle(numberOfSaddles);
700#ifdef TTK_ENABLE_OPENMP
701#pragma omp parallel for num_threads(threadNumber_)
703 for(
SimplexId i = 0; i < numberOfSaddles; ++i) {
704 const Cell saddle{dim - 1, saddles[i]};
707 const auto starNumber{(triangulation.*getFaceStarNumber)(saddle.id_)};
708 for(
SimplexId j = 0; j < starNumber; ++j) {
711 (triangulation.*getFaceStar)(saddle.id_, j, sId);
713 std::vector<Cell> vpath{saddle};
714 discreteGradient_.getAscendingPath(
Cell(dim, sId), vpath, triangulation);
716 const Cell &lastCell = vpath.back();
717 if(lastCell.
dim_ == dim and discreteGradient_.isCellCritical(lastCell)) {
718 sepsPerSaddle[i].emplace_back();
719 sepsPerSaddle[i].back().source_ = saddle;
720 sepsPerSaddle[i].back().destination_ = lastCell;
721 sepsPerSaddle[i].back().geometry_ = std::move(vpath);
726 this->flattenSeparatricesVectors(sepsPerSaddle);
728 separatrices = std::move(sepsPerSaddle[0]);
733template <
typename triangulationType>
735 const std::vector<SimplexId> &saddles2,
736 std::vector<Separatrix> &separatrices,
737 const triangulationType &triangulation)
const {
742 const auto nTriangles = triangulation.getNumberOfTriangles();
744 std::vector<bool> isVisited(nTriangles,
false);
745 std::vector<SimplexId> visitedTriangles{};
747 using Vpath = std::vector<Cell>;
749 const auto dim{triangulation.getDimensionality()};
751 std::vector<std::vector<Separatrix>> sepsByThread(saddles2.size());
752 std::vector<SimplexId> saddles1{};
754#ifdef TTK_ENABLE_OPENMP
755#pragma omp parallel for num_threads(threadNumber_) schedule(dynamic) \
756 firstprivate(isVisited, visitedTriangles, saddles1)
758 for(
size_t i = 0; i < saddles2.size(); ++i) {
759 const Cell s2{dim - 1, saddles2[i]};
762 discreteGradient_.getDescendingWall(
763 s2, mask, triangulation,
nullptr, &saddles1);
765 for(
const auto saddle1Id : saddles1) {
766 const Cell s1{1, saddle1Id};
769 const bool isMultiConnected
770 = discreteGradient_.getAscendingPathThroughWall(
771 s1, s2, isVisited, &vpath, triangulation);
777 const auto &last = vpath.back();
779 if(!isMultiConnected && last.dim_ == s2.dim_ && last.id_ == s2.id_) {
780 sepsByThread[i].emplace_back();
781 sepsByThread[i].back().source_ = s1;
782 sepsByThread[i].back().destination_ = s2;
783 sepsByThread[i].back().geometry_ = std::move(vpath);
788 this->flattenSeparatricesVectors(sepsByThread);
790 separatrices = std::move(sepsByThread[0]);
795template <
typename triangulationType>
798 const std::vector<Separatrix> &separatrices,
800 const triangulationType &triangulation)
const {
818 std::vector<size_t> geomPointsBegId{npoints};
820 std::vector<size_t> geomCellsBegId{ncells};
823 for(
size_t i = 0; i < separatrices.size(); ++i) {
824 const auto &sep = separatrices[i];
825 const auto sepSize = sep.geometry_.size();
827 ncells += sepSize - 1;
828 geomPointsBegId.emplace_back(npoints);
829 geomCellsBegId.emplace_back(ncells);
832 const int dimensionality = triangulation.getCellVertexNumber(0) - 1;
846 separatrixFunctionMaxima.resize(separatrixId + separatrices.size());
847 separatrixFunctionMinima.resize(separatrixId + separatrices.size());
850#ifdef TTK_ENABLE_OPENMP
851#pragma omp parallel for num_threads(threadNumber_) schedule(dynamic)
853 for(
size_t i = 0; i < separatrices.size(); ++i) {
854 const auto &sep = separatrices[i];
855 const auto &sepGeom = sep.geometry_;
856 const auto sepId = separatrixId + i;
863 const auto saddleConnector
864 = dimensionality == 3 && src.
dim_ == 1 && dst.
dim_ == 2;
866 = saddleConnector ? 1 : std::min(dst.
dim_, dimensionality - 1);
870 return offsets[a] < offsets[b];
872 const std::array<SimplexId, 2> gVerts{
873 discreteGradient_.getCellGreaterVertex(src, triangulation),
874 discreteGradient_.getCellGreaterVertex(dst, triangulation)};
875 const auto sepFuncMax
876 = *std::max_element(gVerts.begin(), gVerts.end(), vertsOrder);
877 const std::array<SimplexId, 2> lVerts{
878 discreteGradient_.getCellLowerVertex(src, triangulation),
879 discreteGradient_.getCellLowerVertex(dst, triangulation)};
880 const auto sepFuncMin
881 = *std::min_element(lVerts.begin(), lVerts.end(), vertsOrder);
882 separatrixFunctionMaxima[sepId] = sepFuncMax;
883 separatrixFunctionMinima[sepId] = sepFuncMin;
886 const auto onBoundary
887 =
static_cast<char>(discreteGradient_.isBoundary(src, triangulation))
888 +
static_cast<char>(discreteGradient_.isBoundary(dst, triangulation));
890 for(
size_t j = 0; j < sepGeom.size(); ++j) {
891 const auto &cell = sepGeom[j];
892 std::array<float, 3> pt{};
893 triangulation.getCellIncenter(cell.id_, cell.dim_, pt.data());
896 const auto k = geomPointsBegId[i] + j;
898 points[3 * k + 0] = pt[0];
899 points[3 * k + 1] = pt[1];
900 points[3 * k + 2] = pt[2];
903 = (j == 0 || j == sepGeom.size() - 1) ? 0 : 1;
912 const auto l = geomCellsBegId[i] + j - 1;
914 cellsConn[2 * l + 0] = k - 1;
915 cellsConn[2 * l + 1] = k;
936template <
typename triangulationType>
938 const std::vector<SimplexId> &saddles1,
939 std::vector<Separatrix> &separatrices,
940 std::vector<std::vector<SimplexId>> &separatricesSaddles,
941 const triangulationType &triangulation)
const {
942 const Cell emptyCell;
944 const SimplexId numberOfSaddles = saddles1.size();
948 const SimplexId numberOfSeparatrices = numberOfSaddles;
949 separatrices.resize(numberOfSeparatrices);
950 separatricesSaddles.resize(numberOfSeparatrices);
952 const auto nEdges = triangulation.getNumberOfEdges();
953 std::vector<bool> isVisited(nEdges,
false);
954 std::vector<SimplexId> visitedEdges{};
957#ifdef TTK_ENABLE_OPENMP
958#pragma omp parallel for num_threads(threadNumber_) schedule(dynamic) \
959 firstprivate(isVisited, visitedEdges)
961 for(
SimplexId i = 0; i < numberOfSaddles; ++i) {
962 const Cell saddle1{1, saddles1[i]};
964 std::vector<Cell> wall;
966 discreteGradient_.getAscendingWall(
967 saddle1, mask, triangulation, &wall, &separatricesSaddles[i]);
969 separatrices[i].source_ = saddle1;
970 separatrices[i].destination_ = emptyCell;
971 separatrices[i].geometry_ = std::move(wall);
977template <
typename triangulationType>
979 const std::vector<SimplexId> &saddles2,
980 std::vector<Separatrix> &separatrices,
981 std::vector<std::vector<SimplexId>> &separatricesSaddles,
982 const triangulationType &triangulation)
const {
983 const Cell emptyCell;
985 const SimplexId numberOfSaddles = saddles2.size();
989 const SimplexId numberOfSeparatrices = numberOfSaddles;
990 separatrices.resize(numberOfSeparatrices);
991 separatricesSaddles.resize(numberOfSeparatrices);
993 const auto nTriangles = triangulation.getNumberOfTriangles();
994 std::vector<bool> isVisited(nTriangles,
false);
995 std::vector<SimplexId> visitedTriangles{};
997 const auto dim{triangulation.getDimensionality()};
1000#ifdef TTK_ENABLE_OPENMP
1001#pragma omp parallel for num_threads(threadNumber_) schedule(dynamic) \
1002 firstprivate(isVisited, visitedTriangles)
1004 for(
SimplexId i = 0; i < numberOfSaddles; ++i) {
1005 const Cell saddle2{dim - 1, saddles2[i]};
1007 std::vector<Cell> wall;
1009 discreteGradient_.getDescendingWall(
1010 saddle2, mask, triangulation, &wall, &separatricesSaddles[i]);
1012 separatrices[i].source_ = saddle2;
1013 separatrices[i].destination_ = emptyCell;
1014 separatrices[i].geometry_ = std::move(wall);
1020template <
typename triangulationType>
1024 const size_t polSize,
1025 const triangulationType &triangulation)
const {
1027 for(
size_t i = 0; i < polSize; ++i) {
1029 triangulation.getEdgeStar(edgeId, i, starId);
1030 polygon[i] = starId;
1036template <
typename triangulationType>
1039 const size_t polSize,
1040 const triangulationType &triangulation)
const {
1042 for(
size_t i = 1; i < polSize; ++i) {
1045 bool isFound =
false;
1047 for(; j < polSize; ++j) {
1050 k < triangulation.getCellNeighborNumber(polygon[i - 1]); ++k) {
1052 triangulation.getCellNeighbor(polygon[i - 1], k, neighborId);
1053 if(neighborId == polygon[j]) {
1064 std::swap(polygon[j], polygon[i]);
1071template <
typename triangulationType>
1074 const std::vector<Separatrix> &separatrices,
1075 const std::vector<std::vector<SimplexId>> &separatricesSaddles,
1077 const triangulationType &triangulation)
const {
1095 const auto noldcells{ncells};
1099 std::vector<size_t> geomCellsBegId{ncells};
1102 for(
size_t i = 0; i < separatrices.size(); ++i) {
1103 const auto &sep = separatrices[i];
1104 ncells += sep.geometry_.size();
1105 geomCellsBegId.emplace_back(ncells);
1109 std::vector<SimplexId> sepSourceIds(separatrices.size());
1110 std::vector<SimplexId> sepIds(separatrices.size());
1111 std::vector<char> sepOnBoundary(separatrices.size());
1112 separatrixFunctionMaxima.resize(separatrixId + separatrices.size());
1113 separatrixFunctionMinima.resize(separatrixId + separatrices.size());
1115 std::vector<SimplexId> polygonNTetras(ncells - noldcells);
1116 std::vector<SimplexId> polygonEdgeIds(ncells - noldcells);
1117 std::vector<SimplexId> polygonSepInfosIds(ncells - noldcells);
1119#ifdef TTK_ENABLE_OPENMP
1120#pragma omp parallel for num_threads(threadNumber_) schedule(dynamic)
1122 for(
size_t i = 0; i < separatrices.size(); ++i) {
1123 const auto &sep = separatrices[i];
1124 const auto &sepGeom = sep.geometry_;
1125 const auto &sepSaddles = separatricesSaddles[i];
1126 const auto sepId = separatrixId + i;
1130 const auto sepFuncMin
1131 = discreteGradient_.getCellLowerVertex(src, triangulation);
1133 if(!sepSaddles.empty()) {
1135 const auto maxId = *std::max_element(
1136 sepSaddles.begin(), sepSaddles.end(),
1138 return offsets[discreteGradient_.getCellGreaterVertex(
1139 Cell{2, a}, triangulation)]
1140 < offsets[discreteGradient_.getCellGreaterVertex(
1141 Cell{2, b}, triangulation)];
1144 = discreteGradient_.getCellGreaterVertex(
Cell{2, maxId}, triangulation);
1148 const auto maxId = *std::max_element(
1149 sepGeom.begin(), sepGeom.end(),
1150 [&triangulation, offsets,
this](
const Cell &a,
const Cell &b) {
1151 return offsets[discreteGradient_.getCellGreaterVertex(
1153 < offsets[discreteGradient_.getCellGreaterVertex(
1156 sepFuncMax = discreteGradient_.getCellGreaterVertex(maxId, triangulation);
1160 const char onBoundary
1161 = (sepSaddles.empty()
1163 : std::count_if(sepSaddles.begin(), sepSaddles.end(),
1165 return triangulation.isTriangleOnBoundary(a);
1167 + triangulation.isEdgeOnBoundary(src.id_);
1170 sepSourceIds[i] = src.id_;
1171 separatrixFunctionMaxima[sepId] = sepFuncMax;
1172 separatrixFunctionMinima[sepId] = sepFuncMin;
1173 sepOnBoundary[i] = onBoundary;
1175 for(
size_t j = 0; j < sepGeom.size(); ++j) {
1176 const auto &cell = sepGeom[j];
1178 const auto k = geomCellsBegId[i] + j - noldcells;
1180 polygonNTetras[k] = triangulation.getEdgeStarNumber(cell.id_);
1182 if(polygonNTetras[k] > 2) {
1183 polygonEdgeIds[k] = cell.id_;
1184 polygonSepInfosIds[k] = i;
1190 std::vector<SimplexId> validTetraIds{};
1191 validTetraIds.reserve(polygonNTetras.size());
1193 for(
size_t i = 0; i < polygonNTetras.size(); ++i) {
1194 if(polygonNTetras[i] > 2) {
1195 validTetraIds.emplace_back(i);
1200 size_t nnewpoints{};
1201 std::vector<SimplexId> pointsPerCell(validTetraIds.size() + 1);
1202 for(
size_t i = 0; i < validTetraIds.size(); ++i) {
1203 nnewpoints += polygonNTetras[validTetraIds[i]];
1204 pointsPerCell[i + 1] = nnewpoints;
1208 outSeps2.cl.connectivity_.resize(firstCellId + nnewpoints);
1209 auto cellsConn = &outSeps2.cl.connectivity_[firstCellId];
1211 std::vector<SimplexId> cellVertsIds(nnewpoints);
1213#ifdef TTK_ENABLE_OPENMP
1214#pragma omp parallel for num_threads(threadNumber_)
1216 for(
size_t i = 0; i < validTetraIds.size(); ++i) {
1217 const auto k = validTetraIds[i];
1220 getDualPolygon(polygonEdgeIds[k], &cellVertsIds[pointsPerCell[i]],
1221 polygonNTetras[k], triangulation);
1223 sortDualPolygonVertices(
1224 &cellVertsIds[pointsPerCell[i]], polygonNTetras[k], triangulation);
1226 for(
SimplexId j = 0; j < polygonNTetras[k]; ++j) {
1227 cellsConn[pointsPerCell[i] + j] = cellVertsIds[pointsPerCell[i] + j];
1231 TTK_PSORT(this->threadNumber_, cellVertsIds.begin(), cellVertsIds.end());
1232 const auto last = std::unique(cellVertsIds.begin(), cellVertsIds.end());
1233 cellVertsIds.erase(last, cellVertsIds.end());
1236 std::vector<SimplexId> vertId2PointsId(triangulation.getNumberOfCells());
1238 const auto noldpoints{npoints};
1239 npoints += cellVertsIds.size();
1240 ncells = noldcells + validTetraIds.size();
1243 outSeps2.pt.points_.resize(3 * npoints);
1244 auto points = &outSeps2.pt.points_[3 * noldpoints];
1245 outSeps2.cl.offsets_.resize(ncells + 1);
1246 outSeps2.cl.offsets_[0] = 0;
1247 auto cellsOff = &outSeps2.cl.offsets_[noldcells];
1248 outSeps2.cl.sourceIds_.resize(ncells);
1249 outSeps2.cl.separatrixIds_.resize(ncells);
1250 outSeps2.cl.separatrixTypes_.resize(ncells);
1251 outSeps2.cl.isOnBoundary_.resize(ncells);
1253#ifdef TTK_ENABLE_OPENMP
1254#pragma omp parallel for num_threads(threadNumber_)
1256 for(
size_t i = 0; i < cellVertsIds.size(); ++i) {
1258 triangulation.getTetraIncenter(cellVertsIds[i], &points[3 * i]);
1260 vertId2PointsId[cellVertsIds[i]] = i + noldpoints;
1263#ifdef TTK_ENABLE_OPENMP
1264#pragma omp parallel for num_threads(threadNumber_)
1266 for(
size_t i = 0; i < validTetraIds.size(); ++i) {
1267 const auto m = validTetraIds[i];
1268 const auto k = pointsPerCell[i];
1269 for(
SimplexId j = 0; j < polygonNTetras[m]; ++j) {
1270 cellsConn[k + j] = vertId2PointsId[cellsConn[k + j]];
1272 const auto l = i + noldcells;
1273 const auto n = polygonSepInfosIds[m];
1274 outSeps2.cl.sourceIds_[l] = sepSourceIds[n];
1275 outSeps2.cl.separatrixIds_[l] = sepIds[n];
1276 outSeps2.cl.separatrixTypes_[l] = 1;
1277 outSeps2.cl.isOnBoundary_[l] = sepOnBoundary[n];
1280 for(
size_t i = 0; i < validTetraIds.size(); ++i) {
1282 cellsOff[i + 1] = cellsOff[i] + polygonNTetras[validTetraIds[i]];
1285 outSeps2.pt.numberOfPoints_ = npoints;
1286 outSeps2.cl.numberOfCells_ = ncells;
1291template <
typename triangulationType>
1294 const std::vector<Separatrix> &separatrices,
1295 const std::vector<std::vector<SimplexId>> &separatricesSaddles,
1297 const triangulationType &triangulation)
const {
1315 const auto noldcells{ncells};
1320 std::vector<size_t> geomCellsBegId{ncells};
1323 for(
size_t i = 0; i < separatrices.size(); ++i) {
1324 const auto &sep = separatrices[i];
1325 ncells += sep.geometry_.size();
1326 geomCellsBegId.emplace_back(ncells);
1333 firstCellId + 3 * (ncells - noldcells));
1334 auto cellsOff = &outSeps2.
cl.
offsets_[noldcells];
1339 separatrixFunctionMaxima.resize(separatrixId + separatrices.size());
1340 separatrixFunctionMinima.resize(separatrixId + separatrices.size());
1344 std::vector<SimplexId> cellVertsIds(3 * (ncells - noldcells));
1346#ifdef TTK_ENABLE_OPENMP
1347#pragma omp parallel for num_threads(threadNumber_)
1349 for(
size_t i = 0; i < separatrices.size(); ++i) {
1350 const auto &sep = separatrices[i];
1351 const auto &sepGeom = sep.geometry_;
1352 const auto &sepSaddles = separatricesSaddles[i];
1353 const auto sepId = separatrixId + i;
1355 const char sepType = 2;
1358 const auto sepFuncMax
1359 = discreteGradient_.getCellGreaterVertex(src, triangulation);
1361 if(!sepSaddles.empty()) {
1363 const auto minId = *std::min_element(
1364 sepSaddles.begin(), sepSaddles.end(),
1366 return offsets[discreteGradient_.getCellLowerVertex(
1367 Cell{1, a}, triangulation)]
1368 < offsets[discreteGradient_.getCellLowerVertex(
1369 Cell{1, b}, triangulation)];
1372 = discreteGradient_.getCellLowerVertex(
Cell{1, minId}, triangulation);
1376 const auto minId = *std::min_element(
1377 sepGeom.begin(), sepGeom.end(),
1378 [&triangulation, offsets,
this](
const Cell &a,
const Cell &b) {
1379 return offsets[discreteGradient_.getCellLowerVertex(a, triangulation)]
1380 < offsets[discreteGradient_.getCellLowerVertex(
1383 sepFuncMin = discreteGradient_.getCellLowerVertex(minId, triangulation);
1385 separatrixFunctionMaxima[sepId] = sepFuncMax;
1386 separatrixFunctionMinima[sepId] = sepFuncMin;
1389 const char onBoundary
1390 = (sepSaddles.empty()
1392 : std::count_if(sepSaddles.begin(), sepSaddles.end(),
1394 return triangulation.isEdgeOnBoundary(a);
1396 + triangulation.isTriangleOnBoundary(src.id_);
1398 for(
size_t j = 0; j < sepGeom.size(); ++j) {
1399 const auto &cell = sepGeom[j];
1403 triangulation.getTriangleVertex(cell.id_, 0, v0);
1404 triangulation.getTriangleVertex(cell.id_, 1, v1);
1405 triangulation.getTriangleVertex(cell.id_, 2, v2);
1408 const auto l = geomCellsBegId[i] + j;
1410 const auto m = l - noldcells;
1412 cellsConn[3 * m + 0] = v0;
1413 cellsConn[3 * m + 1] = v1;
1414 cellsConn[3 * m + 2] = v2;
1415 cellVertsIds[3 * m + 0] = v0;
1416 cellVertsIds[3 * m + 1] = v1;
1417 cellVertsIds[3 * m + 2] = v2;
1428 TTK_PSORT(this->threadNumber_, cellVertsIds.begin(), cellVertsIds.end());
1429 const auto last = std::unique(cellVertsIds.begin(), cellVertsIds.end());
1430 cellVertsIds.erase(last, cellVertsIds.end());
1433 std::vector<size_t> vertId2PointsId(triangulation.getNumberOfVertices());
1435 const auto noldpoints{npoints};
1436 npoints += cellVertsIds.size();
1437 outSeps2.pt.points_.resize(3 * npoints);
1438 auto points = &outSeps2.pt.points_[3 * noldpoints];
1440#ifdef TTK_ENABLE_OPENMP
1441#pragma omp parallel for num_threads(threadNumber_)
1443 for(
size_t i = 0; i < cellVertsIds.size(); ++i) {
1445 triangulation.getVertexPoint(
1446 cellVertsIds[i], points[3 * i + 0], points[3 * i + 1], points[3 * i + 2]);
1448 vertId2PointsId[cellVertsIds[i]] = i + noldpoints;
1451 const auto lastOffset = noldcells == 0 ? 0 : cellsOff[-1];
1453#ifdef TTK_ENABLE_OPENMP
1454#pragma omp parallel for num_threads(threadNumber_)
1456 for(
size_t i = 0; i < ncells - noldcells; ++i) {
1457 cellsOff[i] = 3 * i + lastOffset;
1458 cellsConn[3 * i + 0] = vertId2PointsId[cellsConn[3 * i + 0]];
1459 cellsConn[3 * i + 1] = vertId2PointsId[cellsConn[3 * i + 1]];
1460 cellsConn[3 * i + 2] = vertId2PointsId[cellsConn[3 * i + 2]];
1463 cellsOff[ncells - noldcells] = cellsOff[ncells - noldcells - 1] + 3;
1465 outSeps2.pt.numberOfPoints_ = npoints;
1466 outSeps2.cl.numberOfCells_ = ncells;
1475template <
typename triangulationType>
1477 const std::vector<SimplexId> &maxima,
1479 const triangulationType &triangulation)
const {
1481 if(morseSmaleManifold ==
nullptr) {
1482 this->printErr(
"Could not compute ascending segmentation");
1488 const auto nVerts{triangulation.getNumberOfVertices()};
1489 std::fill(morseSmaleManifold, morseSmaleManifold + nVerts, -1);
1490 if(maxima.empty()) {
1495 const auto nCells{triangulation.getNumberOfCells()};
1496 std::vector<SimplexId> morseSmaleManifoldOnCells(nCells, -1);
1499 for(
const auto &
id : maxima) {
1501 morseSmaleManifoldOnCells[id] = nMax++;
1504 const auto dim{triangulation.getDimensionality()};
1507 auto getFaceStarNumber = &triangulationType::getTriangleStarNumber;
1508 auto getFaceStar = &triangulationType::getTriangleStar;
1511 getFaceStarNumber = &triangulationType::getEdgeStarNumber;
1512 getFaceStar = &triangulationType::getEdgeStar;
1513 }
else if(dim == 1) {
1515 getFaceStarNumber = &triangulationType::getVertexStarNumber;
1516 getFaceStar = &triangulationType::getVertexStar;
1520 std::vector<SimplexId> visited{};
1522 std::vector<uint8_t> isMarked(nCells, 0);
1524#ifdef TTK_ENABLE_OPENMP
1525#pragma omp parallel for num_threads(threadNumber_) firstprivate(visited)
1528 if(isMarked[i] == 1) {
1533 while(morseSmaleManifoldOnCells[curr] == -1) {
1534 if(isMarked[curr] == 1) {
1538 const auto paired{this->discreteGradient_.getPairedCell(
1539 Cell{dim, curr}, triangulation,
true)};
1541 const auto nStars{(triangulation.*getFaceStarNumber)(paired)};
1543 (triangulation.*getFaceStar)(paired, j, next);
1549 visited.emplace_back(curr);
1556 for(
const auto el : visited) {
1557 morseSmaleManifoldOnCells[el] = morseSmaleManifoldOnCells[curr];
1562#ifdef TTK_ENABLE_OPENMP
1563#pragma omp parallel for num_threads(threadNumber_)
1566 if(triangulation.getVertexStarNumber(i) < 1) {
1571 triangulation.getVertexStar(i, 0, starId);
1573 morseSmaleManifold[i] = morseSmaleManifoldOnCells[starId];
1576 this->
printMsg(
" Ascending segmentation computed", 1.0, tm.getElapsedTime(),
1583template <
typename triangulationType>
1585 const std::vector<SimplexId> &minima,
1587 const triangulationType &triangulation)
const {
1589 if(morseSmaleManifold ==
nullptr) {
1590 this->printErr(
"Could not compute descending segmentation");
1596 const auto nVerts{triangulation.getNumberOfVertices()};
1598 if(minima.size() == 1) {
1600 std::fill(morseSmaleManifold, morseSmaleManifold + nVerts, 0);
1604 std::fill(morseSmaleManifold, morseSmaleManifold + nVerts, -1);
1607 for(
const auto &cp : minima) {
1609 morseSmaleManifold[cp] = nMin++;
1612 std::vector<SimplexId> visited{};
1614#ifdef TTK_ENABLE_OPENMP
1615#pragma omp parallel for num_threads(threadNumber_) firstprivate(visited)
1618 if(morseSmaleManifold[i] != -1) {
1623 while(morseSmaleManifold[curr] == -1) {
1625 const auto pairedEdge{
1626 this->discreteGradient_.getPairedCell(
Cell{0, curr}, triangulation)};
1628 triangulation.getEdgeVertex(pairedEdge, 0, next);
1630 triangulation.getEdgeVertex(pairedEdge, 1, next);
1632 visited.emplace_back(curr);
1635 for(
const auto el : visited) {
1636 morseSmaleManifold[el] = morseSmaleManifold[curr];
1640 this->
printMsg(
" Descending segmentation computed", 1.0, tm.getElapsedTime(),
1647template <
typename triangulationType>
1650 const SimplexId *
const ascendingManifold,
1651 const SimplexId *
const descendingManifold,
1653 const triangulationType &triangulation)
const {
1655 if(ascendingManifold ==
nullptr || descendingManifold ==
nullptr
1656 || morseSmaleManifold ==
nullptr) {
1657 this->printErr(
"Could not compute final segmentation");
1663 const size_t nVerts = triangulation.getNumberOfVertices();
1667#ifdef TTK_ENABLE_OPENMP
1668#pragma omp parallel for num_threads(threadNumber_)
1670 for(
size_t i = 0; i < nVerts; ++i) {
1671 const auto d = ascendingManifold[i];
1672 const auto a = descendingManifold[i];
1673 if(a == -1 || d == -1) {
1674 morseSmaleManifold[i] = -1;
1676 morseSmaleManifold[i] = a * numberOfMaxima + d;
1681 std::vector<SimplexId> sparseRegionIds(
1682 morseSmaleManifold, morseSmaleManifold + nVerts);
1686 this->threadNumber_, sparseRegionIds.begin(), sparseRegionIds.end());
1687 const auto last = std::unique(sparseRegionIds.begin(), sparseRegionIds.end());
1688 sparseRegionIds.erase(last, sparseRegionIds.end());
1691 std::map<SimplexId, size_t> sparseToDenseRegionId{};
1693 for(
size_t i = 0; i < sparseRegionIds.size(); ++i) {
1694 sparseToDenseRegionId[sparseRegionIds[i]] = i;
1699#ifdef TTK_ENABLE_OPENMP
1700#pragma omp parallel for num_threads(threadNumber_)
1702 for(
size_t i = 0; i < nVerts; ++i) {
1703 morseSmaleManifold[i] = sparseToDenseRegionId[morseSmaleManifold[i]];
1706 this->
printMsg(
" Final segmentation computed", 1.0, tm.getElapsedTime(),
1713template <
typename dataType,
typename triangulationType>
1715 const double persistenceThreshold,
1716 const dataType *
const scalars,
1718 const triangulationType &triangulation) {
1722 const auto dim{triangulation.getDimensionality()};
1724 this->printWrn(
"Can't return saddle connectors without a 3D dataset");
1731 dms.setDebugLevel(this->debugLevel_);
1732 dms.setGradient(std::move(this->discreteGradient_));
1736 std::vector<PersPairType> dms_pairs{};
1737 dms.computePersistencePairs(dms_pairs, offsets, triangulation,
false,
true);
1738 this->discreteGradient_ = dms.getGradient();
1740 this->discreteGradient_.setLocalGradient();
1742 const auto getPersistence
1743 = [
this, &triangulation, scalars](
const PersPairType &p) {
1744 return this->discreteGradient_.getPersistence(
1745 Cell{2, p.death},
Cell{1, p.birth}, scalars, triangulation);
1749 const auto firstSadSadPair{std::distance(
1751 std::find_if(dms_pairs.begin(), dms_pairs.end(),
1752 [](
const auto &pair) { return pair.type == 1; }))};
1756 std::vector<bool> isVisited(triangulation.getNumberOfTriangles(),
false);
1757 std::vector<SimplexId> visitedTriangles{};
1762 std::vector<std::tuple<size_t, dataType>> pairs;
1763 for(
size_t i = firstSadSadPair; i < dms_pairs.size(); ++i) {
1764 const auto &pair{dms_pairs[i]};
1765 pairs.emplace_back(std::make_tuple(i, getPersistence(pair)));
1767 const auto comparePersistence
1768 = [](
const std::tuple<size_t, dataType> &pair1,
1769 const std::tuple<size_t, dataType> &pair2) {
1770 return std::get<1>(pair1) < std::get<1>(pair2);
1773 this->threadNumber_, pairs.begin(), pairs.end(), comparePersistence);
1775 std::vector<std::tuple<dataType, SimplexId, SimplexId>> skippedPairsPers;
1778 for(
const auto &pairTup : pairs) {
1779 const auto &pairIndex = std::get<0>(pairTup);
1780 const auto &pair{dms_pairs[pairIndex]};
1781 const auto &pairPersistence = std::get<1>(pairTup);
1783 if(pair.type != 1 || pairPersistence > persistenceThreshold) {
1787 const Cell birth{1, pair.birth};
1788 const Cell death{2, pair.death};
1792 this->discreteGradient_.getDescendingWall(death, mask, triangulation);
1794 std::vector<Cell> vpath{};
1795 bool disableForkReversal = not ForceLoopFreeGradient;
1796 this->discreteGradient_.getAscendingPathThroughWall(
1797 birth, death, isVisited, &vpath, triangulation, disableForkReversal);
1799 if(vpath.back() == death) {
1800 this->discreteGradient_.reverseAscendingPathOnWall(vpath, triangulation);
1804 if(ForceLoopFreeGradient) {
1806 = this->discreteGradient_.detectGradientCycle(birth, triangulation);
1808 std::vector<bool> isVisitedUpdated(
1809 triangulation.getNumberOfTriangles(),
false);
1810 std::vector<SimplexId> visitedTrianglesUpdated{};
1811 VisitedMask maskUpdated{isVisitedUpdated, visitedTrianglesUpdated};
1812 discreteGradient_.getDescendingWall(death, maskUpdated, triangulation);
1813 discreteGradient_.getAscendingPathThroughWall(
1814 birth, death, isVisitedUpdated,
nullptr, triangulation,
false,
true,
1818 this->discreteGradient_.reverseAscendingPathOnWall(
1819 vpath, triangulation,
true);
1820 this->
printMsg(
"Could not return saddle connector " + birth.to_string()
1821 +
" -> " + death.to_string()
1822 +
" without creating a cycle.",
1825 skippedPairsPers.emplace_back(
1826 std::make_tuple(pairPersistence, pair.birth, pair.death));
1831 this->
printMsg(
"Could not return saddle connector " + birth.to_string()
1832 +
" -> " + death.to_string(),
1835 skippedPairsPers.emplace_back(
1836 std::make_tuple(pairPersistence, pair.birth, pair.death));
1842 this->threadNumber_, skippedPairsPers.begin(), skippedPairsPers.end());
1843 for(
unsigned int i = 0; i < skippedPairsPers.size(); ++i)
1844 this->
printMsg(std::to_string(i) +
" "
1845 + std::to_string(std::get<1>(skippedPairsPers[i])) +
" "
1846 + std::to_string(std::get<2>(skippedPairsPers[i])) +
" "
1847 + std::to_string(std::get<0>(skippedPairsPers[i])));
1850 this->
printMsg(
"Returned " + std::to_string(nReturned) +
" saddle connectors",
1851 1.0, tm.getElapsedTime(), this->threadNumber_);
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
virtual int preconditionCellNeighbors()
virtual int preconditionCellEdges()
virtual int setThreadNumber(const int threadNumber)
Minimalist debugging class.
virtual int setDebugLevel(const int &debugLevel)
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
TTK DiscreteMorseSandwich processing package.
TTK processing package for the computation of Morse-Smale complexes.
int setAscendingSegmentation(const std::vector< SimplexId > &maxima, SimplexId *const morseSmaleManifold, const triangulationType &triangulation) const
int setFinalSegmentation(const SimplexId numberOfMaxima, const SimplexId *const ascendingManifold, const SimplexId *const descendingManifold, SimplexId *const morseSmaleManifold, const triangulationType &triangulation) const
bool ComputeSaddleConnectors
void flattenSeparatricesVectors(std::vector< std::vector< Separatrix > > &separatrices) const
Flatten the vectors of vectors into their first component.
bool ComputeDescendingSeparatrices2
bool ComputeDescendingSegmentation
int getDualPolygon(const SimplexId edgeId, SimplexId *const polygon, const size_t polSize, const triangulationType &triangulation) const
void setComputeCriticalPoints(const bool state)
int getAscendingSeparatrices2(const std::vector< SimplexId > &saddles1, std::vector< Separatrix > &separatrices, std::vector< std::vector< SimplexId > > &separatricesSaddles, const triangulationType &triangulation) const
int getSaddleConnectors(const std::vector< SimplexId > &saddles2, std::vector< Separatrix > &separatrices, const triangulationType &triangulation) const
int getDescendingSeparatrices1(const std::vector< SimplexId > &saddles, std::vector< Separatrix > &separatrices, const triangulationType &triangulation) const
dcg::DiscreteGradient discreteGradient_
int returnSaddleConnectors(const double persistenceThreshold, const dataType *const scalars, const SimplexId *const offsets, const triangulationType &triangulation)
int setAscendingSeparatrices2(Output2Separatrices &outSeps2, const std::vector< Separatrix > &separatrices, const std::vector< std::vector< SimplexId > > &separatricesSaddles, const SimplexId *const offsets, const triangulationType &triangulation) const
bool ComputeAscendingSeparatrices1
bool ComputeDescendingSeparatrices1
int setDescendingSegmentation(const std::vector< SimplexId > &minima, SimplexId *const morseSmaleManifold, const triangulationType &triangulation) const
int setSeparatrices1(Output1Separatrices &outSeps1, const std::vector< Separatrix > &separatrices, const SimplexId *const offsets, const triangulationType &triangulation) const
bool ComputeAscendingSeparatrices2
bool ComputeAscendingSegmentation
void setComputeSegmentation(const bool doAscending, const bool doDescending, const bool doMorseSmale)
void setSaddleConnectorsPersistenceThreshold(const double threshold)
int sortDualPolygonVertices(SimplexId *const polygon, const size_t polSize, const triangulationType &triangulation) const
void setComputeSeparatrices2(const bool doAscending, const bool doDescending)
bool ForceLoopFreeGradient
void preconditionTriangulation(AbstractTriangulation *const data)
int setDescendingSeparatrices2(Output2Separatrices &outSeps2, const std::vector< Separatrix > &separatrices, const std::vector< std::vector< SimplexId > > &separatricesSaddles, const SimplexId *const offsets, const triangulationType &triangulation) const
int getDescendingSeparatrices2(const std::vector< SimplexId > &saddles2, std::vector< Separatrix > &separatrices, std::vector< std::vector< SimplexId > > &separatricesSaddles, const triangulationType &triangulation) const
bool ComputeFinalSegmentation
void setComputeSeparatrices1(const bool doAscending, const bool doDescending, const bool doSaddleConnectors)
void setReturnSaddleConnectors(const bool state)
bool ComputeCriticalPoints
int execute(OutputCriticalPoints &outCP, Output1Separatrices &outSeps1, Output2Separatrices &outSeps2, OutputManifold &outManifold, const dataType *const scalars, const size_t scalarsMTime, const SimplexId *const offsets, const triangulationType &triangulation)
bool ReturnSaddleConnectors
double SaddleConnectorsPersistenceThreshold
int getAscendingSeparatrices1(const std::vector< SimplexId > &saddles, std::vector< Separatrix > &separatrices, const triangulationType &triangulation) const
TTK discreteGradient processing package.
void setInputOffsets(const SimplexId *const data)
int setManifoldSize(const std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, const SimplexId *const ascendingManifold, const SimplexId *const descendingManifold, std::vector< SimplexId > &manifoldSize) const
void setInputScalarField(const void *const data, const size_t mTime)
int getCriticalPoints(std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, const triangulationType &triangulation) const
int buildGradient(const triangulationType &triangulation, bool bypassCache=false, const std::vector< bool > *updateMask=nullptr)
void preconditionTriangulation(AbstractTriangulation *const data)
int setCriticalPoints(const std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, std::vector< std::array< float, 3 > > &points, std::vector< char > &cellDimensions, std::vector< SimplexId > &cellIds, std::vector< char > &isOnBoundary, std::vector< SimplexId > &PLVertexIdentifiers, const triangulationType &triangulation) const
int SimplexId
Identifier type for simplices of any dimension.
Persistence pair struct as exported by DiscreteGradient.
1-Separatrices point and cell data arrays
std::vector< ttk::SimplexId > destinationIds_
std::vector< SimplexId > sepFuncMinId_
std::vector< SimplexId > sepFuncMaxId_
SimplexId numberOfPoints_
std::vector< ttk::SimplexId > separatrixIds_
struct ttk::MorseSmaleComplex::Output1Separatrices::@1 cl
std::vector< char > isOnBoundary_
std::vector< ttk::SimplexId > connectivity_
std::vector< ttk::SimplexId > sourceIds_
std::vector< char > smoothingMask_
std::vector< char > separatrixTypes_
struct ttk::MorseSmaleComplex::Output1Separatrices::@0 pt
std::vector< char > cellDimensions_
std::vector< ttk::SimplexId > cellIds_
std::vector< float > points_
2-Separatrices point and cell data arrays
std::vector< ttk::SimplexId > offsets_
SimplexId numberOfPoints_
std::vector< float > points_
struct ttk::MorseSmaleComplex::Output2Separatrices::@2 pt
std::vector< SimplexId > sepFuncMinId_
std::vector< ttk::SimplexId > sourceIds_
std::vector< char > isOnBoundary_
struct ttk::MorseSmaleComplex::Output2Separatrices::@3 cl
std::vector< ttk::SimplexId > connectivity_
std::vector< char > separatrixTypes_
std::vector< ttk::SimplexId > separatrixIds_
std::vector< SimplexId > sepFuncMaxId_
Critical points data arrays.
std::vector< char > cellDimensions_
std::vector< char > isOnBoundary_
std::vector< SimplexId > cellIds_
std::vector< SimplexId > manifoldSize_
std::vector< SimplexId > PLVertexIdentifiers_
std::vector< std::array< float, 3 > > points_
Pointers to pre-allocated segmentation point data arrays.
std::vector< dcg::Cell > geometry_
Auto-cleaning re-usable graph propagations data structure.
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)