// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkLinearExtrusionFilter.h" #include "vtkCell.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkGenericCell.h" #include "vtkIdList.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkLinearExtrusionFilter); //------------------------------------------------------------------------------ // Create object with normal extrusion type, capping on, scale factor=1.0, // vector (0,0,1), and extrusion point (0,0,0). vtkLinearExtrusionFilter::vtkLinearExtrusionFilter() { this->ExtrusionType = VTK_NORMAL_EXTRUSION; this->Capping = 1; this->ScaleFactor = 1.0; this->Vector[0] = this->Vector[1] = 0.0; this->Vector[2] = 1.0; this->ExtrusionPoint[0] = this->ExtrusionPoint[1] = this->ExtrusionPoint[2] = 0.0; } //------------------------------------------------------------------------------ void vtkLinearExtrusionFilter::ViaNormal(double x[3], vtkIdType id, vtkDataArray* n) { double normal[3]; n->GetTuple(id, normal); for (vtkIdType i = 0; i < 3; i++) { x[i] = x[i] + this->ScaleFactor * normal[i]; } } //------------------------------------------------------------------------------ void vtkLinearExtrusionFilter::ViaVector( double x[3], vtkIdType vtkNotUsed(id), vtkDataArray* vtkNotUsed(n)) { for (vtkIdType i = 0; i < 3; i++) { x[i] = x[i] + this->ScaleFactor * this->Vector[i]; } } //------------------------------------------------------------------------------ void vtkLinearExtrusionFilter::ViaPoint( double x[3], vtkIdType vtkNotUsed(id), vtkDataArray* vtkNotUsed(n)) { for (vtkIdType i = 0; i < 3; i++) { x[i] = x[i] + this->ScaleFactor * (x[i] - this->ExtrusionPoint[i]); } } //------------------------------------------------------------------------------ int vtkLinearExtrusionFilter::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())); vtkIdType numPts, numCells; vtkPointData* pd = input->GetPointData(); vtkDataArray* inNormals = nullptr; vtkPolyData* mesh; vtkPoints* inPts; vtkCellArray *inVerts, *inLines, *inPolys, *inStrips; vtkIdType inCellId, outCellId; int numEdges, dim; const vtkIdType* pts = nullptr; vtkIdType npts = 0; vtkIdType ptId, ncells, p1, p2; vtkIdType i, j; double x[3]; vtkPoints* newPts; vtkCellArray *newLines = nullptr, *newPolys = nullptr, *newStrips; vtkCell* edge; vtkIdList *cellIds, *cellPts; vtkPointData* outputPD = output->GetPointData(); // Here is a big pain about ordering of cells. (Copy CellData) vtkIdList* lineIds; vtkIdList* polyIds; vtkIdList* stripIds; // Initialize / check input // vtkDebugMacro(<< "Linearly extruding data"); numPts = input->GetNumberOfPoints(); numCells = input->GetNumberOfCells(); if (numPts < 1 || numCells < 1) { vtkErrorMacro(<< "No data to extrude!"); return 1; } // Decide which vector to use for extrusion // if (this->ExtrusionType == VTK_POINT_EXTRUSION) { this->ExtrudePoint = &vtkLinearExtrusionFilter::ViaPoint; } else if (this->ExtrusionType == VTK_NORMAL_EXTRUSION && (inNormals = pd->GetNormals()) != nullptr) { this->ExtrudePoint = &vtkLinearExtrusionFilter::ViaNormal; inNormals = pd->GetNormals(); } else // VTK_VECTOR_EXTRUSION { this->ExtrudePoint = &vtkLinearExtrusionFilter::ViaVector; } // 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->GetNumberOfCells() || inStrips->GetNumberOfCells()) { mesh->BuildLinks(); } cellIds = vtkIdList::New(); cellIds->Allocate(VTK_CELL_SIZE); // Allocate memory for output. We don't copy normals because surface geometry // is modified. Copy all points - this is the usual requirement and it makes // creation of skirt much easier. // output->GetCellData()->CopyNormalsOff(); output->GetCellData()->CopyAllocate(input->GetCellData(), 3 * input->GetNumberOfCells()); outputPD->CopyNormalsOff(); outputPD->CopyAllocate(pd, 2 * numPts); newPts = vtkPoints::New(); newPts->SetNumberOfPoints(2 * numPts); if ((ncells = inVerts->GetNumberOfCells()) > 0) { newLines = vtkCellArray::New(); newLines->AllocateEstimate(ncells, 2); } // arbitrary initial allocation size ncells = inLines->GetNumberOfCells() + inPolys->GetNumberOfCells() / 10 + inStrips->GetNumberOfCells() / 10; ncells = (ncells < 100 ? 100 : ncells); newStrips = vtkCellArray::New(); newStrips->AllocateEstimate(ncells, 4); vtkIdType progressInterval = numPts / 10 + 1; bool abort = false; // copy points for (ptId = 0; ptId < numPts && !abort; ptId++) { if (!(ptId % progressInterval)) // manage progress / early abort { this->UpdateProgress(0.25 * ptId / numPts); abort = this->CheckAbort(); } inPts->GetPoint(ptId, x); newPts->SetPoint(ptId, x); (this->*(this->ExtrudePoint))(x, ptId, inNormals); newPts->SetPoint(ptId + numPts, x); outputPD->CopyData(pd, ptId, ptId); outputPD->CopyData(pd, ptId, ptId + numPts); } // We need the cellid to copy cell data. Skip points and lines. inCellId = outCellId = 0; if (input->GetVerts()) { inCellId += input->GetVerts()->GetNumberOfCells(); } if (input->GetLines()) { inCellId += input->GetLines()->GetNumberOfCells(); } // We need to keep track of input cell ids used to generate // output cells so that we can copy cell data at the end. // We do not know how many lines, polys and strips we will get // before hand. lineIds = vtkIdList::New(); polyIds = vtkIdList::New(); stripIds = vtkIdList::New(); // If capping is on, copy 2D cells to output (plus create cap) // if (this->Capping) { if (inPolys->GetNumberOfCells() > 0) { newPolys = vtkCellArray::New(); newPolys->AllocateCopy(inPolys); for (inPolys->InitTraversal(); inPolys->GetNextCell(npts, pts);) { newPolys->InsertNextCell(npts, pts); polyIds->InsertNextId(inCellId); newPolys->InsertNextCell(npts); for (i = 0; i < npts; i++) { newPolys->InsertCellPoint(pts[i] + numPts); } polyIds->InsertNextId(inCellId); ++inCellId; } } if (inStrips->GetNumberOfCells() > 0) { for (inStrips->InitTraversal(); inStrips->GetNextCell(npts, pts);) { newStrips->InsertNextCell(npts, pts); stripIds->InsertNextId(inCellId); newStrips->InsertNextCell(npts); for (i = 0; i < npts; i++) { newStrips->InsertCellPoint(pts[i] + numPts); } stripIds->InsertNextId(inCellId); ++inCellId; } } } this->UpdateProgress(0.4); // Loop over all polygons and triangle strips searching for boundary edges. // If boundary edge found, extrude triangle strip. // progressInterval = numCells / 10 + 1; vtkGenericCell* cell = vtkGenericCell::New(); for (inCellId = 0; inCellId < numCells && !abort; inCellId++) { if (!(inCellId % progressInterval)) // manage progress / early abort { this->UpdateProgress(0.4 + 0.6 * inCellId / numCells); abort = this->CheckAbort(); } mesh->GetCell(inCellId, cell); cellPts = cell->GetPointIds(); if ((dim = cell->GetCellDimension()) == 0) // create lines from points { for (i = 0; i < cellPts->GetNumberOfIds(); i++) { newLines->InsertNextCell(2); ptId = cellPts->GetId(i); newLines->InsertCellPoint(ptId); newLines->InsertCellPoint(ptId + numPts); lineIds->InsertNextId(inCellId); } } else if (dim == 1) // create strips from lines { for (i = 0; i < (cellPts->GetNumberOfIds() - 1); i++) { p1 = cellPts->GetId(i); p2 = cellPts->GetId(i + 1); newStrips->InsertNextCell(4); newStrips->InsertCellPoint(p1); newStrips->InsertCellPoint(p2); newStrips->InsertCellPoint(p1 + numPts); newStrips->InsertCellPoint(p2 + numPts); stripIds->InsertNextId(inCellId); } } else if (dim == 2) // create strips from boundary edges { numEdges = cell->GetNumberOfEdges(); for (i = 0; i < numEdges; i++) { edge = cell->GetEdge(i); for (j = 0; j < (edge->GetNumberOfPoints() - 1); j++) { p1 = edge->PointIds->GetId(j); p2 = edge->PointIds->GetId(j + 1); mesh->GetCellEdgeNeighbors(inCellId, p1, p2, cellIds); if (cellIds->GetNumberOfIds() < 1) // generate strip { newStrips->InsertNextCell(4); newStrips->InsertCellPoint(p1); newStrips->InsertCellPoint(p2); newStrips->InsertCellPoint(p1 + numPts); newStrips->InsertCellPoint(p2 + numPts); stripIds->InsertNextId(inCellId); } } // for each sub-edge } // for each edge } // for each polygon or triangle strip } // for each cell cell->Delete(); // Now Copy cell data. outCellId = 0; j = lineIds->GetNumberOfIds(); for (i = 0; i < j; ++i) { output->GetCellData()->CopyData(input->GetCellData(), lineIds->GetId(i), outCellId); ++outCellId; } j = polyIds->GetNumberOfIds(); for (i = 0; i < j; ++i) { output->GetCellData()->CopyData(input->GetCellData(), polyIds->GetId(i), outCellId); ++outCellId; } j = stripIds->GetNumberOfIds(); for (i = 0; i < j; ++i) { output->GetCellData()->CopyData(input->GetCellData(), stripIds->GetId(i), outCellId); ++outCellId; } lineIds->Delete(); stripIds->Delete(); polyIds->Delete(); polyIds = nullptr; // Send data to output and release memory // output->SetPoints(newPts); newPts->Delete(); cellIds->Delete(); mesh->Delete(); if (newLines) { output->SetLines(newLines); newLines->Delete(); } if (newPolys) { output->SetPolys(newPolys); newPolys->Delete(); } output->SetStrips(newStrips); newStrips->Delete(); output->Squeeze(); return 1; } //------------------------------------------------------------------------------ void vtkLinearExtrusionFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); if (this->ExtrusionType == VTK_VECTOR_EXTRUSION) { os << indent << "Extrusion Type: Extrude along vector\n"; os << indent << "Vector: (" << this->Vector[0] << ", " << this->Vector[1] << ", " << this->Vector[2] << ")\n"; } else if (this->ExtrusionType == VTK_NORMAL_EXTRUSION) { os << indent << "Extrusion Type: Extrude along vertex normals\n"; } else // POINT_EXTRUSION { os << indent << "Extrusion Type: Extrude towards point\n"; os << indent << "Extrusion Point: (" << this->ExtrusionPoint[0] << ", " << this->ExtrusionPoint[1] << ", " << this->ExtrusionPoint[2] << ")\n"; } os << indent << "Capping: " << (this->Capping ? "On\n" : "Off\n"); os << indent << "Scale Factor: " << this->ScaleFactor << "\n"; } VTK_ABI_NAMESPACE_END