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#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 6, 1)
46 object->GetDistinctCellTypes(cellTypes);
47#else
48 object->GetCellTypes(cellTypes);
49#endif
50 nTypes = cellTypes->GetNumberOfTypes();
51 }
52
53 // if cells are empty
54 if(nTypes == 0)
55 return 1; // no error
56
57 // if cells are not homogeneous
58 if(nTypes > 1)
59 return -1;
60
61 // if cells are not simplices
62 if(nTypes == 1) {
63 const auto &cellType = object->GetCellType(0);
64 if(cellType != VTK_VERTEX && cellType != VTK_LINE
65 && cellType != VTK_TRIANGLE && cellType != VTK_TETRA)
66 return -2;
67 }
68
69 return 1;
70}
71
72struct ttkOnDeleteCommand : public vtkCommand {
74 vtkObject *observee;
75
77 return new ttkOnDeleteCommand;
78 }
79 vtkTypeMacro(ttkOnDeleteCommand, vtkCommand);
80
81 void Init(vtkDataSet *dataSet) {
82 this->key = ttkTriangulationFactory::GetKey(dataSet);
83
84 if(dataSet->IsA("vtkPointSet"))
85 this->observee = static_cast<vtkObject *>(GetCells(dataSet));
86 else
87 this->observee = static_cast<vtkObject *>(dataSet);
88
89 this->observee->AddObserver(vtkCommand::DeleteEvent, this, 1);
90 }
91
92 void Execute(vtkObject *,
93 unsigned long ttkNotUsed(eventId),
94 void *ttkNotUsed(callData)) override {
95 if(this->observee)
96 this->observee->RemoveObserver(this);
97
98 auto instance = &ttkTriangulationFactory::Instance;
99
100 if(instance->registry.empty()) {
101 return;
102 }
103
104 auto it = instance->registry.find(this->key);
105 if(it != instance->registry.end()) {
106 instance->registry.erase(it);
107 instance->printMsg("Triangulation Deleted", ttk::debug::Priority::DETAIL);
108 instance->printMsg("# Registered Triangulations: "
109 + std::to_string(instance->registry.size()),
111 }
112 }
113};
114
116 ttk::Triangulation *triangulation_)
117 : triangulation(triangulation_), owner(dataSet) {
118 auto cells = GetCells(dataSet);
119 if(cells)
120 this->cellModTime = cells->GetMTime();
121
122 if(dataSet->IsA("vtkImageData")) {
123 auto image = static_cast<vtkImageData *>(dataSet);
124 image->GetExtent(this->extent);
125 image->GetOrigin(this->origin);
126 image->GetSpacing(this->spacing);
127 image->GetDimensions(this->dimensions);
128 }
129
131 onDelete->Init(dataSet);
132}
133
134bool RegistryValue::isValid(vtkDataSet *dataSet) const {
135 auto cells = GetCells(dataSet);
136 if(cells)
137 return this->cellModTime == cells->GetMTime();
138
139 if(dataSet->IsA("vtkImageData")) {
140 auto image = static_cast<vtkImageData *>(dataSet);
141
142 int extent_[6];
143 double origin_[3];
144 double spacing_[3];
145 int dimensions_[3];
146
147 image->GetExtent(extent_);
148 image->GetOrigin(origin_);
149 image->GetSpacing(spacing_);
150 image->GetDimensions(dimensions_);
151
152#ifdef TTK_ENABLE_MPI
153 bool isValid = triangulation->getIsMPIValid();
154#else
155 bool isValid = true;
156#endif
157 for(int i = 0; i < 6; i++)
158 if(this->extent[i] != extent_[i])
159 isValid = false;
160 for(int i = 0; i < 3; i++)
161 if(this->origin[i] != origin_[i] || this->spacing[i] != spacing_[i]
162 || this->dimensions[i] != dimensions_[i])
163 isValid = false;
164
165 return isValid;
166 }
167
168 return false;
169}
170
171ttkTriangulationFactory::ttkTriangulationFactory() {
172 this->setDebugMsgPrefix("TriangulationFactory");
173}
174
176 ttkTriangulationFactory::CreateImplicitTriangulation(vtkImageData *image) {
177 ttk::Timer timer;
178 this->printMsg("Initializing Implicit Triangulation", 0, 0,
180
181 auto triangulation = std::make_unique<ttk::Triangulation>();
182
183 int extent[6];
184 image->GetExtent(extent);
185
186 double origin[3];
187 image->GetOrigin(origin);
188
189 double spacing[3];
190 image->GetSpacing(spacing);
191
192 // 1D (not tested)
193 if(!spacing[1] && !spacing[2])
194 spacing[1] = 1;
195 if(!spacing[2]) // 2D (tested)
196 spacing[2] = 1;
197
198 int dimensions[3];
199 image->GetDimensions(dimensions);
200
201 double firstPoint[3];
202 firstPoint[0] = origin[0] + extent[0] * spacing[0];
203 firstPoint[1] = origin[1] + extent[2] * spacing[1];
204 firstPoint[2] = origin[2] + extent[4] * spacing[2];
205
206 triangulation->setInputGrid(firstPoint[0], firstPoint[1], firstPoint[2],
207 spacing[0], spacing[1], spacing[2], dimensions[0],
208 dimensions[1], dimensions[2]);
209
210 this->printMsg("Initializing Implicit Triangulation", 1,
213
214 return triangulation;
215}
216
218 ttkTriangulationFactory::CreateExplicitTriangulation(vtkPointSet *pointSet) {
219 ttk::Timer timer;
220
221 auto points = pointSet->GetPoints();
222 if(!points) {
223 this->printErr("DataSet has uninitialized `vtkPoints`.");
224 return nullptr;
225 }
226
227 auto cells = GetCells(pointSet);
228 if(!cells) {
229 this->printErr("DataSet has uninitialized `vtkCellArray`.");
230 return nullptr;
231 }
232
233 auto triangulation = std::make_unique<ttk::Triangulation>();
234 int const hasIndexArray
235 = pointSet->GetPointData()->HasArray(ttk::compactTriangulationIndex);
236
237 if(hasIndexArray) {
238 this->printMsg("Initializing Compact Triangulation", 0, 0,
240 } else {
241 this->printMsg("Initializing Explicit Triangulation", 0, 0,
243 }
244
245 // Points
246 {
247 auto pointDataType = points->GetDataType();
248 if(pointDataType != VTK_FLOAT && pointDataType != VTK_DOUBLE) {
249 this->printErr("Unable to initialize 'ttk::Triangulation' for point "
250 "precision other than 'float' or 'double'.");
251 return {};
252 }
253
254 void *pointDataArray = ttkUtils::GetVoidPointer(points);
255 if(hasIndexArray) {
256 vtkAbstractArray *indexArray = pointSet->GetPointData()->GetAbstractArray(
258 triangulation->setStellarInputPoints(
259 points->GetNumberOfPoints(), pointDataArray,
260 (int *)indexArray->GetVoidPointer(0), pointDataType == VTK_DOUBLE);
261 } else {
262 triangulation->setInputPoints(points->GetNumberOfPoints(), pointDataArray,
263 pointDataType == VTK_DOUBLE);
264 }
265 }
266
267 // check if cell types are simplices
268 int const cellTypeStatus = checkCellTypes(pointSet);
269 if(cellTypeStatus == -1) {
270 this->printWrn("Inhomogeneous cell dimensions detected.");
271 this->printWrn(
272 "Consider using `ttkExtract` to extract cells of a given dimension.");
273 return {};
274 } else if(cellTypeStatus == -2) {
275 this->printWrn("Cells are not simplices.");
276 this->printWrn("Consider using `vtkTetrahedralize` in pre-processing.");
277 return {};
278 }
279
280 // Cells
281 int const nCells = cells->GetNumberOfCells();
282 if(nCells > 0) {
283 if(!cells->IsStorage64Bit()) {
284 if(cells->CanConvertTo64BitStorage()) {
285 this->printWrn("Converting the cell array to 64-bit storage");
286 bool const success = cells->ConvertTo64BitStorage();
287 if(!success) {
288 this->printErr(
289 "Error converting the provided cell array to 64-bit storage");
290 return {};
291 }
292 } else {
293 this->printErr(
294 "Cannot convert the provided cell array to 64-bit storage");
295 return {};
296 }
297 }
298 auto connectivity = static_cast<vtkIdType *>(
299 ttkUtils::GetVoidPointer(cells->GetConnectivityArray()));
300 auto offsets = static_cast<vtkIdType *>(
301 ttkUtils::GetVoidPointer(cells->GetOffsetsArray()));
302
303 int status;
304 if(hasIndexArray) {
305 status
306 = triangulation->setStellarInputCells(nCells, connectivity, offsets);
307 } else {
308 status = triangulation->setInputCells(nCells, connectivity, offsets);
309 }
310
311 if(status != 0) {
312 this->printErr(
313 "Run the `vtkTetrahedralize` filter to resolve the issue.");
314 return {};
315 }
316 }
317
318 if(hasIndexArray) {
319 this->printMsg("Initializing Compact Triangulation", 1,
322 } else {
323 this->printMsg("Initializing Explicit Triangulation", 1,
326 }
327
328 return triangulation;
329}
330
332 ttkTriangulationFactory::CreateTriangulation(vtkDataSet *dataSet) {
333 switch(dataSet->GetDataObjectType()) {
334 case VTK_UNSTRUCTURED_GRID:
335 case VTK_POLY_DATA: {
336 return this->CreateExplicitTriangulation(
337 static_cast<vtkPointSet *>(dataSet));
338 }
339 case VTK_IMAGE_DATA: {
340 return this->CreateImplicitTriangulation((vtkImageData *)dataSet);
341 }
342 default: {
343 this->printErr("Unable to triangulate `"
344 + std::string(dataSet->GetClassName()) + "`");
345 }
346 }
347
348 return nullptr;
349}
350
352 int debugLevel, float cacheRatio, vtkDataSet *object) {
353 auto instance = &ttkTriangulationFactory::Instance;
354 instance->setDebugLevel(debugLevel);
355
356 auto key = ttkTriangulationFactory::GetKey(object);
357
358 ttk::Triangulation *triangulation{nullptr};
359 auto it = instance->registry.find(key);
360 if(it != instance->registry.end()) {
361 // object is the owner of the explicit or implicit triangulation
362 if(it->second.isValid(object)) {
363 instance->printMsg(
364 "Retrieving Existing Triangulation", ttk::debug::Priority::DETAIL);
365 triangulation = it->second.triangulation.get();
366 } else {
367 instance->printMsg(
368 "Existing Triangulation No Longer Valid", ttk::debug::Priority::DETAIL);
369 instance->registry.erase(key);
370 }
371 }
372
373 if(!triangulation && object->IsA("vtkImageData")) {
374 instance->FindImplicitTriangulation(
375 triangulation, static_cast<vtkImageData *>(object));
376 if(triangulation)
377 instance->printMsg("Retrieving Equivalent Implicit-Triangulation",
379 }
380
381 if(!triangulation) {
382 triangulation = instance->CreateTriangulation(object).release();
383 if(triangulation) {
384 instance->registry.emplace(std::piecewise_construct,
385 std::forward_as_tuple(key),
386 std::forward_as_tuple(object, triangulation));
387 }
388 }
389
390 instance->printMsg(
391 "# Registered Triangulations: " + std::to_string(instance->registry.size()),
393
394 if(triangulation) {
395 triangulation->setDebugLevel(debugLevel);
396 triangulation->setCacheSize(cacheRatio);
397 }
398
399 return triangulation;
400}
401
402int ttkTriangulationFactory::FindImplicitTriangulation(
403 ttk::Triangulation *&triangulation, vtkImageData *image) {
404
405 for(const auto &it : this->registry) {
406 if(it.second.owner->IsA("vtkImageData")) {
407 if(it.second.isValid(image)) {
408 triangulation = it.second.triangulation.get();
409 return 1;
410 }
411 }
412 }
413
414 return 0;
415}
416
418 switch(dataSet->GetDataObjectType()) {
419 case VTK_IMAGE_DATA: {
420 return (RegistryKey)dataSet;
421 }
422 default: {
423 auto cells = GetCells(dataSet);
424 if(cells)
425 return (RegistryKey)cells;
426 }
427 }
428
429 return 0;
430}
431
#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:228
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
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 ...
int setDebugLevel(const int &debugLevel) override
Tune the debug level (default: 0).
int setCacheSize(const float &ratio)
const char compactTriangulationIndex[]
Definition DataTypes.h:85
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)