// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkQuadRotationalExtrusionFilter.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkCompositeDataIterator.h" #include "vtkCompositeDataSet.h" #include "vtkGenericCell.h" #include "vtkIdList.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkMultiBlockDataSet.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkQuadRotationalExtrusionFilter); //------------------------------------------------------------------------------ // Create object with capping on, angle of 360 degrees, resolution = 12, and // no translation along z-axis. // vector (0,0,1), and point (0,0,0). vtkQuadRotationalExtrusionFilter::vtkQuadRotationalExtrusionFilter() { this->Axis = 2; this->Capping = 1; this->DefaultAngle = 360.0; this->DeltaRadius = 0.0; this->Translation = 0.0; this->Resolution = 12; // 30 degree increments } //------------------------------------------------------------------------------ int vtkQuadRotationalExtrusionFilter::FillInputPortInformation(int, vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCompositeDataSet"); return 1; } //------------------------------------------------------------------------------ int vtkQuadRotationalExtrusionFilter::RotateAroundAxis(double blockAngle, vtkIdType numPts, vtkPoints* inPts, vtkPoints* newPts, vtkPointData* pd, vtkPointData* outPD) { // Calculate quantities necessary to the sweep operation int idx1; int idx2; if (!this->Axis) { idx1 = 1; idx2 = 2; } else if (this->Axis == 1) { idx1 = 0; idx2 = 2; } else if (this->Axis == 2) { idx1 = 0; idx2 = 1; } else { vtkErrorMacro(<< "Invalid axis number: " << this->Axis << "\n"); return 0; } double radIncr = this->DeltaRadius / this->Resolution; double transIncr = this->Translation / this->Resolution; double angleIncr = vtkMath::RadiansFromDegrees(blockAngle) / this->Resolution; bool abort = false; // Sweep over set resolution for (int i = 1; i <= this->Resolution && !abort; ++i) { this->UpdateProgress(.1 + .5 * (i - 1) / this->Resolution); for (vtkIdType ptId = 0; ptId < numPts; ++ptId) { if (this->CheckAbort()) { abort = true; break; } // Get point coordinates double x[3]; inPts->GetPoint(ptId, x); // Convert to cylindrical coordinates double newX[3]; double radius = sqrt(x[idx1] * x[idx1] + x[idx2] * x[idx2]); if (radius > 0.) { double tempd = x[idx1] / radius; tempd = std::max(tempd, -1.); tempd = std::min(tempd, 1.); double theta = acos(tempd); tempd = x[idx2] / radius; tempd = std::max(tempd, -1.); tempd = std::min(tempd, 1.); double psi = asin(tempd); if (psi < 0.) { if (theta < (vtkMath::Pi() / 2.)) { theta = 2. * vtkMath::Pi() + psi; } else { theta = vtkMath::Pi() - psi; } } // increment angle radius += i * radIncr; newX[this->Axis] = x[this->Axis] + i * transIncr; newX[idx1] = radius * cos(i * angleIncr + theta); newX[idx2] = radius * sin(i * angleIncr + theta); } else // radius is zero { newX[this->Axis] = x[this->Axis] + i * transIncr; newX[idx1] = 0.; newX[idx2] = 0.; } // Update swept mesh newPts->InsertPoint(ptId + i * numPts, newX); outPD->CopyData(pd, ptId, ptId + i * numPts); } } return 1; } //------------------------------------------------------------------------------ int vtkQuadRotationalExtrusionFilter::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the info objects vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); // Get composite input vtkCompositeDataSet* compositeInput = vtkCompositeDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); // Get typed output vtkMultiBlockDataSet* compositeOutput = vtkMultiBlockDataSet::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); if (compositeInput == nullptr || compositeOutput == nullptr) { vtkErrorMacro(<< "Invalid algorithm connection\n"); return 0; } vtkDebugMacro(<< "input=" << compositeInput->GetClassName() << "\n"); // we want the output to have the same structure than the input compositeOutput->CopyStructure(compositeInput); // allocate composite iterator vtkCompositeDataIterator* inputIterator = compositeInput->NewIterator(); inputIterator->SkipEmptyNodesOff(); inputIterator->InitTraversal(); vtkCompositeDataIterator* outputIterator = compositeOutput->NewIterator(); outputIterator->SkipEmptyNodesOff(); outputIterator->InitTraversal(); while (inputIterator->IsDoneWithTraversal() == 0) { if (this->CheckAbort()) { break; } // get the input and output int blockId = inputIterator->GetCurrentFlatIndex(); vtkPolyData* input = vtkPolyData::SafeDownCast(inputIterator->GetCurrentDataObject()); inputIterator->GoToNextItem(); if (!input) { outputIterator->GoToNextItem(); continue; } vtkPolyData* output = vtkPolyData::New(); compositeOutput->SetDataSet(outputIterator, output); outputIterator->GoToNextItem(); output->Delete(); vtkIdType numPts, numCells; numPts = input->GetNumberOfPoints(); numCells = input->GetNumberOfCells(); if (numPts > 0 && numCells > 0) { // Retrieve angle for each block, or angle by default double blockAngle = this->GetDefaultAngle(); vtkDebugMacro(<< "DefaultAngle=" << blockAngle << "\n"); std::map::iterator amit = this->PerBlockAngles.find(blockId); if (amit != this->PerBlockAngles.end()) { vtkDebugMacro(<< "Found angle " << amit->second << " for block " << blockId << "\n"); blockAngle = amit->second; } vtkDebugMacro(<< "process block " << blockId << ", angle=" << blockAngle << ", resolution=" << this->Resolution << ", obj=" << output << "\n"); vtkPointData* pd = input->GetPointData(); vtkCellData* cd = input->GetCellData(); vtkPolyData* mesh; vtkPoints* inPts; vtkCellArray *inVerts, *inLines, *inPolys, *inStrips; int numEdges; const vtkIdType* pts = nullptr; vtkIdType ptId, ncells; vtkPoints* newPts; vtkCellArray *newLines = nullptr, *newPolys, *newStrips = nullptr; vtkCell* edge; vtkIdList* cellIds; vtkIdType p1, p2; vtkPointData* outPD = output->GetPointData(); vtkCellData* outCD = output->GetCellData(); int abort = 0; // Initialize / check input vtkDebugMacro(<< "Rotationally extruding data"); // Build cell data structure. mesh = vtkPolyData::New(); inPts = input->GetPoints(); inVerts = input->GetVerts(); inLines = input->GetLines(); inPolys = input->GetPolys(); inStrips = input->GetStrips(); mesh->SetPoints(inPts); mesh->SetVerts(inVerts); mesh->SetLines(inLines); mesh->SetPolys(inPolys); mesh->SetStrips(inStrips); if (inPolys || inStrips) { mesh->BuildLinks(); } // Allocate memory for output. We don't copy normals because surface geometry // is modified. outPD->CopyNormalsOff(); outPD->CopyAllocate(pd, (this->Resolution + 1) * numPts); newPts = vtkPoints::New(); newPts->Allocate((this->Resolution + 1) * numPts); if ((ncells = inVerts->GetNumberOfCells()) > 0) { newLines = vtkCellArray::New(); newLines->AllocateEstimate(ncells, this->Resolution + 1); } // arbitrary initial allocation size ncells = inLines->GetNumberOfCells() + inPolys->GetNumberOfCells() / 10 + inStrips->GetNumberOfCells() / 10; ncells = (ncells < 100 ? 100 : ncells); newPolys = vtkCellArray::New(); newPolys->AllocateEstimate(ncells, 2 * (this->Resolution + 1)); outCD->CopyNormalsOff(); outCD->CopyAllocate(cd, ncells); // copy points for (ptId = 0; ptId < numPts; ptId++) // base level { newPts->InsertPoint(ptId, inPts->GetPoint(ptId)); outPD->CopyData(pd, ptId, ptId); } this->UpdateProgress(0.1); // Rotate around selected axis if (!this->RotateAroundAxis(blockAngle, numPts, inPts, newPts, pd, outPD)) { return 0; } // To ensure that cell attributes are in consistent order with the // cellId's, we process the verts, lines, polys and strips in order. vtkIdType newCellId = 0; int type; vtkIdType npts = 0; if (newLines) // there are verts which produce lines { for (vtkIdType cellId = 0; cellId < numCells && !abort; ++cellId) { if (this->CheckAbort()) { abort = true; break; } type = mesh->GetCellType(cellId); if (type == VTK_VERTEX || type == VTK_POLY_VERTEX) { mesh->GetCellPoints(cellId, npts, pts); for (int i = 0; i < npts; ++i) { ptId = pts[i]; newLines->InsertNextCell(this->Resolution + 1); for (int j = 0; j <= this->Resolution; ++j) { newLines->InsertCellPoint(ptId + j * numPts); } outCD->CopyData(cd, cellId, newCellId++); } } // if a vertex or polyVertex } // for all cells } // if there are verts generating lines this->UpdateProgress(0.25); abort = this->CheckAbort(); // If capping is on, copy 2D cells to output ( plus create cap ). Notice // that polygons are done first, then strips. // if (this->Capping && (blockAngle != 360.0 || this->DeltaRadius != 0.0 || this->Translation != 0.0)) { if (inPolys->GetNumberOfCells() > 0) { // newPolys = vtkCellArray::New(); // newPolys->Allocate( inPolys->GetSize() ); for (vtkIdType cellId = 0; cellId < numCells && !abort; ++cellId) { if (this->CheckAbort()) { abort = true; break; } type = mesh->GetCellType(cellId); if (type == VTK_TRIANGLE || type == VTK_QUAD || type == VTK_POLYGON) { mesh->GetCellPoints(cellId, npts, pts); newPolys->InsertNextCell(npts, pts); outCD->CopyData(cd, cellId, newCellId++); newPolys->InsertNextCell(npts); for (int i = 0; i < npts; ++i) { vtkIdType idx = pts[i] + this->Resolution * numPts; newPolys->InsertCellPoint(idx); } outCD->CopyData(cd, cellId, newCellId++); } } } if (inStrips->GetNumberOfCells() > 0) { newStrips = vtkCellArray::New(); newStrips->AllocateCopy(inStrips); for (vtkIdType cellId = 0; cellId < numCells && !abort; ++cellId) { if (this->CheckAbort()) { abort = true; break; } type = mesh->GetCellType(cellId); if (type == VTK_TRIANGLE_STRIP) { mesh->GetCellPoints(cellId, npts, pts); newStrips->InsertNextCell(npts, pts); outCD->CopyData(cd, cellId, newCellId++); newStrips->InsertNextCell(npts); for (int i = 0; i < npts; ++i) { vtkIdType idx = pts[i] + this->Resolution * numPts; newStrips->InsertCellPoint(idx); } outCD->CopyData(cd, cellId, newCellId++); } } } } // if capping this->UpdateProgress(0.5); abort = this->CheckAbort(); // Now process lines, polys and/or strips to produce strips // if (inLines->GetNumberOfCells() || inPolys->GetNumberOfCells() || inStrips->GetNumberOfCells()) { cellIds = vtkIdList::New(); cellIds->Allocate(VTK_CELL_SIZE); vtkGenericCell* cell = vtkGenericCell::New(); for (vtkIdType cellId = 0; cellId < numCells && !abort; ++cellId) { if (this->CheckAbort()) { abort = true; break; } type = mesh->GetCellType(cellId); if (type == VTK_LINE || type == VTK_POLY_LINE) { mesh->GetCellPoints(cellId, npts, pts); for (int i = 0; i < (npts - 1); ++i) { p1 = pts[i]; p2 = pts[i + 1]; vtkIdType idxPt; for (int k = 0; k < this->Resolution; ++k) { newPolys->InsertNextCell(4); idxPt = p1 + k * numPts; newPolys->InsertCellPoint(idxPt); idxPt = p2 + k * numPts; newPolys->InsertCellPoint(idxPt); idxPt = p2 + (k + 1) * numPts; newPolys->InsertCellPoint(idxPt); idxPt = p1 + (k + 1) * numPts; newPolys->InsertCellPoint(idxPt); outCD->CopyData(cd, cellId, newCellId++); } } } // if a line else if (type == VTK_TRIANGLE || type == VTK_QUAD || type == VTK_POLYGON || type == VTK_TRIANGLE_STRIP) { // create strips from boundary edges mesh->GetCell(cellId, cell); numEdges = cell->GetNumberOfEdges(); for (int i = 0; i < numEdges; ++i) { edge = cell->GetEdge(i); for (int j = 0; j < (edge->GetNumberOfPoints() - 1); ++j) { p1 = edge->PointIds->GetId(j); p2 = edge->PointIds->GetId(j + 1); mesh->GetCellEdgeNeighbors(cellId, p1, p2, cellIds); if (cellIds->GetNumberOfIds() < 1) // generate strip { vtkIdType idxPt; for (int k = 0; k < this->Resolution; ++k) { newPolys->InsertNextCell(4); idxPt = p1 + k * numPts; newPolys->InsertCellPoint(idxPt); idxPt = p2 + k * numPts; newPolys->InsertCellPoint(idxPt); idxPt = p2 + (k + 1) * numPts; newPolys->InsertCellPoint(idxPt); idxPt = p1 + (k + 1) * numPts; newPolys->InsertCellPoint(idxPt); outCD->CopyData(cd, cellId, newCellId++); } } // if boundary edge } // for each sub-edge } // for each edge } // for each polygon or triangle strip } // for all cells cellIds->Delete(); cell->Delete(); } // if strips are being generated this->UpdateProgress(1.00); // Update ourselves and release memory output->SetPoints(newPts); newPts->Delete(); mesh->Delete(); if (newLines) { output->SetLines(newLines); newLines->Delete(); } if (newPolys) { output->SetPolys(newPolys); newPolys->Delete(); } if (newStrips) { output->SetStrips(newStrips); newStrips->Delete(); } output->Squeeze(); } /* if numPts>0 && numCells>0 */ } /* Iterate over input blocks */ inputIterator->Delete(); outputIterator->Delete(); return 1; } //------------------------------------------------------------------------------ void vtkQuadRotationalExtrusionFilter::RemoveAllPerBlockAngles() { vtkDebugMacro(<< "RemoveAllPerBlockAngles\n"); this->PerBlockAngles.clear(); this->Modified(); } //------------------------------------------------------------------------------ void vtkQuadRotationalExtrusionFilter::AddPerBlockAngle(vtkIdType blockId, double angle) { vtkDebugMacro(<< "PerBlockAngles[" << blockId << "]=" << angle << "\n"); this->PerBlockAngles[blockId] = angle; this->Modified(); } //------------------------------------------------------------------------------ void vtkQuadRotationalExtrusionFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Axis: " << this->Axis << "\n"; os << indent << "Resolution: " << this->Resolution << "\n"; os << indent << "Capping: " << (this->Capping ? "On\n" : "Off\n"); os << indent << "DefaultAngle: " << this->DefaultAngle << "\n"; os << indent << "Translation: " << this->Translation << "\n"; os << indent << "Delta Radius: " << this->DeltaRadius << "\n"; os << indent << "PerBlockAngles:\n"; for (std::map::iterator it = this->PerBlockAngles.begin(); it != this->PerBlockAngles.end(); ++it) { os << indent.GetNextIndent() << "Block #" << it->first << " -> " << it->second << "\n"; } } VTK_ABI_NAMESPACE_END