31 const std::vector<ttk::trackingTuple> &trackings,
32 const std::vector<std::vector<ttk::MatchingType>> &outputMatchings,
33 const std::vector<ttk::DiagramType> &inputPersistenceDiagrams,
34 const bool useGeometricSpacing,
36 const bool doPostProc,
37 const std::vector<std::set<int>> &trackingTupleToMerged,
39 vtkUnstructuredGrid *persistenceDiagram,
40 vtkDoubleArray *persistenceScalars,
41 vtkDoubleArray *valueScalars,
42 vtkIntArray *matchingIdScalars,
43 vtkIntArray *lengthScalars,
44 vtkIntArray *timeScalars,
45 vtkIntArray *componentIds,
46 vtkIntArray *pointTypeScalars,
49 int currentVertex = 0;
50 for(
size_t k = 0; k < trackings.size(); ++k) {
53 int const numStart = std::get<0>(tt);
55 const std::vector<ttk::SimplexId> &chain = std::get<2>(tt);
56 int const chainLength = chain.size();
58 if(chainLength <= 1) {
59 dbg.
printErr(
"Got an unexpected 0-size chain.");
63 for(
int c = 0; c < chainLength - 1; ++c) {
64 const auto &matchings1 = outputMatchings[numStart + c];
65 int const d1id = numStart + c;
66 int const d2id = d1id + 1;
67 const auto &diagram1 = inputPersistenceDiagrams[d1id];
68 const auto &diagram2 = inputPersistenceDiagrams[d2id];
72 const auto n1 = chain[c];
73 const auto n2 = chain[c + 1];
77 for(
const auto &tup : matchings1) {
78 auto d1id1 = (int)std::get<0>(tup);
80 cost = std::get<2>(tup);
85 const auto &pair0 = diagram1[n1];
86 const auto &pair1 = diagram2[n2];
88 double x1, y1, z1, x2, y2, z2;
90 auto point1Type1 = pair0.birth.type;
91 auto point1Type2 = pair0.death.type;
92 auto point1Type = point1Type1 == CriticalType::Local_maximum
93 || point1Type2 == CriticalType::Local_maximum
94 ? CriticalType::Local_maximum
95 : point1Type1 == CriticalType::Local_minimum
96 || point1Type2 == CriticalType::Local_minimum
97 ? CriticalType::Local_minimum
98 : point1Type1 == CriticalType::Saddle2
99 || point1Type2 == CriticalType::Saddle2
100 ? CriticalType::Saddle2
101 : CriticalType::Saddle1;
102 bool t11Min = point1Type1 == CriticalType::Local_minimum;
103 bool t11Max = point1Type1 == CriticalType::Local_maximum;
104 bool t12Min = point1Type2 == CriticalType::Local_minimum;
105 bool t12Max = point1Type2 == CriticalType::Local_maximum;
106 bool bothEx1 = (t11Min && t12Max) || (t11Max && t12Min);
108 x1 = t12Max ? pair0.death.coords[0] : pair0.birth.coords[0];
109 y1 = t12Max ? pair0.death.coords[1] : pair0.birth.coords[1];
110 z1 = t12Max ? pair0.death.coords[2] : pair0.birth.coords[2];
111 if(useGeometricSpacing)
112 z1 += spacing * (numStart + c);
114 x1 = t12Max ? pair0.death.coords[0]
115 : t11Min ? pair0.birth.coords[0]
116 : (pair0.death.coords[0] + pair0.birth.coords[0]) / 2.0;
117 y1 = t12Max ? pair0.death.coords[1]
118 : t11Min ? pair0.birth.coords[1]
119 : (pair0.death.coords[1] + pair0.birth.coords[1]) / 2.0;
120 z1 = t12Max ? pair0.death.coords[2]
121 : t11Min ? pair0.birth.coords[2]
122 : (pair0.death.coords[2] + pair0.birth.coords[2]) / 2.0;
123 if(useGeometricSpacing)
124 z1 += spacing * (numStart + c);
129 bool hasMergedFirst =
false;
131 const auto &connected = trackingTupleToMerged[k];
132 if(!connected.empty()) {
133 int const min = *(connected.begin());
136 int const numEnd2 = std::get<1>(ttt);
137 if((numEnd2 > 0 && numStart + c > numEnd2 + 1) && min < (
int)k) {
138 dbg.
printMsg(
"Switched " + std::to_string(k) +
" for "
139 + std::to_string(min));
141 hasMergedFirst = numStart + c <= numEnd2 + 3;
149 const std::vector<ttk::SimplexId> &chain3 = std::get<2>(ttt);
150 const auto nn = chain3.back();
151 const auto &diagramRematch = inputPersistenceDiagrams[numEnd2];
152 const auto &pairN = diagramRematch[nn];
154 point1Type1 = pairN.birth.type;
155 point1Type2 = pairN.death.type;
156 point1Type = point1Type1 == CriticalType::Local_maximum
157 || point1Type2 == CriticalType::Local_maximum
158 ? CriticalType::Local_maximum
159 : point1Type1 == CriticalType::Local_minimum
160 || point1Type2 == CriticalType::Local_minimum
161 ? CriticalType::Local_minimum
162 : point1Type1 == CriticalType::Saddle2
163 || point1Type2 == CriticalType::Saddle2
164 ? CriticalType::Saddle2
165 : CriticalType::Saddle1;
166 t11Min = point1Type1 == CriticalType::Local_minimum;
167 t11Max = point1Type1 == CriticalType::Local_maximum;
168 t12Min = point1Type2 == CriticalType::Local_minimum;
169 t12Max = point1Type2 == CriticalType::Local_maximum;
170 bothEx1 = (t11Min && t12Max) || (t11Max && t12Min);
174 x1 = t12Max ? pairN.death.coords[0] : pairN.birth.coords[0];
175 y1 = t12Max ? pairN.death.coords[1] : pairN.birth.coords[1];
176 z1 = t12Max ? pairN.death.coords[2] : pairN.birth.coords[2];
177 if(useGeometricSpacing)
178 z1 += spacing * (numStart + c);
180 x1 = t12Max ? pairN.death.coords[0]
182 ? pairN.birth.coords[0]
183 : (pairN.birth.coords[0] + pairN.death.coords[0]) / 2.0;
184 y1 = t12Max ? pairN.death.coords[1]
186 ? pairN.birth.coords[1]
187 : (pairN.birth.coords[1] + pairN.death.coords[1]) / 2.0;
188 z1 = t12Max ? pairN.death.coords[2]
190 ? pairN.birth.coords[2]
191 : (pairN.birth.coords[2] + pairN.death.coords[2]) / 2.0;
192 if(useGeometricSpacing)
193 z1 += spacing * (numStart + c);
201 points->InsertNextPoint(x1, y1, z1);
202 ids[0] = 2 * currentVertex;
203 pointTypeScalars->InsertTuple1(ids[0], (
double)(
int)point1Type);
204 timeScalars->InsertTuple1(ids[0], (
double)numStart + c);
205 componentIds->InsertTuple1(ids[0], cid);
207 const auto point2Type1 = pair1.birth.type;
208 const auto point2Type2 = pair1.death.type;
209 const auto point2Type = point2Type1 == CriticalType::Local_maximum
210 || point2Type2 == CriticalType::Local_maximum
211 ? CriticalType::Local_maximum
212 : point2Type1 == CriticalType::Local_minimum
213 || point2Type2 == CriticalType::Local_minimum
214 ? CriticalType::Local_minimum
215 : point2Type1 == CriticalType::Saddle2
216 || point2Type2 == CriticalType::Saddle2
217 ? CriticalType::Saddle2
218 : CriticalType::Saddle1;
219 bool const t21Ex = point2Type1 == CriticalType::Local_minimum
220 || point2Type1 == CriticalType::Local_maximum;
221 bool const t22Ex = point2Type2 == CriticalType::Local_minimum
222 || point2Type2 == CriticalType::Local_maximum;
223 bool const bothEx2 = t21Ex && t22Ex;
225 x2 = point2Type2 == CriticalType::Local_maximum ? pair1.death.coords[0]
226 : pair1.birth.coords[0];
227 y2 = point2Type2 == CriticalType::Local_maximum ? pair1.death.coords[1]
228 : pair1.birth.coords[1];
229 z2 = point2Type2 == CriticalType::Local_maximum ? pair1.death.coords[2]
230 : pair1.birth.coords[2];
231 if(useGeometricSpacing)
232 z2 += spacing * (numStart + c + 1);
234 x2 = t22Ex ? pair1.death.coords[0]
235 : t21Ex ? pair1.birth.coords[0]
236 : (pair1.birth.coords[0] + pair1.death.coords[0]) / 2;
237 y2 = t22Ex ? pair1.death.coords[1]
238 : t21Ex ? pair1.birth.coords[1]
239 : (pair1.birth.coords[1] + pair1.death.coords[1]) / 2;
240 z2 = t22Ex ? pair1.death.coords[2]
241 : t21Ex ? pair1.birth.coords[2]
242 : (pair1.birth.coords[2] + pair1.death.coords[2]) / 2;
243 if(useGeometricSpacing)
244 z2 += spacing * (numStart + c + 1);
246 points->InsertNextPoint(x2, y2, z2);
247 ids[1] = 2 * currentVertex + 1;
248 pointTypeScalars->InsertTuple1(ids[1], (
double)(
int)point2Type);
249 timeScalars->InsertTuple1(ids[1], (
double)numStart + c);
252 componentIds->InsertTuple1(ids[1], cid);
254 persistenceDiagram->InsertNextCell(VTK_LINE, 2, ids);
256 persistenceScalars->InsertTuple1(currentVertex, cost);
257 valueScalars->InsertTuple1(
258 currentVertex, (pair0.death.sfValue + pair1.death.sfValue) / 2);
259 matchingIdScalars->InsertTuple1(currentVertex, currentVertex);
260 lengthScalars->InsertTuple1(currentVertex, chainLength);
266 persistenceDiagram->SetPoints(points);
267 persistenceDiagram->GetCellData()->AddArray(persistenceScalars);
268 persistenceDiagram->GetCellData()->AddArray(valueScalars);
269 persistenceDiagram->GetCellData()->AddArray(matchingIdScalars);
270 persistenceDiagram->GetCellData()->AddArray(lengthScalars);
271 persistenceDiagram->GetPointData()->AddArray(timeScalars);
272 persistenceDiagram->GetPointData()->AddArray(componentIds);
273 persistenceDiagram->GetPointData()->AddArray(pointTypeScalars);
280 vtkInformationVector **inputVector,
281 vtkInformationVector *outputVector) {
284 vtkUnstructuredGrid *mesh = vtkUnstructuredGrid::GetData(outputVector, 0);
287 int const numInputs = inputVector[0]->GetNumberOfInformationObjects();
288 this->
printMsg(
"Number of inputs: " + std::to_string(numInputs));
291 std::vector<vtkUnstructuredGrid *> inputVTUs(numInputs);
292 for(
int i = 0; i < numInputs; i++) {
293 inputVTUs[i] = vtkUnstructuredGrid::GetData(inputVector[0], i);
296 std::vector<ttk::DiagramType> inputPersistenceDiagrams(numInputs);
297 std::vector<vtkNew<vtkUnstructuredGrid>> outputDiags(2 * numInputs - 2);
298 std::vector<std::vector<ttk::MatchingType>> outputMatchings(numInputs - 1);
301 double const spacing = Spacing;
302 std::string
const algorithm = DistanceAlgorithm;
303 double const tolerance = Tolerance;
304 std::string
const wasserstein = WassersteinMetric;
307 for(
int i = 0; i < numInputs; ++i) {
308 VTUToDiagram(inputPersistenceDiagrams[i], inputVTUs[i], *
this);
312 numInputs, inputPersistenceDiagrams, outputMatchings,
314 wasserstein, tolerance, PX, PY, PZ, PS, PE
319 for(
int i = 0; i < numInputs - 1; ++i) {
320 outputDiags[2 * i + 0]->ShallowCopy(inputVTUs[i]);
321 outputDiags[2 * i + 1]->ShallowCopy(inputVTUs[i + 1]);
324 outputMatchings[i], outputDiags[2 * i + 0], outputDiags[2 * i + 1]);
329 for(
int i = 0; i < numInputs; ++i) {
330 const auto &grid = outputDiags[i];
331 if(!grid || !grid->GetCellData()
332 || !grid->GetCellData()->GetArray(
"Persistence")) {
333 this->
printErr(
"Inputs are not persistence diagrams");
338 if(grid->GetCellData()->GetArray(
"Persistence")->GetDataType()
341 ->GetArray(
"Persistence")
343 this->
printErr(
"Inputs of different data types");
348 for(
int i = 0; i < numInputs - 1; ++i) {
349 const auto &grid1 = outputDiags[i];
350 const auto &grid2 = outputDiags[i + 1];
351 if(i % 2 == 1 && i < numInputs - 1
352 && grid1->GetCellData()->GetNumberOfTuples()
353 != grid2->GetCellData()->GetNumberOfTuples()) {
354 this->
printErr(
"Inconsistent length or order of input diagrams.");
359 vtkUnstructuredGrid *outputMesh = vtkUnstructuredGrid::SafeDownCast(mesh);
361 vtkNew<vtkPoints>
const points{};
362 vtkNew<vtkUnstructuredGrid>
const persistenceDiagram{};
364 vtkNew<vtkDoubleArray> persistenceScalars{};
365 vtkNew<vtkDoubleArray> valueScalars{};
366 vtkNew<vtkIntArray> matchingIdScalars{};
367 vtkNew<vtkIntArray> lengthScalars{};
368 vtkNew<vtkIntArray> timeScalars{};
369 vtkNew<vtkIntArray> componentIds{};
370 vtkNew<vtkIntArray> pointTypeScalars{};
371 persistenceScalars->SetName(
"Cost");
372 valueScalars->SetName(
"Scalar");
373 matchingIdScalars->SetName(
"MatchingIdentifier");
374 lengthScalars->SetName(
"ComponentLength");
375 timeScalars->SetName(
"TimeStep");
376 componentIds->SetName(
"ConnectedComponentId");
377 pointTypeScalars->SetName(
"CriticalType");
380 std::vector<ttk::trackingTuple>
383 inputPersistenceDiagrams, outputMatchings, trackingsBase);
385 std::vector<std::set<int>> trackingTupleToMerged(trackingsBase.size());
388 trackingTupleToMerged, PostProcThresh);
391 bool const useGeometricSpacing = UseGeometricSpacing;
399 buildMesh(trackingsBase, outputMatchings, inputPersistenceDiagrams,
400 useGeometricSpacing, spacing, DoPostProc, trackingTupleToMerged,
401 points, persistenceDiagram, persistenceScalars, valueScalars,
402 matchingIdScalars, lengthScalars, timeScalars, componentIds,
403 pointTypeScalars, *
this);
405 outputMesh->ShallowCopy(persistenceDiagram);