// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkDataSetTriangleFilter.h" #include "vtkCellData.h" #include "vtkCellType.h" #include "vtkGenericCell.h" #include "vtkIdList.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkOrderedTriangulator.h" #include "vtkPointData.h" #include "vtkRectilinearGrid.h" #include "vtkStructuredGrid.h" #include "vtkStructuredPoints.h" #include "vtkUnsignedCharArray.h" #include "vtkUnstructuredGrid.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkDataSetTriangleFilter); vtkDataSetTriangleFilter::vtkDataSetTriangleFilter() { this->Triangulator = vtkOrderedTriangulator::New(); this->Triangulator->PreSortedOff(); this->Triangulator->UseTemplatesOn(); this->TetrahedraOnly = 0; } vtkDataSetTriangleFilter::~vtkDataSetTriangleFilter() { this->Triangulator->Delete(); this->Triangulator = nullptr; } int vtkDataSetTriangleFilter::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the info objects vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); // get the input and output vtkDataSet* input = vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkUnstructuredGrid* output = vtkUnstructuredGrid::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); if (input->IsA("vtkStructuredPoints") || input->IsA("vtkStructuredGrid") || input->IsA("vtkImageData") || input->IsA("vtkRectilinearGrid")) { this->StructuredExecute(input, output); } else { this->UnstructuredExecute(input, output); } vtkDebugMacro(<< "Produced " << this->GetOutput()->GetNumberOfCells() << " cells"); return 1; } void vtkDataSetTriangleFilter::StructuredExecute(vtkDataSet* input, vtkUnstructuredGrid* output) { int dimensions[3], i, j, k, l, m; vtkIdType newCellId, inId; vtkCellData* inCD = input->GetCellData(); vtkCellData* outCD = output->GetCellData(); vtkPoints* newPoints = vtkPoints::New(); vtkIdList* cellPtIds = vtkIdList::New(); int numSimplices, numPts, dim, type; vtkIdType pts[4], num; // Create an array of points. This does an explicit creation // of each point. num = input->GetNumberOfPoints(); newPoints->SetNumberOfPoints(num); for (i = 0; i < num; ++i) { newPoints->SetPoint(i, input->GetPoint(i)); } outCD->CopyAllocate(inCD, input->GetNumberOfCells() * 5); output->Allocate(input->GetNumberOfCells() * 5); if (input->IsA("vtkStructuredPoints")) { static_cast(input)->GetDimensions(dimensions); } else if (input->IsA("vtkStructuredGrid")) { static_cast(input)->GetDimensions(dimensions); } else if (input->IsA("vtkImageData")) { static_cast(input)->GetDimensions(dimensions); } else if (input->IsA("vtkRectilinearGrid")) { static_cast(input)->GetDimensions(dimensions); } else { // Every kind of structured data is listed above, this should never happen. // Report an error and produce no output. vtkErrorMacro("Unrecognized data set " << input->GetClassName()); // Dimensions of 1x1x1 means a single point, i.e. dimensionality of zero. dimensions[0] = 1; dimensions[1] = 1; dimensions[2] = 1; } dimensions[0] = dimensions[0] - 1; dimensions[1] = dimensions[1] - 1; dimensions[2] = dimensions[2] - 1; vtkIdType numSlices = (dimensions[2] > 0 ? dimensions[2] : 1); bool abort = false; for (k = 0; k < numSlices && !abort; k++) { this->UpdateProgress(static_cast(k) / numSlices); abort = this->CheckAbort(); for (j = 0; j < dimensions[1]; j++) { for (i = 0; i < dimensions[0]; i++) { inId = i + (j + (k * dimensions[1])) * dimensions[0]; vtkCell* cell = input->GetCell(i, j, k); if ((i + j + k) % 2 == 0) { cell->TriangulateIds(0, cellPtIds); } else { cell->TriangulateIds(1, cellPtIds); } dim = cell->GetCellDimension() + 1; numPts = cellPtIds->GetNumberOfIds(); numSimplices = numPts / dim; type = 0; switch (dim) { case 1: type = VTK_VERTEX; break; case 2: type = VTK_LINE; break; case 3: type = VTK_TRIANGLE; break; case 4: type = VTK_TETRA; break; } if (!this->TetrahedraOnly || type == VTK_TETRA) { for (l = 0; l < numSimplices; l++) { for (m = 0; m < dim; m++) { pts[m] = cellPtIds->GetId(dim * l + m); } // copy cell data newCellId = output->InsertNextCell(type, dim, pts); outCD->CopyData(inCD, inId, newCellId); } // for all simplices } } // i dimension } // j dimension } // k dimension // Update output output->SetPoints(newPoints); output->GetPointData()->PassData(input->GetPointData()); output->Squeeze(); newPoints->Delete(); cellPtIds->Delete(); } // 3D cells use the ordered triangulator. The ordered triangulator is used // to create templates on the fly. Once the templates are created then they // are used to produce the final triangulation. // void vtkDataSetTriangleFilter::UnstructuredExecute( vtkDataSet* dataSetInput, vtkUnstructuredGrid* output) { vtkPointSet* input = static_cast(dataSetInput); // has to be vtkIdType numCells = input->GetNumberOfCells(); vtkGenericCell* cell; vtkIdType newCellId, j; int k; vtkCellData* inCD = input->GetCellData(); vtkCellData* outCD = output->GetCellData(); vtkIdList* cellPtIds; vtkIdType ptId, numTets, ncells; int numPts, type; int numSimplices, dim; vtkIdType pts[4]; double x[3]; if (numCells == 0) { outCD->CopyAllocate(inCD, 0); output->GetPointData()->CopyAllocate(input->GetPointData(), 0); return; } vtkUnstructuredGrid* inUgrid = vtkUnstructuredGrid::SafeDownCast(dataSetInput); if (inUgrid) { int allSimplices = 1; for (vtkIdType cellId = 0; cellId < inUgrid->GetNumberOfCells() && allSimplices; cellId++) { switch (inUgrid->GetCellType(cellId)) { case VTK_TETRA: break; case VTK_VERTEX: case VTK_LINE: case VTK_TRIANGLE: if (this->TetrahedraOnly) { allSimplices = 0; // don't shallowcopy need to strip non tets } break; default: allSimplices = 0; break; } } if (allSimplices) { output->ShallowCopy(input); return; } } cell = vtkGenericCell::New(); cellPtIds = vtkIdList::New(); // Create an array of points vtkCellData* tempCD = vtkCellData::New(); tempCD->ShallowCopy(inCD); tempCD->SetActiveGlobalIds(nullptr); outCD->CopyAllocate(tempCD, input->GetNumberOfCells() * 5); output->Allocate(input->GetNumberOfCells() * 5); // Points are passed through output->SetPoints(input->GetPoints()); output->GetPointData()->PassData(input->GetPointData()); int abort = 0; vtkIdType updateTime = numCells / 20 + 1; // update roughly every 5% for (vtkIdType cellId = 0; cellId < numCells && !abort; cellId++) { if (!(cellId % updateTime)) { this->UpdateProgress(static_cast(cellId) / numCells); abort = this->CheckAbort(); } input->GetCell(cellId, cell); dim = cell->GetCellDimension(); if (cell->GetCellType() == VTK_POLYHEDRON) // polyhedron { dim = 4; cell->TriangulateIds(0, cellPtIds); numPts = cellPtIds->GetNumberOfIds(); numSimplices = numPts / dim; type = VTK_TETRA; for (j = 0; j < numSimplices; j++) { for (k = 0; k < dim; k++) { pts[k] = cellPtIds->GetId(dim * j + k); } // copy cell data newCellId = output->InsertNextCell(type, dim, pts); outCD->CopyData(tempCD, cellId, newCellId); } } else if (dim == 3) // use ordered triangulation { numPts = cell->GetNumberOfPoints(); double *p, *pPtr = cell->GetParametricCoords(); this->Triangulator->InitTriangulation(0.0, 1.0, 0.0, 1.0, 0.0, 1.0, numPts); for (p = pPtr, j = 0; j < numPts; j++, p += 3) { // the wedge is "flipped" compared to other cells in that // the normal of the first face points out instead of in // so we flip the way we pass the points to the triangulator constexpr vtkIdType wedgemap[18] = { 3, 4, 5, 0, 1, 2, 9, 10, 11, 6, 7, 8, 12, 13, 14, 15, 16, 17 }; type = cell->GetCellType(); if (type == VTK_WEDGE || type == VTK_QUADRATIC_WEDGE || type == VTK_QUADRATIC_LINEAR_WEDGE || type == VTK_BIQUADRATIC_QUADRATIC_WEDGE) { ptId = cell->PointIds->GetId(wedgemap[j]); cell->Points->GetPoint(wedgemap[j], x); } else { ptId = cell->PointIds->GetId(j); cell->Points->GetPoint(j, x); } this->Triangulator->InsertPoint(ptId, x, p, 0); } // for all cell points if (cell->IsPrimaryCell()) // use templates if topology is fixed { int numEdges = cell->GetNumberOfEdges(); this->Triangulator->TemplateTriangulate(cell->GetCellType(), numPts, numEdges); } else // use ordered triangulator { this->Triangulator->Triangulate(); } ncells = output->GetNumberOfCells(); numTets = this->Triangulator->AddTetras(0, output); for (j = 0; j < numTets; j++) { outCD->CopyData(tempCD, cellId, ncells + j); } } else if (!this->TetrahedraOnly) // 2D or lower dimension { dim++; cell->TriangulateIds(0, cellPtIds); numPts = cellPtIds->GetNumberOfIds(); numSimplices = numPts / dim; type = 0; switch (dim) { case 1: type = VTK_VERTEX; break; case 2: type = VTK_LINE; break; case 3: type = VTK_TRIANGLE; break; } for (j = 0; j < numSimplices; j++) { for (k = 0; k < dim; k++) { pts[k] = cellPtIds->GetId(dim * j + k); } // copy cell data newCellId = output->InsertNextCell(type, dim, pts); outCD->CopyData(tempCD, cellId, newCellId); } } // if 2D or less cell } // for all cells // Update output output->Squeeze(); tempCD->Delete(); cellPtIds->Delete(); cell->Delete(); } int vtkDataSetTriangleFilter::FillInputPortInformation(int, vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); return 1; } void vtkDataSetTriangleFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "TetrahedraOnly: " << (this->TetrahedraOnly ? "On" : "Off") << "\n"; } VTK_ABI_NAMESPACE_END