TTK
Loading...
Searching...
No Matches
ttkTrackingFromPersistenceDiagrams.cpp
Go to the documentation of this file.
2#include <ttkMacros.h>
4
5#include <vtkDataArray.h>
6#include <vtkDoubleArray.h>
7#include <vtkFloatArray.h>
8#include <vtkInformation.h>
9#include <vtkInformationVector.h>
10#include <vtkIntArray.h>
11#include <vtkObjectFactory.h>
12#include <vtkPointData.h>
13#include <vtkUnstructuredGrid.h>
14
16
18 SetNumberOfInputPorts(1);
19 SetNumberOfOutputPorts(1);
20}
21
23 int port, vtkInformation *info) {
24 if(port == 0) {
25 info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
26 return 1;
27 }
28 return 0;
29}
30
32 int port, vtkInformation *info) {
33 if(port == 0) {
34 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkUnstructuredGrid");
35 return 1;
36 }
37 return 0;
38}
39
41 const std::vector<ttk::trackingTuple> &trackings,
42 const std::vector<std::vector<ttk::MatchingType>> &outputMatchings,
43 const std::vector<ttk::DiagramType> &inputPersistenceDiagrams,
44 const bool useGeometricSpacing,
45 const double spacing,
46 const bool doPostProc,
47 const std::vector<std::set<int>> &trackingTupleToMerged,
48 vtkPoints *points,
49 vtkUnstructuredGrid *persistenceDiagram,
50 vtkDoubleArray *persistenceScalars,
51 vtkDoubleArray *valueScalars,
52 vtkIntArray *matchingIdScalars,
53 vtkIntArray *lengthScalars,
54 vtkIntArray *timeScalars,
55 vtkIntArray *componentIds,
56 vtkIntArray *pointTypeScalars,
57 const ttk::Debug &dbg) {
58
60 int currentVertex = 0;
61 for(size_t k = 0; k < trackings.size(); ++k) {
62 const ttk::trackingTuple &tt = trackings[k];
63
64 int const numStart = std::get<0>(tt);
65 // int numEnd = std::get<1>(tt);
66 const std::vector<ttk::SimplexId> &chain = std::get<2>(tt);
67 int const chainLength = chain.size();
68
69 if(chainLength <= 1) {
70 dbg.printErr("Got an unexpected 0-size chain.");
71 return 0;
72 }
73
74 for(int c = 0; c < chainLength - 1; ++c) {
75 const auto &matchings1 = outputMatchings[numStart + c];
76 int const d1id = numStart + c;
77 int const d2id = d1id + 1; // c % 2 == 0 ? d1id + 1 : d1id;
78 const auto &diagram1 = inputPersistenceDiagrams[d1id];
79 const auto &diagram2 = inputPersistenceDiagrams[d2id];
80
81 // Insert segments
82 vtkIdType ids[2];
83 const auto n1 = chain[c];
84 const auto n2 = chain[c + 1];
85
86 // Search for right matching.
87 double cost = 0.0;
88 for(const auto &tup : matchings1) {
89 auto d1id1 = (int)std::get<0>(tup);
90 if(d1id1 == n1) {
91 cost = std::get<2>(tup);
92 break;
93 }
94 }
95
96 const auto &pair0 = diagram1[n1];
97 const auto &pair1 = diagram2[n2];
98
99 double x1, y1, z1, x2, y2, z2;
100
101 auto point1Type1 = pair0.birth.type;
102 auto point1Type2 = pair0.death.type;
103 auto point1Type = point1Type1 == CriticalType::Local_maximum
104 || point1Type2 == CriticalType::Local_maximum
105 ? CriticalType::Local_maximum
106 : point1Type1 == CriticalType::Local_minimum
107 || point1Type2 == CriticalType::Local_minimum
108 ? CriticalType::Local_minimum
109 : point1Type1 == CriticalType::Saddle2
110 || point1Type2 == CriticalType::Saddle2
111 ? CriticalType::Saddle2
112 : CriticalType::Saddle1;
113 bool t11Min = point1Type1 == CriticalType::Local_minimum;
114 bool t11Max = point1Type1 == CriticalType::Local_maximum;
115 bool t12Min = point1Type2 == CriticalType::Local_minimum;
116 bool t12Max = point1Type2 == CriticalType::Local_maximum;
117 bool bothEx1 = (t11Min && t12Max) || (t11Max && t12Min);
118 if(bothEx1) {
119 x1 = t12Max ? pair0.death.coords[0] : pair0.birth.coords[0];
120 y1 = t12Max ? pair0.death.coords[1] : pair0.birth.coords[1];
121 z1 = t12Max ? pair0.death.coords[2] : pair0.birth.coords[2];
122 if(useGeometricSpacing)
123 z1 += spacing * (numStart + c);
124 } else {
125 x1 = t12Max ? pair0.death.coords[0]
126 : t11Min ? pair0.birth.coords[0]
127 : (pair0.death.coords[0] + pair0.birth.coords[0]) / 2.0;
128 y1 = t12Max ? pair0.death.coords[1]
129 : t11Min ? pair0.birth.coords[1]
130 : (pair0.death.coords[1] + pair0.birth.coords[1]) / 2.0;
131 z1 = t12Max ? pair0.death.coords[2]
132 : t11Min ? pair0.birth.coords[2]
133 : (pair0.death.coords[2] + pair0.birth.coords[2]) / 2.0;
134 if(useGeometricSpacing)
135 z1 += spacing * (numStart + c);
136 }
137
138 // Postproc component ids.
139 int cid = k;
140 bool hasMergedFirst = false;
141 if(doPostProc) {
142 const auto &connected = trackingTupleToMerged[k];
143 if(!connected.empty()) {
144 int const min = *(connected.begin());
145 const ttk::trackingTuple &ttt = trackings[min];
146 // int numStart2 = std::get<0>(ttt);
147 int const numEnd2 = std::get<1>(ttt);
148 if((numEnd2 > 0 && numStart + c > numEnd2 + 1) && min < (int)k) {
149 dbg.printMsg("Switched " + std::to_string(k) + " for "
150 + std::to_string(min));
151 cid = min;
152 hasMergedFirst = numStart + c <= numEnd2 + 3;
153 }
154
155 if(hasMergedFirst) {
156 // std::cout << "Has merged first " << std::endl;
157
158 // Replace former first end of the segment with previous ending
159 // segment.
160 const std::vector<ttk::SimplexId> &chain3 = std::get<2>(ttt);
161 const auto nn = chain3.back();
162 const auto &diagramRematch = inputPersistenceDiagrams[numEnd2];
163 const auto &pairN = diagramRematch[nn];
164
165 point1Type1 = pairN.birth.type;
166 point1Type2 = pairN.death.type;
167 point1Type = point1Type1 == CriticalType::Local_maximum
168 || point1Type2 == CriticalType::Local_maximum
169 ? CriticalType::Local_maximum
170 : point1Type1 == CriticalType::Local_minimum
171 || point1Type2 == CriticalType::Local_minimum
172 ? CriticalType::Local_minimum
173 : point1Type1 == CriticalType::Saddle2
174 || point1Type2 == CriticalType::Saddle2
175 ? CriticalType::Saddle2
176 : CriticalType::Saddle1;
177 t11Min = point1Type1 == CriticalType::Local_minimum;
178 t11Max = point1Type1 == CriticalType::Local_maximum;
179 t12Min = point1Type2 == CriticalType::Local_minimum;
180 t12Max = point1Type2 == CriticalType::Local_maximum;
181 bothEx1 = (t11Min && t12Max) || (t11Max && t12Min);
182 // std::cout << "xyz " << x1 << ", " << y1 << ", " << z1 <<
183 // std::endl;
184 if(bothEx1) {
185 x1 = t12Max ? pairN.death.coords[0] : pairN.birth.coords[0];
186 y1 = t12Max ? pairN.death.coords[1] : pairN.birth.coords[1];
187 z1 = t12Max ? pairN.death.coords[2] : pairN.birth.coords[2];
188 if(useGeometricSpacing)
189 z1 += spacing * (numStart + c);
190 } else {
191 x1 = t12Max ? pairN.death.coords[0]
192 : t11Min
193 ? pairN.birth.coords[0]
194 : (pairN.birth.coords[0] + pairN.death.coords[0]) / 2.0;
195 y1 = t12Max ? pairN.death.coords[1]
196 : t11Min
197 ? pairN.birth.coords[1]
198 : (pairN.birth.coords[1] + pairN.death.coords[1]) / 2.0;
199 z1 = t12Max ? pairN.death.coords[2]
200 : t11Min
201 ? pairN.birth.coords[2]
202 : (pairN.birth.coords[2] + pairN.death.coords[2]) / 2.0;
203 if(useGeometricSpacing)
204 z1 += spacing * (numStart + c);
205 }
206 // std::cout << "xyz " << x1 << ", " << y1 << ", " << z1 <<
207 // std::endl;
208 }
209 }
210 }
211
212 points->InsertNextPoint(x1, y1, z1);
213 ids[0] = 2 * currentVertex;
214 pointTypeScalars->InsertTuple1(ids[0], (double)(int)point1Type);
215 timeScalars->InsertTuple1(ids[0], (double)numStart + c);
216 componentIds->InsertTuple1(ids[0], cid);
217
218 const auto point2Type1 = pair1.birth.type;
219 const auto point2Type2 = pair1.death.type;
220 const auto point2Type = point2Type1 == CriticalType::Local_maximum
221 || point2Type2 == CriticalType::Local_maximum
222 ? CriticalType::Local_maximum
223 : point2Type1 == CriticalType::Local_minimum
224 || point2Type2 == CriticalType::Local_minimum
225 ? CriticalType::Local_minimum
226 : point2Type1 == CriticalType::Saddle2
227 || point2Type2 == CriticalType::Saddle2
228 ? CriticalType::Saddle2
229 : CriticalType::Saddle1;
230 bool const t21Ex = point2Type1 == CriticalType::Local_minimum
231 || point2Type1 == CriticalType::Local_maximum;
232 bool const t22Ex = point2Type2 == CriticalType::Local_minimum
233 || point2Type2 == CriticalType::Local_maximum;
234 bool const bothEx2 = t21Ex && t22Ex;
235 if(bothEx2) {
236 x2 = point2Type2 == CriticalType::Local_maximum ? pair1.death.coords[0]
237 : pair1.birth.coords[0];
238 y2 = point2Type2 == CriticalType::Local_maximum ? pair1.death.coords[1]
239 : pair1.birth.coords[1];
240 z2 = point2Type2 == CriticalType::Local_maximum ? pair1.death.coords[2]
241 : pair1.birth.coords[2];
242 if(useGeometricSpacing)
243 z2 += spacing * (numStart + c + 1);
244 } else {
245 x2 = t22Ex ? pair1.death.coords[0]
246 : t21Ex ? pair1.birth.coords[0]
247 : (pair1.birth.coords[0] + pair1.death.coords[0]) / 2;
248 y2 = t22Ex ? pair1.death.coords[1]
249 : t21Ex ? pair1.birth.coords[1]
250 : (pair1.birth.coords[1] + pair1.death.coords[1]) / 2;
251 z2 = t22Ex ? pair1.death.coords[2]
252 : t21Ex ? pair1.birth.coords[2]
253 : (pair1.birth.coords[2] + pair1.death.coords[2]) / 2;
254 if(useGeometricSpacing)
255 z2 += spacing * (numStart + c + 1);
256 }
257 points->InsertNextPoint(x2, y2, z2);
258 ids[1] = 2 * currentVertex + 1;
259 pointTypeScalars->InsertTuple1(ids[1], (double)(int)point2Type);
260 timeScalars->InsertTuple1(ids[1], (double)numStart + c);
261
262 // Postproc component ids.
263 componentIds->InsertTuple1(ids[1], cid);
264
265 persistenceDiagram->InsertNextCell(VTK_LINE, 2, ids);
266
267 persistenceScalars->InsertTuple1(currentVertex, cost);
268 valueScalars->InsertTuple1(
269 currentVertex, (pair0.death.sfValue + pair1.death.sfValue) / 2);
270 matchingIdScalars->InsertTuple1(currentVertex, currentVertex);
271 lengthScalars->InsertTuple1(currentVertex, chainLength);
272
273 currentVertex++;
274 }
275 }
276
277 persistenceDiagram->SetPoints(points);
278 persistenceDiagram->GetCellData()->AddArray(persistenceScalars);
279 persistenceDiagram->GetCellData()->AddArray(valueScalars);
280 persistenceDiagram->GetCellData()->AddArray(matchingIdScalars);
281 persistenceDiagram->GetCellData()->AddArray(lengthScalars);
282 persistenceDiagram->GetPointData()->AddArray(timeScalars);
283 persistenceDiagram->GetPointData()->AddArray(componentIds);
284 persistenceDiagram->GetPointData()->AddArray(pointTypeScalars);
285
286 return 0;
287}
288
290 vtkInformation *ttkNotUsed(request),
291 vtkInformationVector **inputVector,
292 vtkInformationVector *outputVector) {
293
294 // Unified bound fields
295 vtkUnstructuredGrid *mesh = vtkUnstructuredGrid::GetData(outputVector, 0);
296
297 // Number of input files
298 int const numInputs = inputVector[0]->GetNumberOfInformationObjects();
299 this->printMsg("Number of inputs: " + std::to_string(numInputs));
300
301 // Get input data
302 std::vector<vtkUnstructuredGrid *> inputVTUs(numInputs);
303 for(int i = 0; i < numInputs; i++) {
304 inputVTUs[i] = vtkUnstructuredGrid::GetData(inputVector[0], i);
305 }
306
307 std::vector<ttk::DiagramType> inputPersistenceDiagrams(numInputs);
308 std::vector<vtkNew<vtkUnstructuredGrid>> outputDiags(2 * numInputs - 2);
309 std::vector<std::vector<ttk::MatchingType>> outputMatchings(numInputs - 1);
310
311 // Input parameters.
312 double const spacing = Spacing;
313 std::string const algorithm = DistanceAlgorithm;
314 double const tolerance = Tolerance;
315 std::string const wasserstein = WassersteinMetric;
316
317 // Transform inputs into the right structure.
318 for(int i = 0; i < numInputs; ++i) {
319 VTUToDiagram(inputPersistenceDiagrams[i], inputVTUs[i], *this);
320 }
321
322 this->performMatchings(
323 numInputs, inputPersistenceDiagrams, outputMatchings,
324 algorithm, // Not from paraview, from enclosing tracking plugin
325 wasserstein, tolerance, PX, PY, PZ, PS, PE // Coefficients
326 );
327
328 // Get back meshes.
329 // #pragma omp parallel for num_threads(ThreadNumber)
330 for(int i = 0; i < numInputs - 1; ++i) {
331 outputDiags[2 * i + 0]->ShallowCopy(inputVTUs[i]);
332 outputDiags[2 * i + 1]->ShallowCopy(inputVTUs[i + 1]);
333
334 int const status = augmentDiagrams(
335 outputMatchings[i], outputDiags[2 * i + 0], outputDiags[2 * i + 1]);
336 if(status < 0)
337 return -2;
338 }
339
340 for(int i = 0; i < numInputs; ++i) {
341 const auto &grid = outputDiags[i];
342 if(!grid || !grid->GetCellData()
343 || !grid->GetCellData()->GetArray("Persistence")) {
344 this->printErr("Inputs are not persistence diagrams");
345 return 0;
346 }
347
348 // Check if inputs have the same data type and the same number of points
349 if(grid->GetCellData()->GetArray("Persistence")->GetDataType()
350 != outputDiags[0]
351 ->GetCellData()
352 ->GetArray("Persistence")
353 ->GetDataType()) {
354 this->printErr("Inputs of different data types");
355 return 0;
356 }
357 }
358
359 for(int i = 0; i < numInputs - 1; ++i) {
360 const auto &grid1 = outputDiags[i];
361 const auto &grid2 = outputDiags[i + 1];
362 if(i % 2 == 1 && i < numInputs - 1
363 && grid1->GetCellData()->GetNumberOfTuples()
364 != grid2->GetCellData()->GetNumberOfTuples()) {
365 this->printErr("Inconsistent length or order of input diagrams.");
366 return 0;
367 }
368 }
369
370 vtkUnstructuredGrid *outputMesh = vtkUnstructuredGrid::SafeDownCast(mesh);
371
372 vtkNew<vtkPoints> const points{};
373 vtkNew<vtkUnstructuredGrid> const persistenceDiagram{};
374
375 vtkNew<vtkDoubleArray> persistenceScalars{};
376 vtkNew<vtkDoubleArray> valueScalars{};
377 vtkNew<vtkIntArray> matchingIdScalars{};
378 vtkNew<vtkIntArray> lengthScalars{};
379 vtkNew<vtkIntArray> timeScalars{};
380 vtkNew<vtkIntArray> componentIds{};
381 vtkNew<vtkIntArray> pointTypeScalars{};
382 persistenceScalars->SetName("Cost");
383 valueScalars->SetName("Scalar");
384 matchingIdScalars->SetName("MatchingIdentifier");
385 lengthScalars->SetName("ComponentLength");
386 timeScalars->SetName("TimeStep");
387 componentIds->SetName("ConnectedComponentId");
388 pointTypeScalars->SetName("CriticalType");
389
390 // (+ vertex id)
391 std::vector<ttk::trackingTuple>
392 trackingsBase; // structure containing all trajectories
393 this->performTracking(
394 inputPersistenceDiagrams, outputMatchings, trackingsBase);
395
396 std::vector<std::set<int>> trackingTupleToMerged(trackingsBase.size());
397 if(DoPostProc)
398 this->performPostProcess(inputPersistenceDiagrams, trackingsBase,
399 trackingTupleToMerged, PostProcThresh);
400
401 // bool Is3D = true;
402 bool const useGeometricSpacing = UseGeometricSpacing;
403 // auto spacing = (float) Spacing;
404
405 // std::vector<trackingTuple> trackings = *trackingsBase;
406 // Row = iteration number
407 // Col = (id in pd 2, arrival point id)
408
409 // Build mesh.
410 buildMesh(trackingsBase, outputMatchings, inputPersistenceDiagrams,
411 useGeometricSpacing, spacing, DoPostProc, trackingTupleToMerged,
412 points, persistenceDiagram, persistenceScalars, valueScalars,
413 matchingIdScalars, lengthScalars, timeScalars, componentIds,
414 pointTypeScalars, *this);
415
416 outputMesh->ShallowCopy(persistenceDiagram);
417
418 return 1;
419}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
int FillOutputPortInformation(int port, vtkInformation *info) override
int FillInputPortInformation(int port, vtkInformation *info) override
static int buildMesh(const std::vector< ttk::trackingTuple > &trackings, const std::vector< std::vector< ttk::MatchingType > > &outputMatchings, const std::vector< ttk::DiagramType > &inputPersistenceDiagrams, const bool useGeometricSpacing, const double spacing, const bool doPostProc, const std::vector< std::set< int > > &trackingTupleToMerged, vtkPoints *points, vtkUnstructuredGrid *persistenceDiagram, vtkDoubleArray *persistenceScalars, vtkDoubleArray *valueScalars, vtkIntArray *matchingIdScalars, vtkIntArray *lengthScalars, vtkIntArray *timeScalars, vtkIntArray *componentIds, vtkIntArray *pointTypeScalars, const ttk::Debug &dbg)
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
Minimalist debugging class.
Definition Debug.h:88
int printMsg(const std::string &msg, const debug::Priority &priority=debug::Priority::INFO, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cout) const
Definition Debug.h:118
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
int performMatchings(int numInputs, std::vector< ttk::DiagramType > &inputPersistenceDiagrams, std::vector< std::vector< MatchingType > > &outputMatchings, const std::string &algorithm, const std::string &wasserstein, double tolerance, double px, double py, double pz, double ps, double pe)
int performTracking(std::vector< ttk::DiagramType > &allDiagrams, std::vector< std::vector< MatchingType > > &allMatchings, std::vector< trackingTuple > &trackings)
int performPostProcess(const std::vector< ttk::DiagramType > &allDiagrams, const std::vector< trackingTuple > &trackings, std::vector< std::set< int > > &trackingTupleToMerged, const double postProcThresh)
std::string to_string(__int128)
Definition ripserpy.cpp:99
CriticalType
default value for critical index
Definition DataTypes.h:80
std::tuple< int, int, std::vector< SimplexId > > trackingTuple
int augmentDiagrams(const std::vector< ttk::MatchingType > &matchings, vtkUnstructuredGrid *const vtu0, vtkUnstructuredGrid *const vtu1)
int VTUToDiagram(ttk::DiagramType &diagram, vtkUnstructuredGrid *vtu, const ttk::Debug &dbg)
Converts a Persistence Diagram in the VTK Unstructured Grid format (as generated by the ttkPersistenc...
vtkStandardNewMacro(ttkTrackingFromPersistenceDiagrams)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)