// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkVolumeOfRevolutionFilter.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkCellIterator.h" #include "vtkGenericCell.h" #include "vtkIdList.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkMergePoints.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkUnsignedCharArray.h" #include "vtkUnstructuredGrid.h" #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkVolumeOfRevolutionFilter); namespace { struct AxisOfRevolution { double Position[3]; double Direction[3]; }; void RevolvePoint( const double in[3], const AxisOfRevolution* axis, double angleInRadians, double out[3]) { double c = cos(angleInRadians); double cm = 1. - c; double s = sin(angleInRadians); double translated[3]; vtkMath::Subtract(in, axis->Position, translated); double dot = vtkMath::Dot(translated, axis->Direction); double cross[3]; vtkMath::Cross(translated, axis->Direction, cross); for (vtkIdType i = 0; i < 3; i++) { out[i] = ((translated[i] * c + axis->Direction[i] * dot * cm - cross[i] * s) + axis->Position[i]); } } void RevolvePoints(vtkDataSet* pts, vtkPoints* newPts, AxisOfRevolution* axis, double sweepAngle, int resolution, vtkPointData* outPd, bool partialSweep) { double angleInRadians = vtkMath::RadiansFromDegrees(sweepAngle / resolution); vtkIdType n2DPoints = pts->GetNumberOfPoints(); vtkIdType counter = 0; double p2d[3], p3d[3]; for (int i = 0; i < resolution + partialSweep; i++) { for (int id = 0; id < n2DPoints; id++) { pts->GetPoint(id, p2d); RevolvePoint(p2d, axis, i * angleInRadians, p3d); newPts->SetPoint(counter, p3d); outPd->CopyData(pts->GetPointData(), id, counter); counter++; } } } template void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep); template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { vtkIdType newPtIds[2], newCellId; newPtIds[0] = pointIds->GetId(0); for (int i = 0; i < resolution; i++) { newPtIds[1] = (pointIds->GetId(0) + ((i + 1) % (resolution + partialSweep)) * n2DPoints); newCellId = connectivity->InsertNextCell(2, newPtIds); types->InsertNextValue(VTK_LINE); outCd->CopyData(inCd, cellId, newCellId); newPtIds[0] = newPtIds[1]; } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { vtkNew pointId; pointId->SetNumberOfIds(1); for (vtkIdType i = 0; i < pointIds->GetNumberOfIds(); i++) { pointId->SetId(0, pointIds->GetId(i)); Revolve( pointId, n2DPoints, resolution, connectivity, types, inCd, cellId, outCd, partialSweep); } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { static const int nPoints = 2; vtkIdType newPtIds[2 * nPoints], newCellId; for (vtkIdType i = 0; i < nPoints; i++) { newPtIds[i] = pointIds->GetId(i); } for (int i = 0; i < resolution; i++) { for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[2 * nPoints - 1 - j] = pointIds->GetId(j) + ((i + 1) % (resolution + partialSweep)) * n2DPoints; } newCellId = connectivity->InsertNextCell(2 * nPoints, newPtIds); types->InsertNextValue(VTK_QUAD); outCd->CopyData(inCd, cellId, newCellId); for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[nPoints - 1 - j] = newPtIds[j + nPoints]; } } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { vtkNew newPointIds; newPointIds->SetNumberOfIds(2); newPointIds->SetId(0, pointIds->GetId(0)); for (vtkIdType i = 1; i < pointIds->GetNumberOfIds(); i++) { newPointIds->SetId(1, pointIds->GetId(i)); Revolve( newPointIds, n2DPoints, resolution, connectivity, types, inCd, cellId, outCd, partialSweep); newPointIds->SetId(0, pointIds->GetId(i)); } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { static const int nPoints = 3; vtkIdType newPtIds[2 * nPoints], newCellId; for (vtkIdType i = 0; i < nPoints; i++) { newPtIds[i] = pointIds->GetId(i); } for (int i = 0; i < resolution; i++) { for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[j + nPoints] = pointIds->GetId(j) + ((i + 1) % (resolution + partialSweep)) * n2DPoints; } newCellId = connectivity->InsertNextCell(2 * nPoints, newPtIds); types->InsertNextValue(VTK_WEDGE); outCd->CopyData(inCd, cellId, newCellId); for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[j] = newPtIds[j + nPoints]; } } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { vtkNew newPointIds; newPointIds->SetNumberOfIds(3); newPointIds->SetId(0, pointIds->GetId(0)); newPointIds->SetId(1, pointIds->GetId(1)); for (vtkIdType i = 2; i < pointIds->GetNumberOfIds(); i++) { newPointIds->SetId(2, pointIds->GetId(i)); Revolve( newPointIds, n2DPoints, resolution, connectivity, types, inCd, cellId, outCd, partialSweep); newPointIds->SetId(0, pointIds->GetId(i)); newPointIds->SetId(1, pointIds->GetId(i - 1)); } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { static const int nPoints = 4; vtkIdType newPtIds[2 * nPoints], newCellId; for (vtkIdType i = 0; i < nPoints; i++) { newPtIds[i] = pointIds->GetId(i); } for (int i = 0; i < resolution; i++) { for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[j + nPoints] = pointIds->GetId(j) + ((i + 1) % (resolution + partialSweep)) * n2DPoints; } newCellId = connectivity->InsertNextCell(2 * nPoints, newPtIds); types->InsertNextValue(VTK_HEXAHEDRON); outCd->CopyData(inCd, cellId, newCellId); for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[j] = newPtIds[j + nPoints]; } } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { static const int nPoints = 4; vtkIdType newPtIds[2 * nPoints], newCellId; for (vtkIdType i = 0; i < nPoints; i++) { newPtIds[i] = pointIds->GetId(i); } for (int i = 0; i < resolution; i++) { for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[j + nPoints] = pointIds->GetId(j) + ((i + 1) % (resolution + partialSweep)) * n2DPoints; } newCellId = connectivity->InsertNextCell(2 * nPoints, newPtIds); types->InsertNextValue(VTK_HEXAHEDRON); outCd->CopyData(inCd, cellId, newCellId); for (vtkIdType j = 0; j < nPoints; j++) { newPtIds[j] = newPtIds[j + nPoints]; } } } template <> void Revolve(vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { // A swept polygon creates a polyhedron with two polygon faces and // quad faces, comprised from 2* points. Because polyhedra have a // special connectivity format, the length of the connectivity array is // 1 + (+2) + 2* + 4* = 7* + 3. // ^ ^ ^ ^ // integer describing # of faces ( + 2) // ^ ^ ^ // integers describing # of points per face // ^ ^ // point ids for the two polygon faces // ^ // point ids for the 4 quad faces int nPoly = pointIds->GetNumberOfIds(); std::vector newPtIds(7 * nPoly + 3); // newFacePtIds are pointers to the point arrays describing each face std::vector newFacePtIds(nPoly + 2); vtkIdType newCellId; newPtIds[0] = nPoly + 2; newPtIds[1] = nPoly; newPtIds[nPoly + 2] = nPoly; newFacePtIds[0] = &newPtIds[2]; newFacePtIds[1] = &newPtIds[nPoly + 3]; for (vtkIdType i = 0; i < nPoly; i++) { // All of the subsequent faces have four point ids newPtIds[3 + 2 * nPoly + 5 * i] = 4; newFacePtIds[2 + i] = &newPtIds[4 + 2 * nPoly + 5 * i]; newFacePtIds[0][i] = pointIds->GetId(i); } for (int i = 0; i < resolution; i++) { for (vtkIdType j = 0; j < nPoly; j++) { newFacePtIds[1][nPoly - 1 - j] = pointIds->GetId(j) + ((i + 1) % (resolution + partialSweep)) * n2DPoints; } for (vtkIdType j = 0; j < nPoly; j++) { newFacePtIds[j + 2][0] = newFacePtIds[0][j]; newFacePtIds[j + 2][1] = newFacePtIds[0][(j + 1) % nPoly]; newFacePtIds[j + 2][2] = newFacePtIds[1][(2 * nPoly - 2 - j) % nPoly]; newFacePtIds[j + 2][3] = newFacePtIds[1][nPoly - 1 - j]; } newCellId = connectivity->InsertNextCell(7 * nPoly + 3, newPtIds.data()); types->InsertNextValue(VTK_POLYHEDRON); outCd->CopyData(inCd, cellId, newCellId); for (vtkIdType j = 0; j < nPoly; j++) { newFacePtIds[0][j] = newFacePtIds[1][nPoly - 1 - j]; } } } int RevolveCell(int cellType, vtkIdList* pointIds, vtkIdType n2DPoints, int resolution, vtkCellArray* connectivity, vtkUnsignedCharArray* types, vtkCellData* inCd, vtkIdType cellId, vtkCellData* outCd, bool partialSweep) { int returnValue = 0; #define RevolveCellCase(CellType) \ case CellType: \ Revolve( \ pointIds, n2DPoints, resolution, connectivity, types, inCd, cellId, outCd, partialSweep); \ break switch (cellType) { RevolveCellCase(VTK_VERTEX); RevolveCellCase(VTK_POLY_VERTEX); RevolveCellCase(VTK_LINE); RevolveCellCase(VTK_POLY_LINE); RevolveCellCase(VTK_TRIANGLE); RevolveCellCase(VTK_TRIANGLE_STRIP); RevolveCellCase(VTK_POLYGON); RevolveCellCase(VTK_PIXEL); RevolveCellCase(VTK_QUAD); default: returnValue = 1; } return returnValue; #undef RevolveCellCase } } //------------------------------------------------------------------------------ vtkVolumeOfRevolutionFilter::vtkVolumeOfRevolutionFilter() { this->SweepAngle = 360.0; this->Resolution = 12; // 30 degree increments this->AxisPosition[0] = this->AxisPosition[1] = this->AxisPosition[2] = 0.; this->AxisDirection[0] = this->AxisDirection[1] = 0.; this->AxisDirection[2] = 1.; this->OutputPointsPrecision = DEFAULT_PRECISION; } //------------------------------------------------------------------------------ vtkVolumeOfRevolutionFilter::~vtkVolumeOfRevolutionFilter() = default; //------------------------------------------------------------------------------ int vtkVolumeOfRevolutionFilter::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())); vtkPointData* inPd = input->GetPointData(); vtkCellData* inCd = input->GetCellData(); vtkPointData* outPd = output->GetPointData(); vtkCellData* outCd = output->GetCellData(); vtkNew outPts; // Check to see that the input data is amenable to this operation vtkCellIterator* it = input->NewCellIterator(); for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) { int cellDimension = it->GetCellDimension(); if (cellDimension > 2) { vtkErrorMacro(<< "All cells must have a topological dimension < 2."); return 1; } } it->Delete(); // Set up output points if (this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION) { vtkPointSet* inputPointSet = vtkPointSet::SafeDownCast(input); if (inputPointSet) { outPts->SetDataType(inputPointSet->GetPoints()->GetDataType()); } else { outPts->SetDataType(VTK_FLOAT); } } else if (this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION) { outPts->SetDataType(VTK_FLOAT); } else if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION) { outPts->SetDataType(VTK_DOUBLE); } // determine whether or not the sweep angle is a full 2*pi bool partialSweep = false; if (fabs(360. - fabs(this->SweepAngle)) > 1024 * VTK_DBL_EPSILON) { partialSweep = true; } // Set up output points and point data outPts->SetNumberOfPoints(input->GetNumberOfPoints() * (this->Resolution + partialSweep)); outPd->CopyAllocate(inPd, input->GetNumberOfPoints() * (this->Resolution + partialSweep)); // Set up output cell data vtkIdType nNewCells = input->GetNumberOfCells() * this->Resolution; outCd->CopyAllocate(inCd, nNewCells); vtkNew outTypes; vtkNew outCells; AxisOfRevolution axis; for (vtkIdType i = 0; i < 3; i++) { axis.Position[i] = this->AxisPosition[i]; axis.Direction[i] = this->AxisDirection[i]; } RevolvePoints(input, outPts, &axis, this->SweepAngle, this->Resolution, outPd, partialSweep); it = input->NewCellIterator(); for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell()) { if (this->CheckAbort()) { break; } if (RevolveCell(it->GetCellType(), it->GetPointIds(), input->GetNumberOfPoints(), this->Resolution, outCells, outTypes, inCd, it->GetCellId(), outCd, partialSweep) == 1) { vtkWarningMacro(<< "No method for revolving cell type " << it->GetCellType() << ". Skipping."); } } it->Delete(); output->SetPoints(outPts); output->SetCells(outTypes, outCells); return 1; } //------------------------------------------------------------------------------ int vtkVolumeOfRevolutionFilter::FillInputPortInformation(int, vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); return 1; } //------------------------------------------------------------------------------ void vtkVolumeOfRevolutionFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Resolution: " << this->Resolution << "\n"; os << indent << "Sweep Angle: " << this->SweepAngle << "\n"; os << indent << "Axis Position: (" << this->AxisPosition[0] << "," << this->AxisPosition[1] << "," << this->AxisPosition[2] << ")\n"; os << indent << "Axis Direction: (" << this->AxisPosition[0] << "," << this->AxisDirection[1] << "," << this->AxisDirection[2] << ")\n"; os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n"; } VTK_ABI_NAMESPACE_END