// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkDensifyPolyData.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkDoubleArray.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkStreamingDemandDrivenPipeline.h" #include //------------------------------------------------------------------------------ VTK_ABI_NAMESPACE_BEGIN class vtkDensifyPolyDataInternals { public: // Constructor with params : // - vertices of the triangle to be decimated, // - number of vertices // - ptIds of these vertices // - current running count of the max pointIds, so the new points added // can be assigned ptIds starting from this value. // - Number of subdivisions vtkDensifyPolyDataInternals(double* verts, vtkIdType numVerts, const vtkIdType* vertIds, vtkIdType& numPoints, unsigned int nSubdivisions) { this->NumPoints = this->StartPointId = this->CurrentPointId = numPoints; Polygon p(verts, numVerts, vertIds); this->Polygons.push_back(p); // The actual work: subdivision of the supplied polygon is done here. for (unsigned int i = 0; i < nSubdivisions; i++) { this->Polygons = this->Subdivide(this->Polygons); } this->PolygonsIterator = this->Polygons.begin(); numPoints = this->NumPoints; } // Internal class to represent an nSided polygon. class Polygon { public: // Construct a polygon. // nPts: number of vertices in the polygon // p: Array of vertices, organized as p0[0], p0[1], p0[2], // p1[0], .. // ptIds: The pointids of the vertices. // parentPtIds: PointIds of the parent polygon (if one exists), ie the // polygon this is intended to represent a subdivision of. // Polygon(double* p, vtkIdType nPts, const vtkIdType* ptIds, vtkIdType nParentPoints = 0, vtkIdType* parentPtIds = nullptr) { this->Verts = new double[3 * nPts]; this->VertIds = new vtkIdType[nPts]; this->NumVerts = nPts; for (vtkIdType i = 0; i < nPts; i++) { this->Verts[3 * i] = p[3 * i]; this->Verts[3 * i + 1] = p[3 * i + 1]; this->Verts[3 * i + 2] = p[3 * i + 2]; this->VertIds[i] = ptIds[i]; } if (nParentPoints && parentPtIds) { this->ParentVertIds = new vtkIdType[nParentPoints]; for (vtkIdType i = 0; i < nPts; i++) { this->ParentVertIds[i] = parentPtIds[i]; } this->NumParentVerts = nParentPoints; } else { this->NumParentVerts = 0; this->ParentVertIds = nullptr; } } // Default constructor Polygon() { this->NumVerts = this->NumParentVerts = 0; this->VertIds = this->ParentVertIds = nullptr; this->Verts = nullptr; } ~Polygon() { this->Clear(); } void Clear() { delete[] this->Verts; this->Verts = nullptr; delete[] this->VertIds; this->VertIds = nullptr; delete[] this->ParentVertIds; this->ParentVertIds = nullptr; } void DeepCopy(const Polygon& p) { // Copy the vertices. this->NumVerts = p.NumVerts; if (p.Verts) { this->Verts = new double[3 * p.NumVerts]; for (vtkIdType i = 0; i < this->NumVerts; i++) { this->Verts[3 * i] = p.Verts[3 * i]; this->Verts[3 * i + 1] = p.Verts[3 * i + 1]; this->Verts[3 * i + 2] = p.Verts[3 * i + 2]; } } else { this->Verts = nullptr; // can happen if called from the copy constructor. } // Copy the vertex ids. if (p.VertIds) { this->VertIds = new vtkIdType[p.NumVerts]; for (vtkIdType i = 0; i < this->NumVerts; i++) { this->VertIds[i] = p.VertIds[i]; } } else { this->VertIds = nullptr; // can happen if called from the copy constructor. } // Copy the parent vertex ids, if any. this->NumParentVerts = p.NumParentVerts; if (p.ParentVertIds) { this->ParentVertIds = new vtkIdType[p.NumParentVerts]; for (vtkIdType i = 0; i < this->NumParentVerts; i++) { this->ParentVertIds[i] = p.ParentVertIds[i]; } } else { this->ParentVertIds = nullptr; this->NumParentVerts = 0; } } // Overload method to deep-copy internal data instead of just copying over // the pointers. Polygon(const Polygon& p) { this->DeepCopy(p); } // Overload method to deep-copy internal data instead of just copying over // the pointers. Polygon& operator=(const Polygon& p) { if (this == &p) { return *this; } // start afresh this->Clear(); this->DeepCopy(p); return *this; } void GetCentroid(double centroid[3]) { centroid[0] = centroid[1] = centroid[2] = 0.0; for (vtkIdType i = 0; i < this->NumVerts; i++) { centroid[0] += this->Verts[3 * i]; centroid[1] += this->Verts[3 * i + 1]; centroid[2] += this->Verts[3 * i + 2]; } centroid[0] /= static_cast(this->NumVerts); centroid[1] /= static_cast(this->NumVerts); centroid[2] /= static_cast(this->NumVerts); } // Get a point with a specific pointId. Returns false if not found bool GetPointWithId(vtkIdType id, double p[3]) { for (vtkIdType i = 0; i < this->NumVerts; i++) { if (this->VertIds[i] == id) { p[0] = this->Verts[3 * i]; p[1] = this->Verts[3 * i + 1]; p[2] = this->Verts[3 * i + 2]; return true; } } return false; } double* Verts; vtkIdType* VertIds; vtkIdType NumVerts; vtkIdType* ParentVertIds; vtkIdType NumParentVerts; }; // A container of polygons. typedef std::vector PolygonsType; // After subdivision, use this method to get the next point. // Returns the pointId of the point. Returns -1 if no more points. vtkIdType GetNextPoint(double p[3], vtkIdList* parentPointIds = nullptr) { vtkIdType id = -1; if (this->CurrentPointId < this->NumPoints) { for (PolygonsType::iterator it = this->Polygons.begin(); it != this->Polygons.end(); ++it) { if ((*it).GetPointWithId(this->CurrentPointId, p)) { id = this->CurrentPointId; if (parentPointIds) { parentPointIds->Reset(); for (vtkIdType i = 0; i < it->NumParentVerts; i++) { parentPointIds->InsertNextId(it->ParentVertIds[i]); } } } } } this->CurrentPointId++; return id; } // After subdivision, methods to get the next cell (polygon) point Ids. // Returns false if no more cells. vtkIdType* GetNextCell(vtkIdType& numVerts /* number of verts in the returned ids */) { vtkIdType* vertIds = nullptr; numVerts = 0; if (this->PolygonsIterator != this->Polygons.end()) { vertIds = this->PolygonsIterator->VertIds; numVerts = this->PolygonsIterator->NumVerts; ++this->PolygonsIterator; } return vertIds; } // Subdivide a triangle. Returns a container containing 3 new triangles. PolygonsType Subdivide(Polygon& t) { PolygonsType polygons; // Can't subdivide a polygon with less than 3 vertices ! It will be passed // through to the output. if (t.NumVerts < 3) { polygons.push_back(t); return polygons; } // Subdivide the polygon by fanning out triangles from the centroid of the // polygon over to each of the vertices of the polygon. double centroid[3]; t.GetCentroid(centroid); for (vtkIdType i = 0; i < t.NumVerts; i++) { vtkIdType id1 = i, id2 = (i + 1) % (t.NumVerts), id3 = this->NumPoints; // verts for the new triangle. double verts[9] = { t.Verts[3 * id1], t.Verts[3 * id1 + 1], t.Verts[3 * id1 + 2], t.Verts[3 * id2], t.Verts[3 * id2 + 1], t.Verts[3 * id2 + 2], centroid[0], centroid[1], centroid[2] }; vtkIdType vertIds[3] = { t.VertIds[id1], t.VertIds[id2], id3 }; polygons.emplace_back(verts, 3, vertIds, t.NumVerts, t.VertIds); } this->NumPoints++; return polygons; } // Subdivide each polygon in a container of polygons once. PolygonsType Subdivide(PolygonsType& polygons) { PolygonsType newPolygons; for (PolygonsType::iterator it = polygons.begin(); it != polygons.end(); ++it) { PolygonsType output = this->Subdivide(*it); for (PolygonsType::iterator it2 = output.begin(); it2 != output.end(); ++it2) { newPolygons.push_back(*it2); } } return newPolygons; } private: PolygonsType Polygons; vtkIdType NumPoints; vtkIdType StartPointId; vtkIdType CurrentPointId; PolygonsType::iterator PolygonsIterator; }; //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkDensifyPolyData); //------------------------------------------------------------------------------ vtkDensifyPolyData::vtkDensifyPolyData() { this->NumberOfSubdivisions = 1; this->SetNumberOfInputPorts(1); } //------------------------------------------------------------------------------ vtkDensifyPolyData::~vtkDensifyPolyData() = default; //------------------------------------------------------------------------------ int vtkDensifyPolyData::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 vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkCellArray* inputPolys = input->GetPolys(); vtkPoints* inputPoints = input->GetPoints(); if (!inputPolys || !inputPoints) { vtkWarningMacro("vtkDensifyPolyData has no points/cells to linearly interpolate."); return 0; } vtkIdType npts = 0; const vtkIdType* ptIds = nullptr; input->BuildLinks(); const vtkIdType inputNumCells = input->GetNumberOfCells(); const vtkIdType inputNumPoints = input->GetNumberOfPoints(); vtkCellArray* outputPolys = vtkCellArray::New(); // Deep copy the input points. We will then add more points this during // subdivision. vtkPoints* outputPoints = vtkPoints::New(); outputPoints->DeepCopy(inputPoints); // Will be at least that big.. in reality much larger.. outputPolys->AllocateEstimate(inputNumCells, 3); // Copy pointdata structure from input. There will be at least as many // points as in the input. vtkPointData* inputPD = input->GetPointData(); vtkCellData* inputCD = input->GetCellData(); vtkPointData* outputPD = output->GetPointData(); vtkCellData* outputCD = output->GetCellData(); outputPD->DeepCopy(inputPD); // Copy celldata structure from input. There will be at least as many cells // in the output as in the input. outputCD->CopyStructure(inputCD); outputCD->CopyAllocate(outputCD, inputNumCells); double q[3]; vtkIdType outputNumPoints = inputNumPoints, cellId = 0, ptId; vtkIdList* parentPointIds = vtkIdList::New(); // var containing number of cells in the output vtkIdType outputNumCells = 0; for (inputPolys->InitTraversal(); inputPolys->GetNextCell(npts, ptIds); cellId++) { // for every cell if (this->CheckAbort()) { break; } // Make sure that the polygon is a planar polygon. int cellType = input->GetCellType(cellId); if (cellType != VTK_POLYGON && cellType != VTK_QUAD && cellType != VTK_TRIANGLE) { // Only triangles are subdivided, the others are simply passed through // to the output. vtkIdType newCellId = outputPolys->InsertNextCell(npts, ptIds); ++outputNumCells; outputCD->CopyAllocate(outputCD, outputNumCells); outputCD->CopyData(inputCD, cellId, newCellId); continue; } double triangleOrQuadPoints[4 * 3]; // points of the cell (triangle or quad) double* p; if (cellType == VTK_POLYGON) { p = new double[npts * 3]; } else { p = triangleOrQuadPoints; } for (vtkIdType j = 0; j < npts; j++) { inputPoints->GetPoint(ptIds[j], p + (3 * j)); } // Check constraints.. unsigned int nSubdivisions = VTK_UNSIGNED_INT_MAX; if (this->NumberOfSubdivisions > 0) { nSubdivisions = std::min(nSubdivisions, this->NumberOfSubdivisions); } if (nSubdivisions == 0 || nSubdivisions == VTK_UNSIGNED_INT_MAX) { // No need to subdivide.. just keep the same cell.. vtkIdType newCellId = outputPolys->InsertNextCell(npts, ptIds); ++outputNumCells; outputCD->CopyAllocate(outputCD, outputNumCells); outputCD->CopyData(inputCD, cellId, newCellId); } else { // Subdivide the triangle. // The number of new cells formed due to the subdivision of this triangle // = nPts * pow(3, nSubdivisions-1) outputNumCells += (npts * static_cast(pow(3.0, static_cast(nSubdivisions - 1)))); // Ensure that we have enough space to hold the new cell data. (This does // not actually resize the array at every step of the iteration.) It will // end up resizing when // outputNumCells = { 2*inputNumCells, 4*inputNumCells, 8*inputNumCells, // 16*inputNumCells, .... }. outputCD->CopyAllocate(outputCD, outputNumCells); vtkDensifyPolyDataInternals polygons(p, npts, ptIds, outputNumPoints, nSubdivisions); // Insert points and cells generated by subdividing this polygon // nSubdivisions times. Generate the point data and the cell data // for the new cell points and cells. outputPD->CopyAllocate(outputPD, outputNumPoints); while ((ptId = polygons.GetNextPoint(q, parentPointIds)) != -1) { // Interpolation weights for interpolating point data at the // subdivided polygon vtkIdType nParentVerts = parentPointIds->GetNumberOfIds(); double* interpolationWeights = new double[nParentVerts]; double weight = 1.0 / static_cast(nParentVerts); for (vtkIdType i = 0; i < nParentVerts; i++) { interpolationWeights[i] = weight; } outputPoints->InsertNextPoint(q); outputPD->InterpolatePoint(inputPD, ptId, parentPointIds, interpolationWeights); delete[] interpolationWeights; } vtkIdType numNewCellVerts; while (vtkIdType* newCellVertIds = polygons.GetNextCell(numNewCellVerts)) { vtkIdType newCellId = outputPolys->InsertNextCell(numNewCellVerts, newCellVertIds); outputCD->CopyData(inputCD, cellId, newCellId); } } // else if (cellType == VTK_POLYGON) { delete[] p; } } // for every cell output->SetPoints(outputPoints); output->SetPolys(outputPolys); outputPolys->Delete(); outputPoints->Delete(); parentPointIds->Delete(); return 1; } //------------------------------------------------------------------------------ int vtkDensifyPolyData::FillInputPortInformation(int port, vtkInformation* info) { if (port == 0) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); } else { return 0; } return 1; } //------------------------------------------------------------------------------ void vtkDensifyPolyData::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Number of Subdivisions: " << this->NumberOfSubdivisions << endl; } VTK_ABI_NAMESPACE_END