6#include <vtkCellData.h>
7#include <vtkDoubleArray.h>
8#include <vtkFloatArray.h>
9#include <vtkPointData.h>
10#include <vtkThreshold.h>
11#include <vtkTransform.h>
12#include <vtkTransformFilter.h>
13#include <vtkUnsignedCharArray.h>
14#include <vtkUnstructuredGrid.h>
15#include <vtkVersionMacros.h>
18 vtkUnstructuredGrid *vtu,
21 const auto pd = vtu->GetPointData();
22 const auto cd = vtu->GetCellData();
23 const auto points = vtu->GetPoints();
26 dbg.
printWrn(
"VTU diagram with NULL Point Data");
30 dbg.
printWrn(
"VTU diagram with NULL Cell Data");
33 if(points ==
nullptr) {
39 const auto pairId = ttkSimplexIdTypeArray::SafeDownCast(
48 const auto vertexId = ttkSimplexIdTypeArray::SafeDownCast(
52 const auto coords = vtkFloatArray::SafeDownCast(
55 const bool embed = coords ==
nullptr;
57 if(pairId ==
nullptr) {
58 dbg.
printWrn(
"Missing PairIdentifier cell data array");
61 if(pairType ==
nullptr) {
62 dbg.
printWrn(
"Missing PairType cell data array");
65 if(pairPers ==
nullptr) {
66 dbg.
printWrn(
"Missing Persistence cell data array");
69 if(vertexId ==
nullptr) {
70 dbg.
printWrn(
"Missing ttkVertexScalarField point data array");
73 if(critType ==
nullptr) {
74 dbg.
printWrn(
"Missing CriticalType point data array");
77 if(birthScalars ==
nullptr) {
78 dbg.
printWrn(
"Missing Birth cell data array");
81 if(isFinite ==
nullptr) {
82 dbg.
printWrn(
"Missing IsFinite cell data array");
86 int nPairs = pairId->GetNumberOfTuples();
89 for(
int i = 0; i < nPairs; i++) {
90 if(pairId->GetTuple1(i) != -1) {
91 pairId->SetTuple1(i, i);
99 dbg.
printWrn(
"Diagram has no pairs");
103 diagram.resize(nPairs);
106 for(
int i = 0; i < nPairs; ++i) {
108 const auto pId = pairId->GetValue(i);
114 const auto i0 = 2 * i + 0;
115 const auto i1 = 2 * i + 1;
117 const auto v0 = vertexId->GetValue(i0);
118 const auto v1 = vertexId->GetValue(i1);
122 const auto pType = pairType->GetValue(i);
123 const auto pers = pairPers->GetTuple1(i);
124 const auto birth = birthScalars->GetTuple1(i);
125 const auto isFin =
static_cast<bool>(isFinite->GetTuple1(i));
127 std::array<float, 3> coordsBirth{}, coordsDeath{};
130 std::array<double, 3> tmp{};
133 points->GetPoint(i0, tmp.data());
134 coordsBirth = {
static_cast<float>(tmp[0]),
static_cast<float>(tmp[1]),
135 static_cast<float>(tmp[2])};
136 points->GetPoint(i1, tmp.data());
137 coordsDeath = {
static_cast<float>(tmp[0]),
static_cast<float>(tmp[1]),
138 static_cast<float>(tmp[2])};
140 coords->GetTuple(i0, tmp.data());
141 coordsBirth = {
static_cast<float>(tmp[0]),
static_cast<float>(tmp[1]),
142 static_cast<float>(tmp[2])};
143 coords->GetTuple(i1, tmp.data());
144 coordsDeath = {
static_cast<float>(tmp[0]),
static_cast<float>(tmp[1]),
145 static_cast<float>(tmp[2])};
160 vtkDataArray *
const inputScalars,
163 const bool embedInDomain) {
165 if(diagram.empty()) {
170 const auto pd = vtu->GetPointData();
171 const auto cd = vtu->GetCellData();
173 if(pd ==
nullptr || cd ==
nullptr) {
174 dbg.
printWrn(
"Grid has no point data or no cell data");
180 vtkNew<ttkSimplexIdTypeArray> vertsId{};
182 vertsId->SetNumberOfTuples(2 * diagram.size());
183 pd->AddArray(vertsId);
185 vtkNew<vtkIntArray> critType{};
187 critType->SetNumberOfTuples(2 * diagram.size());
188 pd->AddArray(critType);
190 vtkNew<vtkFloatArray> coordsScalars{};
193 coordsScalars->SetNumberOfComponents(3);
195 coordsScalars->SetNumberOfTuples(2 * diagram.size());
196 pd->AddArray(coordsScalars);
201 vtkNew<ttkSimplexIdTypeArray> pairsId{};
203 pairsId->SetNumberOfTuples(diagram.size());
204 cd->AddArray(pairsId);
206 vtkNew<vtkIntArray> pairsDim{};
208 pairsDim->SetNumberOfTuples(diagram.size());
209 cd->AddArray(pairsDim);
213 persistence->SetNumberOfTuples(diagram.size());
214 cd->AddArray(persistence);
218 birthScalars->SetNumberOfTuples(diagram.size());
219 cd->AddArray(birthScalars);
221 vtkNew<vtkUnsignedCharArray> isFinite{};
223 isFinite->SetNumberOfTuples(diagram.size());
224 cd->AddArray(isFinite);
228 vtkNew<vtkPoints> points{};
229 points->SetNumberOfPoints(2 * diagram.size());
230 vtkNew<vtkIdTypeArray> offsets{}, connectivity{};
231 offsets->SetNumberOfComponents(1);
232 offsets->SetNumberOfTuples(diagram.size() + 1);
233 connectivity->SetNumberOfComponents(1);
234 connectivity->SetNumberOfTuples(2 * diagram.size());
236#ifdef TTK_ENABLE_OPENMP
237#pragma omp parallel for num_threads(dbg.getThreadNumber())
239 for(
size_t i = 0; i < diagram.size(); ++i) {
240 const auto &pair{diagram[i]};
241 const auto i0{2 * i + 0}, i1{2 * i + 1};
244 i0, pair.birth.coords[0], pair.birth.coords[1], pair.birth.coords[2]);
246 i1, pair.death.coords[0], pair.death.coords[1], pair.death.coords[2]);
248 points->SetPoint(i0, pair.birth.sfValue, pair.birth.sfValue, 0);
249 points->SetPoint(i1, pair.birth.sfValue, pair.death.sfValue, 0);
252 connectivity->SetTuple1(i0, i0);
253 connectivity->SetTuple1(i1, i1);
254 offsets->SetTuple1(i, 2 * i);
257 vertsId->SetTuple1(i0, pair.birth.id);
258 vertsId->SetTuple1(i1, pair.death.id);
259 critType->SetTuple1(i0,
static_cast<ttk::SimplexId>(pair.birth.type));
260 critType->SetTuple1(i1,
static_cast<ttk::SimplexId>(pair.death.type));
263 coordsScalars->SetTuple3(
264 i0, pair.birth.coords[0], pair.birth.coords[1], pair.birth.coords[2]);
265 coordsScalars->SetTuple3(
266 i1, pair.death.coords[0], pair.death.coords[1], pair.death.coords[2]);
270 pairsId->SetTuple1(i, i);
271 persistence->SetTuple1(i, pair.persistence());
272 birthScalars->SetTuple1(i, pair.birth.sfValue);
273 isFinite->SetTuple1(i, pair.isFinite);
275 i, (pair.dim == 2 && pair.isFinite) ? dim - 1 : pair.dim);
277 offsets->SetTuple1(diagram.size(), connectivity->GetNumberOfTuples());
279 vtkNew<vtkCellArray> cells{};
280 cells->SetData(offsets, connectivity);
281 vtu->SetPoints(points);
282 vtu->SetCells(VTK_LINE, cells);
286 const auto lastPair = std::max_element(diagram.begin(), diagram.end());
288 std::array<vtkIdType, 2> diag{
289 0, 2 * std::distance(diagram.begin(), lastPair)};
290 vtu->InsertNextCell(VTK_LINE, 2, diag.data());
291 pairsId->InsertTuple1(diagram.size(), -1);
292 pairsDim->InsertTuple1(diagram.size(), -1);
293 isFinite->InsertTuple1(diagram.size(),
false);
295 const auto maxPersistence = diagram[0].persistence();
296 persistence->InsertTuple1(diagram.size(), 2 * maxPersistence);
298 birthScalars->InsertTuple1(diagram.size(), 0);
304#if defined(TTK_ENABLE_MPI) && defined(TTK_ENABLE_OPENMP)
305int DiagramToDistributedVTU(vtkUnstructuredGrid *vtu,
307 vtkDataArray *
const inputScalars,
310 const bool embedInDomain) {
315 MPI_Datatype MPI_SimplexId = ttk::getMPIType(beginning);
316 MPI_Exscan(&nPairs, &beginning, 1, MPI_SimplexId, MPI_SUM, ttk::MPIcomm_);
318 if(diagram.empty()) {
319 dbg.
printMsg(
"Empty diagram on this rank");
323 const auto pd = vtu->GetPointData();
324 const auto cd = vtu->GetCellData();
328 vtkNew<ttkSimplexIdTypeArray> vertsId{};
330 vertsId->SetNumberOfTuples(2 * diagram.size());
331 pd->AddArray(vertsId);
333 vtkNew<vtkIntArray> critType{};
335 critType->SetNumberOfTuples(2 * diagram.size());
336 pd->AddArray(critType);
338 vtkNew<vtkFloatArray> coordsScalars{};
341 coordsScalars->SetNumberOfComponents(3);
343 coordsScalars->SetNumberOfTuples(2 * diagram.size());
344 pd->AddArray(coordsScalars);
349 vtkNew<ttkSimplexIdTypeArray> pairsId{};
351 pairsId->SetNumberOfTuples(diagram.size());
352 cd->AddArray(pairsId);
354 vtkNew<vtkIntArray> pairsDim{};
356 pairsDim->SetNumberOfTuples(diagram.size());
357 cd->AddArray(pairsDim);
362 cd->AddArray(persistence);
366 birthScalars->SetNumberOfTuples(diagram.size());
367 cd->AddArray(birthScalars);
369 vtkNew<vtkUnsignedCharArray> isFinite{};
371 isFinite->SetNumberOfTuples(diagram.size());
372 cd->AddArray(isFinite);
376 vtkNew<vtkPoints> points{};
377 points->SetNumberOfPoints(2 * diagram.size());
378 vtkNew<vtkIdTypeArray> offsets{}, connectivity{};
379 offsets->SetNumberOfComponents(1);
380 offsets->SetNumberOfTuples(diagram.size() + 1);
381 connectivity->SetNumberOfComponents(1);
382 connectivity->SetNumberOfTuples(2 * diagram.size());
384#pragma omp parallel for num_threads(dbg.getThreadNumber())
385 for(
size_t i = 0; i < diagram.size(); ++i) {
386 const auto &pair{diagram[i]};
387 const auto i0{2 * i + 0}, i1{2 * i + 1};
390 i0, pair.birth.coords[0], pair.birth.coords[1], pair.birth.coords[2]);
392 i1, pair.death.coords[0], pair.death.coords[1], pair.death.coords[2]);
394 points->SetPoint(i0, pair.birth.sfValue, pair.birth.sfValue, 0);
395 points->SetPoint(i1, pair.birth.sfValue, pair.death.sfValue, 0);
398 connectivity->SetTuple1(i0, i0);
399 connectivity->SetTuple1(i1, i1);
400 offsets->SetTuple1(i, 2 * i);
403 vertsId->SetTuple1(i0, pair.birth.id);
404 vertsId->SetTuple1(i1, pair.death.id);
405 critType->SetTuple1(i0,
static_cast<ttk::SimplexId>(pair.birth.type));
406 critType->SetTuple1(i1,
static_cast<ttk::SimplexId>(pair.death.type));
409 coordsScalars->SetTuple3(
410 i0, pair.birth.coords[0], pair.birth.coords[1], pair.birth.coords[2]);
411 coordsScalars->SetTuple3(
412 i1, pair.death.coords[0], pair.death.coords[1], pair.death.coords[2]);
416 pairsId->SetTuple1(i, beginning + i);
418 birthScalars->SetTuple1(i, pair.birth.sfValue);
419 isFinite->SetTuple1(i, pair.isFinite);
421 i, (pair.dim == 2 && pair.isFinite) ? dim - 1 : pair.dim);
423 offsets->SetTuple1(diagram.size(), connectivity->GetNumberOfTuples());
425 vtkNew<vtkCellArray> cells{};
426 cells->SetData(offsets, connectivity);
427 vtu->SetPoints(points);
428 vtu->SetCells(VTK_LINE, cells);
431 const auto lastPair = std::max_element(diagram.begin(), diagram.end());
433 std::array<vtkIdType, 2> diag{
434 0, 2 * std::distance(diagram.begin(), lastPair)};
435 vtu->InsertNextCell(VTK_LINE, 2, diag.data());
436 pairsId->InsertTuple1(diagram.size(), -1);
437 pairsDim->InsertTuple1(diagram.size(), -1);
438 isFinite->InsertTuple1(diagram.size(),
false);
439 const auto maxPersistence = diagram[0].persistence();
440 persistence->InsertTuple1(diagram.size(), 2 * maxPersistence);
441 birthScalars->InsertTuple1(diagram.size(), 0);
448 vtkUnstructuredGrid *
const outputDiagram,
454 vtkNew<vtkThreshold> threshold{};
455 threshold->SetInputDataObject(0, inputDiagram);
456 threshold->SetInputArrayToProcess(0, 0, 0,
457 vtkDataObject::FIELD_ASSOCIATION_CELLS,
459#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 2, 0)
460 threshold->ThresholdByUpper(0);
462 threshold->SetThresholdFunction(vtkThreshold::THRESHOLD_UPPER);
463 threshold->SetUpperThreshold(0);
468 auto diagonalLess = threshold->GetOutput();
469 auto diagonalLessData = diagonalLess->GetPointData();
471 const auto critCoordinates = vtkFloatArray::SafeDownCast(
475 vtkNew<vtkFloatArray> coords{};
476 coords->DeepCopy(critCoordinates);
477 coords->SetName(
"Points");
478 diagonalLess->GetPoints()->SetData(coords);
481 outputDiagram->ShallowCopy(diagonalLess);
484 outputDiagram->GetFieldData()->ShallowCopy(inputDiagram->GetFieldData());
486 dbg.
printMsg(
"Projected Persistence Diagram inside domain", 1.0,
492template <
typename dataType>
494 const dataType *
const births,
495 const dataType *
const perss,
497 const int nThreads) {
499#ifdef TTK_ENABLE_OPENMP
500#pragma omp parallel for num_threads(nThreads)
502 for(
int i = 0; i < nPoints / 2; ++i) {
503 std::array<float, 3> pt0{
504 static_cast<float>(births[i]),
505 static_cast<float>(births[i]),
508 std::array<float, 3> pt1{
509 static_cast<float>(births[i]),
510 static_cast<float>(births[i] + perss[i]),
513 points->SetPoint(2 * i + 0, pt0.data());
514 points->SetPoint(2 * i + 1, pt1.data());
521 vtkUnstructuredGrid *
const outputDiagram,
526 outputDiagram->ShallowCopy(inputDiagram);
528 auto pointData = outputDiagram->GetPointData();
533 if(birth ==
nullptr || pers ==
nullptr) {
534 dbg.
printWrn(
"Missing Birth or Persistence arrays");
539 vtkNew<vtkFloatArray> coords{};
540 coords->DeepCopy(inputDiagram->GetPoints()->GetData());
542 pointData->AddArray(coords);
544 vtkNew<vtkPoints> points{};
545 const auto nPoints = inputDiagram->GetNumberOfPoints();
546 points->SetNumberOfPoints(nPoints);
548 if(birth->GetNumberOfTuples() != nPoints / 2
549 || pers->GetNumberOfTuples() != nPoints / 2) {
550 dbg.
printWrn(
"Wrong number of tuples for Birth or Persistence arrays");
554 switch(birth->GetDataType()) {
560 outputDiagram->SetPoints(points);
563 std::array<vtkIdType, 2> diag{0, 2 * (outputDiagram->GetNumberOfCells() - 1)};
564 outputDiagram->InsertNextCell(VTK_LINE, 2, diag.data());
567 auto cellData = outputDiagram->GetCellData();
568 auto pairIdentifierScalars = vtkIntArray::SafeDownCast(
570 auto extremumIndexScalars = vtkIntArray::SafeDownCast(
576 pairIdentifierScalars->InsertNextTuple1(-1);
577 extremumIndexScalars->InsertNextTuple1(-1);
578 isFinite->InsertNextTuple1(0);
580 persistenceScalars->InsertNextTuple1(2 * persistenceScalars->GetTuple1(0));
582 birthScalars->InsertNextTuple1(0);
585 outputDiagram->GetFieldData()->ShallowCopy(inputDiagram->GetFieldData());
587 dbg.
printMsg(
"Projected Persistence Diagram back to 2D", 1.0,
594 const std::array<double, 3> &trans) {
596 vtkNew<vtkUnstructuredGrid> tmp{};
597 tmp->ShallowCopy(diagram);
599 vtkNew<vtkTransform> tr{};
600 tr->Translate(trans.data());
602 vtkNew<vtkTransformFilter> trf{};
603 trf->SetTransform(tr);
604 trf->SetInputData(tmp);
607 diagram->ShallowCopy(trf->GetOutputDataObject(0));
620 dbg.
printWrn(
"Cannot reset embedded diagram position");
625 std::array<double, 3> pos{};
626 diagram->GetPoint(diagram->GetCell(0)->GetPointId(0), pos.data());
629 const auto firstBirth{
632 if((pos[0] != pos[1] && pos[0] != firstBirth) || pos[2] != 0) {
633 vtkNew<vtkUnstructuredGrid> tmp{};
634 tmp->ShallowCopy(diagram);
636 vtkNew<vtkTransform> tr{};
637 tr->Translate(firstBirth - pos[0], firstBirth - pos[1], -pos[2]);
639 vtkNew<vtkTransformFilter> trf{};
640 trf->SetTransform(tr);
641 trf->SetInputData(tmp);
644 diagram->ShallowCopy(trf->GetOutputDataObject(0));
646 dbg.
printMsg(
"Diagram reset to initial position");
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
static DT * GetPointer(vtkDataArray *array, vtkIdType start=0)
int getThreadNumber() const
Minimalist debugging class.
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
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
int SimplexId
Identifier type for simplices of any dimension.
const char PersistenceName[]
CriticalType
default value for critical index
const char PersistenceCoordinatesName[]
const char PersistencePairTypeName[]
const char PersistenceCriticalTypeName[]
const char PersistenceIsFinite[]
const char VertexScalarFieldName[]
default name for vertex scalar field
const char PersistencePairIdentifierName[]
const char PersistenceBirthName[]
std::vector< PersistencePair > DiagramType
Persistence Diagram type as a vector of Persistence pairs.
int ProjectDiagramInsideDomain(vtkUnstructuredGrid *const inputDiagram, vtkUnstructuredGrid *const outputDiagram, const ttk::Debug &dbg)
Generate the spatial embedding of a given Persistence Diagram.
int DiagramToVTU(vtkUnstructuredGrid *vtu, const ttk::DiagramType &diagram, vtkDataArray *const inputScalars, const ttk::Debug &dbg, const int dim, const bool embedInDomain)
Converts a Persistence Diagram in the ttk::DiagramType format to the VTK Unstructured Grid format (as...
int TranslateDiagram(vtkUnstructuredGrid *const diagram, const std::array< double, 3 > &trans)
Translate a diagram to a new position.
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...
int ProjectDiagramIn2D(vtkUnstructuredGrid *const inputDiagram, vtkUnstructuredGrid *const outputDiagram, const ttk::Debug &dbg)
Generate the 2D embedding of a given Persistence Diagram.
void getCoords(vtkPoints *points, const dataType *const births, const dataType *const perss, vtkIdType nPoints, const int nThreads)
int ResetDiagramPosition(vtkUnstructuredGrid *const diagram, const ttk::Debug &dbg)
Translate back a canonical diagram into its original position.