// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkOctreeImageToPointSetFilter.h" #include "vtkArrayDispatch.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkDataArrayRange.h" #include "vtkDoubleArray.h" #include "vtkIdTypeArray.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkSMPTools.h" #include "vtkUnsignedCharArray.h" VTK_ABI_NAMESPACE_BEGIN //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkOctreeImageToPointSetFilter); //------------------------------------------------------------------------------ vtkOctreeImageToPointSetFilter::vtkOctreeImageToPointSetFilter() = default; //------------------------------------------------------------------------------ vtkOctreeImageToPointSetFilter::~vtkOctreeImageToPointSetFilter() = default; //------------------------------------------------------------------------------ void vtkOctreeImageToPointSetFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "CreateVerticesCellArray: " << this->CreateVerticesCellArray << endl; os << indent << "ProcessInputCellArray: " << this->ProcessInputCellArray << endl; os << indent << "CellArrayComponent: " << this->CellArrayComponent << endl; } //------------------------------------------------------------------------------ int vtkOctreeImageToPointSetFilter::FillInputPortInformation( int vtkNotUsed(port), vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData"); return 1; } namespace { struct CellInformation { vtkIdType CellId; vtkIdType PointOffset; }; // Compute the number of output points per cell and globally struct ComputePointsOffsetsFunctor { vtkUnsignedCharArray* Octree; vtkIdType NumberOfInputCells; vtkSMPThreadLocal TLNumberOfOutputPoints; vtkIdType NumberOfOutputPoints; vtkSmartPointer PointsPerCell; std::vector CellsInfo; ComputePointsOffsetsFunctor(vtkUnsignedCharArray* octree, vtkIdType numberOfInputCells) : Octree(octree) , NumberOfInputCells(numberOfInputCells) , NumberOfOutputPoints(0) { this->PointsPerCell = vtkSmartPointer::New(); this->PointsPerCell->SetNumberOfValues(this->NumberOfInputCells); } void Initialize() { this->TLNumberOfOutputPoints.Local() = 0; } void operator()(vtkIdType begin, vtkIdType end) { vtkIdType& numberOfOutputPoints = this->TLNumberOfOutputPoints.Local(); unsigned char* octree = this->Octree->GetPointer(0); unsigned char* pointsPerCell = this->PointsPerCell->GetPointer(0); unsigned char numberOfCellPoints; for (vtkIdType cellId = begin; cellId < end; ++cellId) { unsigned char& octreeValue = octree[cellId]; // get the number of points of the cell numberOfCellPoints = static_cast(octreeValue & 1) + static_cast(octreeValue & 2) + static_cast(octreeValue & 4) + static_cast(octreeValue & 8) + static_cast(octreeValue & 16) + static_cast(octreeValue & 32) + static_cast(octreeValue & 64) + static_cast(octreeValue & 128); pointsPerCell[cellId] = numberOfCellPoints; numberOfOutputPoints += numberOfCellPoints; } } void Reduce() { // compute the number of output points this->NumberOfOutputPoints = 0; for (auto& numberOfOutputPoints : this->TLNumberOfOutputPoints) { this->NumberOfOutputPoints += numberOfOutputPoints; } // compute the offset of each cell this->CellsInfo.reserve(static_cast(this->NumberOfOutputPoints)); vtkIdType pointOffset = 0; unsigned char* pointsPerCell = this->PointsPerCell->GetPointer(0); for (vtkIdType cellId = 0; cellId < this->NumberOfInputCells; ++cellId) { if (pointsPerCell[cellId] > 0) { this->CellsInfo.push_back({ cellId, pointOffset }); pointOffset += pointsPerCell[cellId]; } } } }; // Compute the output points template struct ComputeOutputPointsFunctor { vtkImageData* Input; vtkUnsignedCharArray* Octree; vtkUnsignedCharArray* PointsPerCell; vtkDoubleArray* OutputPoints; const std::vector& CellsInfo; TInputField* InputField; TOutputField* OutputField; int CellArrayComponent; bool UseFieldArray; ComputeOutputPointsFunctor(vtkImageData* input, vtkUnsignedCharArray* octree, vtkUnsignedCharArray* pointPerCell, vtkDoubleArray* outputPoints, const std::vector& cellsInfo, TInputField* inputField, TOutputField* outputField, int cellArrayComponent) : Input(input) , Octree(octree) , PointsPerCell(pointPerCell) , OutputPoints(outputPoints) , CellsInfo(cellsInfo) , InputField(inputField) , OutputField(outputField) , CellArrayComponent(cellArrayComponent) , UseFieldArray(inputField != nullptr) { } void operator()(vtkIdType begin, vtkIdType end) { unsigned char* octree = this->Octree->GetPointer(0); unsigned char* pointsPerCell = this->PointsPerCell->GetPointer(0); double* outputPoints = this->OutputPoints->GetPointer(0); const int* extent = this->Input->GetExtent(); const int xExtent = extent[1] - extent[0]; const int yExtent = extent[3] - extent[2]; const double* spacing = this->Input->GetSpacing(); const double spacing_4[3] = { spacing[0] / 4.0, spacing[1] / 4.0, spacing[2] / 4.0 }; vtk::detail::TupleRange inField; vtk::detail::ValueRange outField; if (this->UseFieldArray) { inField = vtk::DataArrayTupleRange(this->InputField); outField = vtk::DataArrayValueRange<1>(this->OutputField); } unsigned char j; double ijk[3], centerPoint[3]; for (vtkIdType i = begin; i < end; ++i) { const CellInformation& cellInfo = this->CellsInfo[i]; const vtkIdType& cellId = cellInfo.CellId; const vtkIdType& pointOffset = cellInfo.PointOffset; const unsigned char& octreeValue = octree[cellId]; double* point = &outputPoints[3 * pointOffset]; ijk[0] = (cellId % xExtent) + 0.5; ijk[1] = ((cellId / xExtent) % yExtent) + 0.5; ijk[2] = (cellId / (xExtent * yExtent)) + 0.5; this->Input->TransformContinuousIndexToPhysicalPoint(ijk, centerPoint); if (octreeValue & 1) { point[0] = centerPoint[0] - spacing_4[0]; point[1] = centerPoint[1] - spacing_4[1]; point[2] = centerPoint[2] - spacing_4[2]; point += 3; } if (octreeValue & 2) { point[0] = centerPoint[0] + spacing_4[0]; point[1] = centerPoint[1] - spacing_4[1]; point[2] = centerPoint[2] - spacing_4[2]; point += 3; } if (octreeValue & 4) { point[0] = centerPoint[0] - spacing_4[0]; point[1] = centerPoint[1] + spacing_4[1]; point[2] = centerPoint[2] - spacing_4[2]; point += 3; } if (octreeValue & 8) { point[0] = centerPoint[0] + spacing_4[0]; point[1] = centerPoint[1] + spacing_4[1]; point[2] = centerPoint[2] - spacing_4[2]; point += 3; } if (octreeValue & 16) { point[0] = centerPoint[0] - spacing_4[0]; point[1] = centerPoint[1] - spacing_4[1]; point[2] = centerPoint[2] + spacing_4[2]; point += 3; } if (octreeValue & 32) { point[0] = centerPoint[0] + spacing_4[0]; point[1] = centerPoint[1] - spacing_4[1]; point[2] = centerPoint[2] + spacing_4[2]; point += 3; } if (octreeValue & 64) { point[0] = centerPoint[0] - spacing_4[0]; point[1] = centerPoint[1] + spacing_4[1]; point[2] = centerPoint[2] + spacing_4[2]; point += 3; } if (octreeValue & 128) { point[0] = centerPoint[0] + spacing_4[0]; point[1] = centerPoint[1] + spacing_4[1]; point[2] = centerPoint[2] + spacing_4[2]; point += 3; } if (this->UseFieldArray) { const auto& numberOfPoints = pointsPerCell[cellId]; const auto& inTuple = inField[cellId]; // fill the output field array with the input field array value for (j = 0; j < numberOfPoints; ++j) { outField[pointOffset + j] = inTuple[this->CellArrayComponent]; } } } } }; struct ComputeOutputPointsWorker { template void operator()(TInputField* inputField, TOutputField* outputField, vtkImageData* input, vtkUnsignedCharArray* octree, vtkUnsignedCharArray* pointsPerCell, vtkDoubleArray* outputPoints, const std::vector& cellsInfo, int cellArrayComponent) { ComputeOutputPointsFunctor functor(input, octree, pointsPerCell, outputPoints, cellsInfo, inputField, outputField, cellArrayComponent); vtkSMPTools::For(0, static_cast(cellsInfo.size()), functor); } }; } //------------------------------------------------------------------------------ int vtkOctreeImageToPointSetFilter::RequestData( vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the input vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkImageData* input = vtkImageData::GetData(inInfo); // get the output vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkPolyData* output = vtkPolyData::GetData(outInfo); if (!input || input->GetNumberOfCells() == 0) { vtkErrorMacro("No input or empty input."); return 0; } // get the octree array auto octree = vtkUnsignedCharArray::SafeDownCast(input->GetCellData()->GetArray("octree")); if (!octree) { vtkErrorMacro("No octree array in input."); return 0; } // compute the number of output points per cell and globally and their offsets ComputePointsOffsetsFunctor computePointsOffsets(octree, input->GetNumberOfCells()); vtkSMPTools::For(0, input->GetNumberOfCells(), computePointsOffsets); vtkIdType numberOfOutputPoints = computePointsOffsets.NumberOfOutputPoints; std::vector cellsInfo = std::move(computePointsOffsets.CellsInfo); vtkSmartPointer pointsPerCell = computePointsOffsets.PointsPerCell; // create the output points vtkNew pointsArray; pointsArray->SetNumberOfComponents(3); pointsArray->SetNumberOfTuples(numberOfOutputPoints); // create the pointData data array vtkSmartPointer outField = nullptr; vtkDataArray* inField = nullptr; if (this->ProcessInputCellArray) { inField = this->GetInputArrayToProcess(0, inputVector); if (!inField) { vtkErrorMacro("Array to process is null."); return 0; } if (inField->GetNumberOfTuples() != input->GetNumberOfCells()) { vtkErrorMacro("Array to process must have as many tuples as the number of cells."); return 0; } if (inField->GetNumberOfComponents() <= this->CellArrayComponent) { vtkErrorMacro("Invalid component."); return 0; } outField = vtkSmartPointer::Take(inField->NewInstance()); outField->SetName(inField->GetName()); outField->SetNumberOfComponents(1); outField->SetNumberOfTuples(numberOfOutputPoints); } using Dispatcher = vtkArrayDispatch::Dispatch2SameValueType; ComputeOutputPointsWorker computeOutputPointsWorker; if (!Dispatcher::Execute(inField, outField.Get(), computeOutputPointsWorker, input, octree, pointsPerCell.Get(), pointsArray, cellsInfo, this->CellArrayComponent)) { computeOutputPointsWorker(inField, outField.Get(), input, octree, pointsPerCell.Get(), pointsArray, cellsInfo, this->CellArrayComponent); } // set the output points vtkNew points; points->SetData(pointsArray); output->SetPoints(points); // set the output field array if (this->ProcessInputCellArray) { output->GetPointData()->AddArray(outField); } // create output vertices cell array if (this->CreateVerticesCellArray) { vtkNew connectivity; connectivity->SetNumberOfValues(numberOfOutputPoints); vtkSMPTools::For(0, numberOfOutputPoints, [&](vtkIdType begin, vtkIdType end) { auto connectivityPtr = connectivity->GetPointer(0); for (vtkIdType i = begin; i < end; ++i) { connectivityPtr[i] = i; } }); vtkNew cells; cells->SetData(1, connectivity); output->SetVerts(cells); } return 1; } VTK_ABI_NAMESPACE_END