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,
46 const bool doPostProc,
47 const std::vector<std::set<int>> &trackingTupleToMerged,
49 vtkUnstructuredGrid *persistenceDiagram,
50 vtkDoubleArray *persistenceScalars,
51 vtkDoubleArray *valueScalars,
52 vtkIntArray *matchingIdScalars,
53 vtkIntArray *lengthScalars,
54 vtkIntArray *timeScalars,
55 vtkIntArray *componentIds,
56 vtkIntArray *pointTypeScalars,
60 int currentVertex = 0;
61 for(
size_t k = 0; k < trackings.size(); ++k) {
64 int const numStart = std::get<0>(tt);
66 const std::vector<ttk::SimplexId> &chain = std::get<2>(tt);
67 int const chainLength = chain.size();
69 if(chainLength <= 1) {
70 dbg.
printErr(
"Got an unexpected 0-size chain.");
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;
78 const auto &diagram1 = inputPersistenceDiagrams[d1id];
79 const auto &diagram2 = inputPersistenceDiagrams[d2id];
83 const auto n1 = chain[c];
84 const auto n2 = chain[c + 1];
88 for(
const auto &tup : matchings1) {
89 auto d1id1 = (int)std::get<0>(tup);
91 cost = std::get<2>(tup);
96 const auto &pair0 = diagram1[n1];
97 const auto &pair1 = diagram2[n2];
99 double x1, y1, z1, x2, y2, z2;
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);
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);
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);
140 bool hasMergedFirst =
false;
142 const auto &connected = trackingTupleToMerged[k];
143 if(!connected.empty()) {
144 int const min = *(connected.begin());
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));
152 hasMergedFirst = numStart + c <= numEnd2 + 3;
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];
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);
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);
191 x1 = t12Max ? pairN.death.coords[0]
193 ? pairN.birth.coords[0]
194 : (pairN.birth.coords[0] + pairN.death.coords[0]) / 2.0;
195 y1 = t12Max ? pairN.death.coords[1]
197 ? pairN.birth.coords[1]
198 : (pairN.birth.coords[1] + pairN.death.coords[1]) / 2.0;
199 z1 = t12Max ? pairN.death.coords[2]
201 ? pairN.birth.coords[2]
202 : (pairN.birth.coords[2] + pairN.death.coords[2]) / 2.0;
203 if(useGeometricSpacing)
204 z1 += spacing * (numStart + c);
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);
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;
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);
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);
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);
263 componentIds->InsertTuple1(ids[1], cid);
265 persistenceDiagram->InsertNextCell(VTK_LINE, 2, ids);
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);
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);
291 vtkInformationVector **inputVector,
292 vtkInformationVector *outputVector) {
295 vtkUnstructuredGrid *mesh = vtkUnstructuredGrid::GetData(outputVector, 0);
298 int const numInputs = inputVector[0]->GetNumberOfInformationObjects();
299 this->
printMsg(
"Number of inputs: " + std::to_string(numInputs));
302 std::vector<vtkUnstructuredGrid *> inputVTUs(numInputs);
303 for(
int i = 0; i < numInputs; i++) {
304 inputVTUs[i] = vtkUnstructuredGrid::GetData(inputVector[0], i);
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);
312 double const spacing = Spacing;
313 std::string
const algorithm = DistanceAlgorithm;
314 double const tolerance = Tolerance;
315 std::string
const wasserstein = WassersteinMetric;
318 for(
int i = 0; i < numInputs; ++i) {
319 VTUToDiagram(inputPersistenceDiagrams[i], inputVTUs[i], *
this);
323 numInputs, inputPersistenceDiagrams, outputMatchings,
325 wasserstein, tolerance, PX, PY, PZ, PS, PE
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]);
335 outputMatchings[i], outputDiags[2 * i + 0], outputDiags[2 * i + 1]);
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");
349 if(grid->GetCellData()->GetArray(
"Persistence")->GetDataType()
352 ->GetArray(
"Persistence")
354 this->
printErr(
"Inputs of different data types");
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.");
370 vtkUnstructuredGrid *outputMesh = vtkUnstructuredGrid::SafeDownCast(mesh);
372 vtkNew<vtkPoints>
const points{};
373 vtkNew<vtkUnstructuredGrid>
const persistenceDiagram{};
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");
391 std::vector<ttk::trackingTuple>
394 inputPersistenceDiagrams, outputMatchings, trackingsBase);
396 std::vector<std::set<int>> trackingTupleToMerged(trackingsBase.size());
399 trackingTupleToMerged, PostProcThresh);
402 bool const useGeometricSpacing = UseGeometricSpacing;
410 buildMesh(trackingsBase, outputMatchings, inputPersistenceDiagrams,
411 useGeometricSpacing, spacing, DoPostProc, trackingTupleToMerged,
412 points, persistenceDiagram, persistenceScalars, valueScalars,
413 matchingIdScalars, lengthScalars, timeScalars, componentIds,
414 pointTypeScalars, *
this);
416 outputMesh->ShallowCopy(persistenceDiagram);