TTK
Loading...
Searching...
No Matches
ttkMergeTree.cpp
Go to the documentation of this file.
1#include <ttkMacros.h>
2#include <ttkMergeTree.h>
3#include <ttkUtils.h>
4
5// VTK includes
6#include <vtkConnectivityFilter.h>
7#include <vtkIdTypeArray.h>
8#include <vtkImageData.h>
9#include <vtkInformation.h>
10#include <vtkInformationVector.h>
11#include <vtkPolyData.h>
12#include <vtkSignedCharArray.h>
13#include <vtkThreshold.h>
14#include <vtkUnsignedCharArray.h>
15#include <vtkVersionMacros.h>
16
18
20 this->setDebugMsgPrefix("MergeTree");
21 SetNumberOfInputPorts(1);
22 SetNumberOfOutputPorts(3);
23}
24
26 // should be called after getScalars for inputScalars_ needs to be filled
27
28 offsets_.resize(nbCC_);
29 for(int cc = 0; cc < nbCC_; cc++) {
30 const auto offsets
33
34 offsets_[cc].resize(connected_components_[cc]->GetNumberOfPoints());
35
36 for(size_t i = 0; i < offsets_[cc].size(); i++) {
37 offsets_[cc][i] = offsets->GetTuple1(i);
38 }
39
40#ifndef TTK_ENABLE_KAMIKAZE
41 if(offsets_[cc].empty()) {
42 this->printMsg(
43 {"Error : wrong input offset scalar field for ", std::to_string(cc)},
45 return 0;
46 }
47#endif
48 }
49
50 return 1;
51}
52
54 inputScalars_.resize(nbCC_);
55 for(int cc = 0; cc < nbCC_; cc++) {
57 = this->GetInputArrayToProcess(0, connected_components_[cc]);
58
59#ifndef TTK_ENABLE_KAMIKAZE
60 if(!inputScalars_[cc]) {
61 this->printMsg({"Error : input scalar ", std::to_string(cc),
62 " field pointer is null."},
64 return 0;
65 }
66#endif
67 }
68
69 return 1;
70}
71
73 triangulation_.resize(nbCC_);
74 ftmTree_ = std::vector<ttk::ftm::LocalFTM>(nbCC_);
75
76 for(int cc = 0; cc < nbCC_; cc++) {
79#ifndef TTK_ENABLE_KAMIKAZE
80 if(!triangulation_[cc]) {
81 this->printErr("Error : ttkTriangulation::getTriangulation() is null.");
82 return 0;
83 }
84#endif
85
86 ftmTree_[cc].tree.setDebugLevel(debugLevel_);
87 ftmTree_[cc].tree.setThreadNumber(threadNumber_);
88 ftmTree_[cc].tree.preconditionTriangulation(triangulation_[cc]);
89
90#ifndef TTK_ENABLE_KAMIKAZE
91 if(triangulation_[cc]->isEmpty()) {
92 this->printMsg({"Error : ttkTriangulation on connected component",
93 std::to_string(cc), " allocation problem."},
95 return 0;
96 }
97#endif
98 }
99 return 1;
100}
101
102int ttkMergeTree::FillInputPortInformation(int port, vtkInformation *info) {
103 if(port == 0) {
104 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
105 return 1;
106 }
107 return 0;
108}
109
110int ttkMergeTree::FillOutputPortInformation(int port, vtkInformation *info) {
111 if(port == 0 || port == 1) {
112 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
113 return 1;
114 } else if(port == 2) {
116 return 1;
117 }
118 return 0;
119}
120
121int ttkMergeTree::RequestData(vtkInformation *ttkNotUsed(request),
122 vtkInformationVector **inputVector,
123 vtkInformationVector *outputVector) {
124
125 const auto input = vtkDataSet::GetData(inputVector[0]);
126 auto outputSkeletonNodes = vtkUnstructuredGrid::GetData(outputVector, 0);
127 auto outputSkeletonArcs = vtkUnstructuredGrid::GetData(outputVector, 1);
128 auto outputSegmentation = vtkDataSet::GetData(outputVector, 2);
129
130#ifndef TTK_ENABLE_KAMIKAZE
131 if(!input) {
132 this->printErr("Error: input pointer is NULL.");
133 return 0;
134 }
135
136 if(!input->GetNumberOfPoints()) {
137 this->printErr("Error: input has no point.");
138 return 0;
139 }
140
141 if(!outputSkeletonNodes || !outputSkeletonArcs || !outputSegmentation) {
142 this->printErr("Error: output pointer is NULL.");
143 return 0;
144 }
145#endif
146
148 && (Backend == (int)BACKEND::EXTREEM)) {
150 printWrn("Contour tree computation triggered.");
151 printWrn("Defaulting to the FTM backend.");
153 }
154
155 if((Backend == (int)BACKEND::EXTREEM) && (!input->IsA("vtkImageData"))) {
157 printWrn("The input is not a regular grid.");
158 printWrn("Defaulting to the FTM backend.");
160 }
161
162 if((Backend == (int)BACKEND::EXTREEM)
164 && (input->IsA("vtkImageData"))) {
165 printMsg("Triggering ExTreeM Backend.");
166 const size_t nVertices = input->GetNumberOfPoints();
167
168 // Get triangulation of the input object
169 auto triangulation = ttkAlgorithm::GetTriangulation(input);
170 if(!triangulation)
171 return 0;
172
173 triangulation->preconditionVertexNeighbors();
174
175 // Get input array
176 auto scalarArray = this->GetInputArrayToProcess(0, inputVector);
177 if(!scalarArray) {
178 this->printErr("Unable to retrieve scalar array.");
179 return 0;
180 }
181
182 // Order Array
183 auto orderArray = this->GetOrderArray(input, 0, triangulation, false);
184 auto orderArrayData = ttkUtils::GetPointer<ttk::SimplexId>(orderArray);
185
186 auto segmentation = vtkDataSet::GetData(outputVector, 2);
187 segmentation->ShallowCopy(input);
188 auto segmentationPD = segmentation->GetPointData();
189
190 // enforce that ascending and descending manifolds exist
191 std::string ascendingName = std::string(scalarArray->GetName()) + "_"
192 + std::string(ttk::MorseSmaleAscendingName);
193 std::string descendingName = std::string(scalarArray->GetName()) + "_"
194 + std::string(ttk::MorseSmaleDescendingName);
195 if(!segmentationPD->HasArray(ascendingName.data())
196 || !segmentationPD->HasArray(descendingName.data())) {
198 this->printWrn("TIP: run `ttkPathCompression` first");
199 this->printWrn("for improved performances :)");
201 bool doAscend = false;
202 bool doDescend = false;
203 if(!segmentationPD->HasArray(ascendingName.data())) {
204 doAscend = true;
205 auto ascendingManifold = vtkSmartPointer<vtkIdTypeArray>::New();
206 ascendingManifold->SetNumberOfComponents(1);
207 ascendingManifold->SetNumberOfTuples(nVertices);
208 ascendingManifold->SetName(ascendingName.data());
209 segmentationPD->AddArray(ascendingManifold);
210 }
211 if(!segmentationPD->HasArray(descendingName.data())) {
212 doDescend = true;
213 auto descendingManifold = vtkSmartPointer<vtkIdTypeArray>::New();
214 descendingManifold->SetNumberOfComponents(1);
215 descendingManifold->SetNumberOfTuples(nVertices);
216 descendingManifold->SetName(descendingName.data());
217 segmentationPD->AddArray(descendingManifold);
218 }
219 ttk::PathCompression subModule;
220 subModule.setThreadNumber(this->threadNumber_);
221 subModule.setDebugLevel(this->debugLevel_);
222 // only compute the segmentation which doesn't exist (maybe both)
223 subModule.setComputeSegmentation(doAscend, doDescend, false);
224
226 ttkUtils::GetPointer<ttk::SimplexId>(
227 segmentationPD->GetArray(ascendingName.data())),
228 ttkUtils::GetPointer<ttk::SimplexId>(
229 segmentationPD->GetArray(descendingName.data())),
230 nullptr};
231
232 int status = 0;
234 triangulation->getType(),
235 (status = subModule.execute<T0>(
236 om, ttkUtils::GetPointer<const ttk::SimplexId>(orderArray),
237 *(T0 *)triangulation->getData())));
238 if(status != 0)
239 return 0;
240 }
241
242 auto ascendingManifold = segmentationPD->GetArray(ascendingName.data());
243 auto descendingManifold = segmentationPD->GetArray(descendingName.data());
244
245 vtkNew<ttkSimplexIdTypeArray> segmentationId{};
246 segmentationId->SetNumberOfComponents(1);
247 segmentationId->SetNumberOfTuples(nVertices);
248 segmentationId->SetName("SegmentationId");
249
250 vtkNew<vtkCharArray> regionType{};
251 regionType->SetNumberOfComponents(1);
252 regionType->SetNumberOfTuples(nVertices);
253 regionType->SetName("RegionType");
254
255 // compute joinTree
256 auto exTreeMTree = ttk::ExTreeM();
257 exTreeMTree.setThreadNumber(this->threadNumber_);
258 exTreeMTree.setDebugLevel(this->debugLevel_);
259 std::map<ttk::SimplexId, int> cpMap{};
260
262 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>>
263 persistencePairsJoin{};
264 std::vector<ttk::ExTreeM::Branch> mergeTreeJoin{};
265
266 int status = 0;
267#ifdef TTK_ENABLE_OPENMP
268#pragma omp parallel for num_threads(this->threadNumber_)
269#endif
270 for(size_t i = 0; i < nVertices; i++) {
271 orderArrayData[i] = nVertices - orderArrayData[i] - 1;
272 }
274 triangulation->getType(),
275 (status = exTreeMTree.computePairs<T0>(
276 persistencePairsJoin, cpMap, mergeTreeJoin,
277 ttkUtils::GetPointer<ttk::SimplexId>(segmentationId),
278 ttkUtils::GetPointer<char>(regionType),
279 ttkUtils::GetPointer<ttk::SimplexId>(ascendingManifold),
280 ttkUtils::GetPointer<ttk::SimplexId>(descendingManifold),
281 orderArrayData, (T0 *)triangulation->getData(), params_.treeType)));
282 // swap the data back (even if the execution failed)
283#ifdef TTK_ENABLE_OPENMP
284#pragma omp parallel for num_threads(this->threadNumber_)
285#endif
286 for(size_t i = 0; i < nVertices; i++) {
287 orderArrayData[i] = nVertices - orderArrayData[i] - 1;
288 }
289 if(status != 1)
290 return 0;
291
292 auto outputPoints = vtkUnstructuredGrid::GetData(outputVector, 0);
293 auto outputMergeTreeJoin = vtkUnstructuredGrid::GetData(outputVector, 1);
294
296 triangulation->getType(),
297 getMergeTree<T0>(outputMergeTreeJoin, mergeTreeJoin, scalarArray,
298 (T0 *)triangulation->getData()));
300 triangulation->getType(),
301 getMergeTreePoints<T0>(outputPoints, cpMap, persistencePairsJoin,
302 scalarArray, (T0 *)triangulation->getData()));
303
304 } else {
305 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>>
306 persistencePairsSplit{};
307 std::vector<ttk::ExTreeM::Branch> mergeTreeSplit{};
308
309 int status = 0;
310
312 triangulation->getType(),
313 (status = exTreeMTree.computePairs<T0>(
314 persistencePairsSplit, cpMap, mergeTreeSplit,
315 ttkUtils::GetPointer<ttk::SimplexId>(segmentationId),
316 ttkUtils::GetPointer<char>(regionType),
317 ttkUtils::GetPointer<ttk::SimplexId>(descendingManifold),
318 ttkUtils::GetPointer<ttk::SimplexId>(ascendingManifold),
319 orderArrayData, (T0 *)triangulation->getData(), params_.treeType)));
320
321 if(status != 1)
322 return 0;
323 auto outputPoints = vtkUnstructuredGrid::GetData(outputVector, 0);
324 auto outputMergeTreeSplit = vtkUnstructuredGrid::GetData(outputVector, 1);
325
327 triangulation->getType(),
328 getMergeTree<T0>(outputMergeTreeSplit, mergeTreeSplit, scalarArray,
329 (T0 *)triangulation->getData()));
331 triangulation->getType(),
332 getMergeTreePoints<T0>(outputPoints, cpMap, persistencePairsSplit,
333 scalarArray, (T0 *)triangulation->getData()));
334 }
335 {
336 segmentationPD->AddArray(segmentationId);
337 segmentationPD->AddArray(regionType);
338 }
339 } else {
340 // Arrays
341
342 vtkDataArray *inputArray = this->GetInputArrayToProcess(0, inputVector);
343 if(!inputArray)
344 return 0;
345
346 // Connected components
347 if(input->IsA("vtkUnstructuredGrid")) {
348 // This data set may have several connected components,
349 // we need to apply the FTM Tree for each one of these components
350 // We then reconstruct the global tree using an offset mechanism
352 inputWithId->ShallowCopy(input);
353 identify(inputWithId);
354
355 vtkNew<vtkConnectivityFilter> connectivity{};
356 connectivity->SetInputData(inputWithId);
357 connectivity->SetExtractionModeToAllRegions();
358 connectivity->ColorRegionsOn();
359 connectivity->Update();
360
361 nbCC_ = connectivity->GetOutput()
362 ->GetCellData()
363 ->GetArray("RegionId")
364 ->GetRange()[1]
365 + 1;
367
368 if(nbCC_ > 1) {
369 // Warning, in case of several connected components, the ids seen by
370 // the base code will not be consistent with those of the original
371 // mesh
372 for(int cc = 0; cc < nbCC_; cc++) {
373 vtkNew<vtkThreshold> threshold{};
374 threshold->SetInputConnection(connectivity->GetOutputPort());
375 threshold->SetInputArrayToProcess(
376 0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_CELLS, "RegionId");
377#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 2, 0)
378 threshold->ThresholdBetween(cc, cc);
379#else
380 threshold->SetThresholdFunction(vtkThreshold::THRESHOLD_BETWEEN);
381 threshold->SetLowerThreshold(cc);
382 threshold->SetUpperThreshold(cc);
383#endif
384 threshold->Update();
387 connected_components_[cc]->ShallowCopy(threshold->GetOutput());
388 }
389 } else {
390 connected_components_[0] = inputWithId;
391 }
392 } else if(input->IsA("vtkPolyData")) {
393 // NOTE: CC check should not be implemented on a per vtk module layer.
394 nbCC_ = 1;
397 connected_components_[0]->ShallowCopy(input);
399 } else {
400 nbCC_ = 1;
403 connected_components_[0]->ShallowCopy(input);
405 }
406
407 // now proceed for each triangulation obtained.
408
409 if(preconditionTriangulation() == 0) {
410#ifndef TTK_ENABLE_KAMIKAZE
411 this->printErr("Error : wrong triangulation.");
412 return 0;
413#endif
414 }
415
416 // Fill the vector of scalar/offset, cut the array in pieces if needed
417 if(getScalars() == 0) {
418#ifndef TTK_ENABLE_KAMIKAZE
419 this->printErr("Error : wrong input scalars.");
420 return 0;
421#endif
422 }
423 getOffsets();
424
425 this->printMsg("Launching on field "
426 + std::string{inputScalars_[0]->GetName()});
427
428 ttk::ftm::idNode acc_nbNodes = 0;
429
430 // Build tree
431 for(int cc = 0; cc < nbCC_; cc++) {
432 ftmTree_[cc].tree.setVertexScalars(
434 ftmTree_[cc].tree.setVertexSoSoffsets(offsets_[cc].data());
435 ftmTree_[cc].tree.setTreeType(GetTreeType());
436 ftmTree_[cc].tree.setSegmentation(GetWithSegmentation());
437 ftmTree_[cc].tree.setNormalizeIds(GetWithNormalize());
438
439 ttkVtkTemplateMacro(inputArray->GetDataType(),
440 triangulation_[cc]->getType(),
441 (ftmTree_[cc].tree.build<VTK_TT, TTK_TT>(
442 (TTK_TT *)triangulation_[cc]->getData())));
443
444 ftmTree_[cc].offset = acc_nbNodes;
445 acc_nbNodes
446 += ftmTree_[cc].tree.getTree(GetTreeType())->getNumberOfNodes();
447 }
448
449 UpdateProgress(0.50);
450
451 // Construct output
452 if(getSkeletonNodes(outputSkeletonNodes) == 0) {
453#ifndef TTK_ENABLE_KAMIKAZE
454 this->printErr("Error : wrong properties on skeleton nodes.");
455 return 0;
456#endif
457 }
458
459 if(getSkeletonArcs(outputSkeletonArcs) == 0) {
460#ifndef TTK_ENABLE_KAMIKAZE
461 this->printErr("Error : wrong properties on skeleton arcs.");
462 return 0;
463#endif
464 }
465
466 if(GetWithSegmentation()) {
467 outputSegmentation->ShallowCopy(input);
468 if(getSegmentation(outputSegmentation) == 0) {
469#ifndef TTK_ENABLE_KAMIKAZE
470 this->printErr("Error : wrong properties on segmentation.");
471 return 0;
472#endif
473 }
474 }
475
476 UpdateProgress(1);
477
478#ifdef TTK_ENABLE_FTM_TREE_STATS_TIME
479 printCSVStats();
480#endif
481 }
482
483 return 1;
484}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
static vtkInformationIntegerKey * SAME_DATA_TYPE_AS_INPUT_PORT()
ttk::Triangulation * GetTriangulation(vtkDataSet *dataSet)
vtkDataArray * GetOrderArray(vtkDataSet *const inputData, const int scalarArrayIdx, ttk::Triangulation *triangulation, const bool getGlobalOrder=false, const int orderArrayIdx=0, const bool enforceOrderArrayIdx=false)
ttk::ftm::TreeType GetTreeType() const
std::vector< ttk::Triangulation * > triangulation_
std::vector< vtkDataArray * > inputScalars_
int getSegmentation(vtkDataSet *outputSegmentation)
ttk::ftm::Params params_
int getSkeletonNodes(vtkUnstructuredGrid *outputSkeletonNodes)
std::vector< std::vector< ttk::SimplexId > > offsets_
int getSkeletonArcs(vtkUnstructuredGrid *outputSkeletonArcs)
void identify(vtkDataSet *ds) const
std::vector< vtkSmartPointer< vtkDataSet > > connected_components_
std::vector< ttk::ftm::LocalFTM > ftmTree_
TTK filter for the computation of merge trees.
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
int preconditionTriangulation()
int FillOutputPortInformation(int port, vtkInformation *info) override
int FillInputPortInformation(int port, vtkInformation *info) override
bool GetWithNormalize() const
bool GetWithSegmentation() const
static void * GetVoidPointer(vtkDataArray *array, vtkIdType start=0)
Definition ttkUtils.cpp:226
virtual int setThreadNumber(const int threadNumber)
Definition BaseClass.h:80
int debugLevel_
Definition Debug.h:379
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
virtual int setDebugLevel(const int &debugLevel)
Definition Debug.cpp:147
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
TTK processing package for the computation of Morse-Smale segmentations using Path Compression.
int execute(OutputSegmentation &outSegmentation, const SimplexId *const orderArray, const triangulationType &triangulation)
Main function for computing the Morse-Smale complex.
void setComputeSegmentation(const bool doAscending, const bool doDescending, const bool doMorseSmale)
std::string to_string(__int128)
Definition ripserpy.cpp:99
unsigned int idNode
Node index in vect_nodes_.
const char MorseSmaleAscendingName[]
Definition DataTypes.h:62
const char MorseSmaleDescendingName[]
Definition DataTypes.h:63
Pointers to pre-allocated segmentation point data arrays.
#define ttkTypeMacroT(group, call)
Definition ttkMacros.h:140
#define ttkVtkTemplateMacro(dataType, triangulationType, call)
Definition ttkMacros.h:69
vtkStandardNewMacro(ttkMergeTree)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)