TTK
Loading...
Searching...
No Matches
ttkTriangulationFactory.cpp
Go to the documentation of this file.
2
3#include <Triangulation.h>
4#include <ttkUtils.h>
5
6#include <vtkCallbackCommand.h>
7#include <vtkCellData.h>
8#include <vtkCellTypes.h>
9#include <vtkCommand.h>
10#include <vtkImageData.h>
11#include <vtkPointData.h>
12#include <vtkPolyData.h>
13#include <vtkUnstructuredGrid.h>
14#include <vtkVersionMacros.h>
15
16static vtkCellArray *GetCells(vtkDataSet *dataSet) {
17 switch(dataSet->GetDataObjectType()) {
18 case VTK_UNSTRUCTURED_GRID: {
19 auto dataSetAsUG = static_cast<vtkUnstructuredGrid *>(dataSet);
20 return dataSetAsUG->GetCells();
21 }
22 case VTK_POLY_DATA: {
23 auto dataSetAsPD = static_cast<vtkPolyData *>(dataSet);
24 return dataSetAsPD->GetNumberOfPolys() > 0 ? dataSetAsPD->GetPolys()
25 : dataSetAsPD->GetNumberOfLines() > 0 ? dataSetAsPD->GetLines()
26 : dataSetAsPD->GetVerts();
27 }
28 }
29 return nullptr;
30}
31
32static int checkCellTypes(vtkPointSet *object) {
33
34 size_t nTypes = 0;
35
36#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 0)
37 if(object->GetDataObjectType() == VTK_UNSTRUCTURED_GRID) {
38 auto objectAsUG = vtkUnstructuredGrid::SafeDownCast(object);
39 auto distinctCellTypes = objectAsUG->GetDistinctCellTypesArray();
40 nTypes = distinctCellTypes->GetNumberOfTuples();
41 } else
42#endif
43 {
44 auto cellTypes = vtkSmartPointer<vtkCellTypes>::New();
45 object->GetCellTypes(cellTypes);
46 nTypes = cellTypes->GetNumberOfTypes();
47 }
48
49 // if cells are empty
50 if(nTypes == 0)
51 return 1; // no error
52
53 // if cells are not homogeneous
54 if(nTypes > 1)
55 return -1;
56
57 // if cells are not simplices
58 if(nTypes == 1) {
59 const auto &cellType = object->GetCellType(0);
60 if(cellType != VTK_VERTEX && cellType != VTK_LINE
61 && cellType != VTK_TRIANGLE && cellType != VTK_TETRA)
62 return -2;
63 }
64
65 return 1;
66}
67
68struct ttkOnDeleteCommand : public vtkCommand {
70 vtkObject *observee;
71
73 return new ttkOnDeleteCommand;
74 }
75 vtkTypeMacro(ttkOnDeleteCommand, vtkCommand);
76
77 void Init(vtkDataSet *dataSet) {
78 this->key = ttkTriangulationFactory::GetKey(dataSet);
79
80 if(dataSet->IsA("vtkPointSet"))
81 this->observee = static_cast<vtkObject *>(GetCells(dataSet));
82 else
83 this->observee = static_cast<vtkObject *>(dataSet);
84
85 this->observee->AddObserver(vtkCommand::DeleteEvent, this, 1);
86 }
87
88 void Execute(vtkObject *,
89 unsigned long ttkNotUsed(eventId),
90 void *ttkNotUsed(callData)) override {
91 if(this->observee)
92 this->observee->RemoveObserver(this);
93
94 auto instance = &ttkTriangulationFactory::Instance;
95
96 if(instance->registry.empty()) {
97 return;
98 }
99
100 auto it = instance->registry.find(this->key);
101 if(it != instance->registry.end()) {
102 instance->registry.erase(it);
103 instance->printMsg("Triangulation Deleted", ttk::debug::Priority::DETAIL);
104 instance->printMsg("# Registered Triangulations: "
105 + std::to_string(instance->registry.size()),
107 }
108 }
109};
110
112 ttk::Triangulation *triangulation_)
113 : triangulation(triangulation_), owner(dataSet) {
114 auto cells = GetCells(dataSet);
115 if(cells)
116 this->cellModTime = cells->GetMTime();
117
118 if(dataSet->IsA("vtkImageData")) {
119 auto image = static_cast<vtkImageData *>(dataSet);
120 image->GetExtent(this->extent);
121 image->GetOrigin(this->origin);
122 image->GetSpacing(this->spacing);
123 image->GetDimensions(this->dimensions);
124 }
125
127 onDelete->Init(dataSet);
128}
129
130bool RegistryValue::isValid(vtkDataSet *dataSet) const {
131 auto cells = GetCells(dataSet);
132 if(cells)
133 return this->cellModTime == cells->GetMTime();
134
135 if(dataSet->IsA("vtkImageData")) {
136 auto image = static_cast<vtkImageData *>(dataSet);
137
138 int extent_[6];
139 double origin_[3];
140 double spacing_[3];
141 int dimensions_[3];
142
143 image->GetExtent(extent_);
144 image->GetOrigin(origin_);
145 image->GetSpacing(spacing_);
146 image->GetDimensions(dimensions_);
147
148#ifdef TTK_ENABLE_MPI
149 bool isValid = triangulation->getIsMPIValid();
150#else
151 bool isValid = true;
152#endif
153 for(int i = 0; i < 6; i++)
154 if(this->extent[i] != extent_[i])
155 isValid = false;
156 for(int i = 0; i < 3; i++)
157 if(this->origin[i] != origin_[i] || this->spacing[i] != spacing_[i]
158 || this->dimensions[i] != dimensions_[i])
159 isValid = false;
160
161 return isValid;
162 }
163
164 return false;
165}
166
167ttkTriangulationFactory::ttkTriangulationFactory() {
168 this->setDebugMsgPrefix("TriangulationFactory");
169}
170
172 ttkTriangulationFactory::CreateImplicitTriangulation(vtkImageData *image) {
173 ttk::Timer timer;
174 this->printMsg("Initializing Implicit Triangulation", 0, 0,
176
177 auto triangulation = std::make_unique<ttk::Triangulation>();
178
179 int extent[6];
180 image->GetExtent(extent);
181
182 double origin[3];
183 image->GetOrigin(origin);
184
185 double spacing[3];
186 image->GetSpacing(spacing);
187
188 int dimensions[3];
189 image->GetDimensions(dimensions);
190
191 double firstPoint[3];
192 firstPoint[0] = origin[0] + extent[0] * spacing[0];
193 firstPoint[1] = origin[1] + extent[2] * spacing[1];
194 firstPoint[2] = origin[2] + extent[4] * spacing[2];
195
196 triangulation->setInputGrid(firstPoint[0], firstPoint[1], firstPoint[2],
197 spacing[0], spacing[1], spacing[2], dimensions[0],
198 dimensions[1], dimensions[2]);
199
200 this->printMsg("Initializing Implicit Triangulation", 1,
203
204 return triangulation;
205}
206
208 ttkTriangulationFactory::CreateExplicitTriangulation(vtkPointSet *pointSet) {
209 ttk::Timer timer;
210
211 auto points = pointSet->GetPoints();
212 if(!points) {
213 this->printErr("DataSet has uninitialized `vtkPoints`.");
214 return nullptr;
215 }
216
217 auto cells = GetCells(pointSet);
218 if(!cells) {
219 this->printErr("DataSet has uninitialized `vtkCellArray`.");
220 return nullptr;
221 }
222
223 auto triangulation = std::make_unique<ttk::Triangulation>();
224 int const hasIndexArray
225 = pointSet->GetPointData()->HasArray(ttk::compactTriangulationIndex);
226
227 if(hasIndexArray) {
228 this->printMsg("Initializing Compact Triangulation", 0, 0,
230 } else {
231 this->printMsg("Initializing Explicit Triangulation", 0, 0,
233 }
234
235 // Points
236 {
237 auto pointDataType = points->GetDataType();
238 if(pointDataType != VTK_FLOAT && pointDataType != VTK_DOUBLE) {
239 this->printErr("Unable to initialize 'ttk::Triangulation' for point "
240 "precision other than 'float' or 'double'.");
241 return {};
242 }
243
244 void *pointDataArray = ttkUtils::GetVoidPointer(points);
245 if(hasIndexArray) {
246 vtkAbstractArray *indexArray = pointSet->GetPointData()->GetAbstractArray(
248 triangulation->setStellarInputPoints(
249 points->GetNumberOfPoints(), pointDataArray,
250 (int *)indexArray->GetVoidPointer(0), pointDataType == VTK_DOUBLE);
251 } else {
252 triangulation->setInputPoints(points->GetNumberOfPoints(), pointDataArray,
253 pointDataType == VTK_DOUBLE);
254 }
255 }
256
257 // check if cell types are simplices
258 int const cellTypeStatus = checkCellTypes(pointSet);
259 if(cellTypeStatus == -1) {
260 this->printWrn("Inhomogeneous cell dimensions detected.");
261 this->printWrn(
262 "Consider using `ttkExtract` to extract cells of a given dimension.");
263 return {};
264 } else if(cellTypeStatus == -2) {
265 this->printWrn("Cells are not simplices.");
266 this->printWrn("Consider using `vtkTetrahedralize` in pre-processing.");
267 return {};
268 }
269
270 // Cells
271 int const nCells = cells->GetNumberOfCells();
272 if(nCells > 0) {
273 if(!cells->IsStorage64Bit()) {
274 if(cells->CanConvertTo64BitStorage()) {
275 this->printWrn("Converting the cell array to 64-bit storage");
276 bool const success = cells->ConvertTo64BitStorage();
277 if(!success) {
278 this->printErr(
279 "Error converting the provided cell array to 64-bit storage");
280 return {};
281 }
282 } else {
283 this->printErr(
284 "Cannot convert the provided cell array to 64-bit storage");
285 return {};
286 }
287 }
288 auto connectivity = static_cast<vtkIdType *>(
289 ttkUtils::GetVoidPointer(cells->GetConnectivityArray()));
290 auto offsets = static_cast<vtkIdType *>(
291 ttkUtils::GetVoidPointer(cells->GetOffsetsArray()));
292
293 int status;
294 if(hasIndexArray) {
295 status
296 = triangulation->setStellarInputCells(nCells, connectivity, offsets);
297 } else {
298 status = triangulation->setInputCells(nCells, connectivity, offsets);
299 }
300
301 if(status != 0) {
302 this->printErr(
303 "Run the `vtkTetrahedralize` filter to resolve the issue.");
304 return {};
305 }
306 }
307
308 if(hasIndexArray) {
309 this->printMsg("Initializing Compact Triangulation", 1,
312 } else {
313 this->printMsg("Initializing Explicit Triangulation", 1,
316 }
317
318 return triangulation;
319}
320
322 ttkTriangulationFactory::CreateTriangulation(vtkDataSet *dataSet) {
323 switch(dataSet->GetDataObjectType()) {
324 case VTK_UNSTRUCTURED_GRID:
325 case VTK_POLY_DATA: {
326 return this->CreateExplicitTriangulation(
327 static_cast<vtkPointSet *>(dataSet));
328 }
329 case VTK_IMAGE_DATA: {
330 return this->CreateImplicitTriangulation((vtkImageData *)dataSet);
331 }
332 default: {
333 this->printErr("Unable to triangulate `"
334 + std::string(dataSet->GetClassName()) + "`");
335 }
336 }
337
338 return nullptr;
339}
340
342 int debugLevel, float cacheRatio, vtkDataSet *object) {
343 auto instance = &ttkTriangulationFactory::Instance;
344 instance->setDebugLevel(debugLevel);
345
346 auto key = ttkTriangulationFactory::GetKey(object);
347
348 ttk::Triangulation *triangulation{nullptr};
349 auto it = instance->registry.find(key);
350 if(it != instance->registry.end()) {
351 // object is the owner of the explicit or implicit triangulation
352 if(it->second.isValid(object)) {
353 instance->printMsg(
354 "Retrieving Existing Triangulation", ttk::debug::Priority::DETAIL);
355 triangulation = it->second.triangulation.get();
356 } else {
357 instance->printMsg(
358 "Existing Triangulation No Longer Valid", ttk::debug::Priority::DETAIL);
359 instance->registry.erase(key);
360 }
361 }
362
363 if(!triangulation && object->IsA("vtkImageData")) {
364 instance->FindImplicitTriangulation(
365 triangulation, static_cast<vtkImageData *>(object));
366 if(triangulation)
367 instance->printMsg("Retrieving Equivalent Implicit-Triangulation",
369 }
370
371 if(!triangulation) {
372 triangulation = instance->CreateTriangulation(object).release();
373 if(triangulation) {
374 instance->registry.emplace(std::piecewise_construct,
375 std::forward_as_tuple(key),
376 std::forward_as_tuple(object, triangulation));
377 }
378 }
379
380 instance->printMsg(
381 "# Registered Triangulations: " + std::to_string(instance->registry.size()),
383
384 if(triangulation) {
385 triangulation->setDebugLevel(debugLevel);
386 triangulation->setCacheSize(cacheRatio);
387 }
388
389 return triangulation;
390}
391
392int ttkTriangulationFactory::FindImplicitTriangulation(
393 ttk::Triangulation *&triangulation, vtkImageData *image) {
394
395 for(const auto &it : this->registry) {
396 if(it.second.owner->IsA("vtkImageData")) {
397 if(it.second.isValid(image)) {
398 triangulation = it.second.triangulation.get();
399 return 1;
400 }
401 }
402 }
403
404 return 0;
405}
406
408 switch(dataSet->GetDataObjectType()) {
409 case VTK_IMAGE_DATA: {
410 return (RegistryKey)dataSet;
411 }
412 default: {
413 auto cells = GetCells(dataSet);
414 if(cells)
415 return (RegistryKey)cells;
416 }
417 }
418
419 return 0;
420}
421
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
static RegistryKey GetKey(vtkDataSet *dataSet)
static ttk::Triangulation * GetTriangulation(int debugLevel, float cacheRatio, vtkDataSet *object)
static ttkTriangulationFactory Instance
static void * GetVoidPointer(vtkDataArray *array, vtkIdType start=0)
Definition ttkUtils.cpp:226
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 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
double getElapsedTime()
Definition Timer.h:15
Triangulation is a class that provides time and memory efficient traversal methods on triangulations ...
const char compactTriangulationIndex[]
Definition DataTypes.h:77
bool isValid(vtkDataSet *dataSet) const
RegistryValue(vtkDataSet *dataSet, ttk::Triangulation *triangulation_)
RegistryTriangulation triangulation
void Execute(vtkObject *, unsigned long ttkNotUsed(eventId), void *ttkNotUsed(callData)) override
static ttkOnDeleteCommand * New()
void Init(vtkDataSet *dataSet)
long long RegistryKey
std::unique_ptr< ttk::Triangulation > RegistryTriangulation
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)