diff --git a/Modules/Filtering/MeshToPolyData/CMakeLists.txt b/Modules/Filtering/MeshToPolyData/CMakeLists.txt new file mode 100644 index 00000000000..1829c73a6c2 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16.3) +project(MeshToPolyData) + +if(NOT ITK_SOURCE_DIR) + find_package(ITK REQUIRED) + list(APPEND CMAKE_MODULE_PATH ${ITK_CMAKE_DIR}) + include(ITKModuleExternal) +else() + set(ITK_DIR ${CMAKE_BINARY_DIR}) + itk_module_impl() +endif() diff --git a/Modules/Filtering/MeshToPolyData/include/itkImageToPointSetFilter.h b/Modules/Filtering/MeshToPolyData/include/itkImageToPointSetFilter.h new file mode 100644 index 00000000000..3f0231e039e --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkImageToPointSetFilter.h @@ -0,0 +1,93 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkImageToPointSetFilter_h +#define itkImageToPointSetFilter_h + +#include "itkImageToMeshFilter.h" + +namespace itk +{ +/** \class ImageToPointSetFilter + * + * \brief Convert an Image to a PointSet. + * + * This class provides default where every pixel in an Image is converted to a + * point in the output PointSet. + * + * This class inherits from ImageToMeshFilter, but no topological information is + * added to the mesh. + * + * \ingroup MeshToPolyData + */ +template +class ITK_TEMPLATE_EXPORT ImageToPointSetFilter : public ImageToMeshFilter +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(ImageToPointSetFilter); + + /** Standard class type alias. */ + using Self = ImageToPointSetFilter; + using Superclass = ImageToMeshFilter; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkOverrideGetNameOfClassMacro(ImageToPointSetFilter); + + /** Some convenient type alias. */ + using InputImageType = TInputImage; + using InputImageConstPointer = typename InputImageType::ConstPointer; + using InputImageRegionType = typename InputImageType::RegionType; + using InputImagePixelType = typename InputImageType::PixelType; + + /** Some type alias associated with the output mesh. */ + using OutputMeshType = TOutputMesh; + using PointType = typename OutputMeshType::PointType; + using OutputMeshPointer = typename OutputMeshType::Pointer; + using PointsContainer = typename OutputMeshType::PointsContainer; + using PointIdentifier = typename OutputMeshType::PointIdentifier; + using PointsContainerPointer = typename PointsContainer::Pointer; + using PointsContainerIterator = typename PointsContainer::Iterator; + using PointDataContainer = typename OutputMeshType::PointDataContainer; + using PointDataContainerPointer = typename PointDataContainer::Pointer; + using PointDataContainerIterator = typename PointDataContainer::Iterator; + + /** The dimension of the output mesh. */ + static constexpr unsigned int PointDimension = TOutputMesh::PointDimension; + + /** ImageDimension constant */ + static constexpr unsigned int ImageDimension = TInputImage::ImageDimension; + +protected: + ImageToPointSetFilter() = default; + ~ImageToPointSetFilter() override = default; + + void + GenerateData() override; +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkImageToPointSetFilter.hxx" +#endif + +#endif diff --git a/Modules/Filtering/MeshToPolyData/include/itkImageToPointSetFilter.hxx b/Modules/Filtering/MeshToPolyData/include/itkImageToPointSetFilter.hxx new file mode 100644 index 00000000000..4582706d2cd --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkImageToPointSetFilter.hxx @@ -0,0 +1,76 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkImageToPointSetFilter_hxx +#define itkImageToPointSetFilter_hxx + +#include "itkImageToPointSetFilter.h" + +#include "itkImageRegionConstIteratorWithIndex.h" +#include "itkProgressReporter.h" + +namespace itk +{ + +template +void +ImageToPointSetFilter::GenerateData() +{ + // " from itkBinaryMask3DMeshSource: + // This indicates that the current BufferedRegion is equal to the + // requested region. This action prevents useless rexecutions of + // the pipeline. + this->GetOutput()->SetBufferedRegion(this->GetOutput()->GetRequestedRegion()); + + OutputMeshPointer mesh = this->GetOutput(); + PointsContainerPointer points = mesh->GetPoints(); + InputImageConstPointer image = this->GetInput(0); + + PointDataContainerPointer pointData; + if (mesh->GetPointData()) + { + pointData = mesh->GetPointData(); + } + else + { // Create + pointData = PointDataContainer::New(); + } + + const SizeValueType numberOfPixels = image->GetRequestedRegion().GetNumberOfPixels(); + ProgressReporter progress(this, 0, numberOfPixels); + points->Reserve(numberOfPixels); + pointData->Reserve(numberOfPixels); + mesh->SetPointData(pointData.GetPointer()); + + typename itk::ImageRegionConstIteratorWithIndex imageIt(image, image->GetRequestedRegion()); + imageIt.GoToBegin(); + PointsContainerIterator pointsIt = points->Begin(); + PointDataContainerIterator pointDataIt = pointData->Begin(); + while (!imageIt.IsAtEnd()) + { + image->TransformIndexToPhysicalPoint(imageIt.GetIndex(), pointsIt.Value()); + (pointDataIt.Value()) = imageIt.Get(); + ++pointsIt; + ++pointDataIt; + ++imageIt; + progress.CompletedPixel(); + } +} + +} // end namespace itk + +#endif diff --git a/Modules/Filtering/MeshToPolyData/include/itkMeshToPolyDataFilter.h b/Modules/Filtering/MeshToPolyData/include/itkMeshToPolyDataFilter.h new file mode 100644 index 00000000000..4e7dfb17e45 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkMeshToPolyDataFilter.h @@ -0,0 +1,132 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkMeshToPolyDataFilter_h +#define itkMeshToPolyDataFilter_h + +#include "itkProcessObject.h" +#include "itkPolyData.h" + +#include + +namespace itk +{ + +template +struct HasCellTraits : std::false_type +{}; + +template +struct HasCellTraits> : std::true_type +{}; + +/** \class MeshToPolyDataFilter + * + * \brief Convert a PointSet or Mesh to PolyData + * + * Convert an itk::PointSet or itk::Mesh to an itk::PolyData for visualization + * with vtk.js. + * + * Currently support ITK cell types: + * + * - itk::VertexCell + * - itk::LineCell + * - itk::TriangleCell + * - itk::QuadrilateralCell + * - itk::PolygonCell + * + * \ingroup MeshToPolyData + * + */ +template +class MeshToPolyDataFilter : public ProcessObject +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(MeshToPolyDataFilter); + + static constexpr unsigned int PointDimension = TInputMesh::PointDimension; + + using InputMeshType = TInputMesh; + + /** Standard class typedefs. */ + using Self = MeshToPolyDataFilter; + using Superclass = ProcessObject; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; + + using PolyDataType = PolyData; + + /** Run-time type information. */ + itkOverrideGetNameOfClassMacro(MeshToPolyDataFilter); + + /** Standard New macro. */ + itkNewMacro(Self); + + /** Set the mesh input of this process object. */ + using Superclass::SetInput; + void + SetInput(const InputMeshType * input); + + /** Get the mesh input of this process object. */ + const InputMeshType * + GetInput() const; + + const InputMeshType * + GetInput(unsigned int idx) const; + + PolyDataType * + GetOutput(); + const PolyDataType * + GetOutput() const; + + PolyDataType * + GetOutput(unsigned int idx); + +protected: + MeshToPolyDataFilter(); + ~MeshToPolyDataFilter() override = default; + + void + PrintSelf(std::ostream & os, Indent indent) const override; + + void + GenerateData() override; + + template ::value, int>::type = 0> + void + GenerateDataDispatch(); + + template ::value, int>::type = 0> + void + GenerateDataDispatch(); + + ProcessObject::DataObjectPointer + MakeOutput(ProcessObject::DataObjectPointerArraySizeType idx) override; + ProcessObject::DataObjectPointer + MakeOutput(const ProcessObject::DataObjectIdentifierType &) override; + +private: +}; +} // namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkMeshToPolyDataFilter.hxx" +#endif + +#endif // itkMeshToPolyDataFilter diff --git a/Modules/Filtering/MeshToPolyData/include/itkMeshToPolyDataFilter.hxx b/Modules/Filtering/MeshToPolyData/include/itkMeshToPolyDataFilter.hxx new file mode 100644 index 00000000000..96d6796e247 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkMeshToPolyDataFilter.hxx @@ -0,0 +1,677 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkMeshToPolyDataFilter_hxx +#define itkMeshToPolyDataFilter_hxx + +#include "itkMeshToPolyDataFilter.h" + +#include "itkVertexCell.h" +#include "itkLineCell.h" +#include "itkPolyLineCell.h" +#include "itkMesh.h" +#include "itkTriangleCell.h" +#include "itkQuadrilateralCell.h" +#include "itkPolygonCell.h" +#include "itkTetrahedronCell.h" +#include "itkHexahedronCell.h" + +namespace +{ + +template +class VisitCellsClass +{ +public: + using MeshType = TMesh; + using PolyDataType = TPolyData; + // typedef the itk cells we are interested in + using CellInterfaceType = itk::CellInterface; + + using CellsContainerType = typename TPolyData::CellsContainer; + + using VertexCellType = itk::VertexCell; + using LineCellType = itk::LineCell; + using PolyLineCellType = itk::PolyLineCell; + using TriangleCellType = itk::TriangleCell; + using QuadrilateralCellType = itk::QuadrilateralCell; + using PolygonCellType = itk::PolygonCell; + using TetrahedronCellType = itk::TetrahedronCell; + using HexahedronCellType = itk::HexahedronCell; + + // Set the output polydata vertices container + void + SetVertices(CellsContainerType * vertices) + { + m_Vertices = vertices; + } + + // Set the output polydata polygons container + void + SetLines(CellsContainerType * lines) + { + m_Lines = lines; + } + + // Set the output polydata polygons container + void + SetPolygons(CellsContainerType * polygons) + { + m_Polygons = polygons; + } + + // Set the output polydata triangleStrips container + void + SetTriangleStrips(CellsContainerType * triangleStrips) + { + m_TriangleStrips = triangleStrips; + } + + // Set the associated input cell id container + void + SetVerticesCellIds(CellsContainerType * cellIds) + { + m_VerticesCellIds = cellIds; + } + + // Set the associated input cell id container + void + SetLinesCellIds(CellsContainerType * cellIds) + { + m_LinesCellIds = cellIds; + } + + // Set the associated input cell id container + void + SetPolygonsCellIds(CellsContainerType * cellIds) + { + m_PolygonsCellIds = cellIds; + } + + // Set the associated input cell id container + void + SetTriangleStripsCellIds(CellsContainerType * cellIds) + { + m_TriangleStripsCellIds = cellIds; + } + + // Visit a vertex and create a vertex in the output + void + Visit(unsigned long cellId, VertexCellType * cell) + { + constexpr unsigned int numberOfPoints = VertexCellType::NumberOfPoints; + m_Vertices->push_back(numberOfPoints); + m_Vertices->push_back(cell->GetPointId()); + m_VerticesCellIds->push_back(static_cast(cellId)); + } + + // Visit a line and create a line in the output + void + Visit(unsigned long cellId, LineCellType * cell) + { + constexpr unsigned int numberOfPoints = LineCellType::NumberOfPoints; + m_Lines->push_back(numberOfPoints); + const typename LineCellType::PointIdConstIterator pointIdEnd = cell->PointIdsEnd(); + for (typename LineCellType::PointIdConstIterator pointIdIt = cell->PointIdsBegin(); pointIdIt != pointIdEnd; + ++pointIdIt) + { + m_Lines->push_back(*pointIdIt); + } + m_LinesCellIds->push_back(static_cast(cellId)); + } + + // Visit a polyline and create a polyline in the output + void + Visit(unsigned long cellId, PolyLineCellType * cell) + { + const unsigned int numberOfPoints = cell->GetNumberOfPoints(); + m_Lines->push_back(numberOfPoints); + const typename PolyLineCellType::PointIdConstIterator pointIdEnd = cell->PointIdsEnd(); + for (typename PolyLineCellType::PointIdConstIterator pointIdIt = cell->PointIdsBegin(); pointIdIt != pointIdEnd; + ++pointIdIt) + { + m_Lines->push_back(*pointIdIt); + } + m_LinesCellIds->push_back(static_cast(cellId)); + } + + // Visit a triangle and create a triangle in the output + void + Visit(unsigned long cellId, TriangleCellType * cell) + { + constexpr unsigned int numberOfPoints = TriangleCellType::NumberOfPoints; + m_Polygons->push_back(numberOfPoints); + const typename TriangleCellType::PointIdConstIterator pointIdEnd = cell->PointIdsEnd(); + for (typename TriangleCellType::PointIdConstIterator pointIdIt = cell->PointIdsBegin(); pointIdIt != pointIdEnd; + ++pointIdIt) + { + m_Polygons->push_back(*pointIdIt); + } + m_PolygonsCellIds->push_back(static_cast(cellId)); + } + + // Visit a quadrilateral and create a quadrilateral in the output + void + Visit(unsigned long cellId, QuadrilateralCellType * cell) + { + constexpr unsigned int numberOfPoints = QuadrilateralCellType::NumberOfPoints; + m_Polygons->push_back(numberOfPoints); + const typename QuadrilateralCellType::PointIdConstIterator pointIdEnd = cell->PointIdsEnd(); + for (typename QuadrilateralCellType::PointIdConstIterator pointIdIt = cell->PointIdsBegin(); + pointIdIt != pointIdEnd; + ++pointIdIt) + { + m_Polygons->push_back(*pointIdIt); + } + m_PolygonsCellIds->push_back(static_cast(cellId)); + } + + // Visit a polygon and create a polygon in the output + void + Visit(unsigned long cellId, PolygonCellType * cell) + { + const unsigned int numberOfPoints = cell->GetNumberOfPoints(); + m_Polygons->push_back(numberOfPoints); + const typename PolygonCellType::PointIdConstIterator pointIdEnd = cell->PointIdsEnd(); + for (typename PolygonCellType::PointIdConstIterator pointIdIt = cell->PointIdsBegin(); pointIdIt != pointIdEnd; + ++pointIdIt) + { + m_Polygons->push_back(*pointIdIt); + } + m_PolygonsCellIds->push_back(static_cast(cellId)); + } + + //// Visit a tetrahedron and create a tetrahedron in the output + // void Visit(unsigned long, TetrahedronCellType* cell) + //{ + // constexpr unsigned int numberOfPoints = TetrahedronCellType::NumberOfPoints; + // constexpr unsigned int numberOfFaces = TetrahedronCellType::NumberOfFaces; + // constexpr unsigned int numberOfEdges = TetrahedronCellType::NumberOfFaces; + // for( unsigned int faceId = 0; faceId < numberOfFaces; ++faceId ) + //{ + // typename TetrahedronCellType::FaceAutoPointer facePointer; + // cell->GetFace( faceId, facePointer ); + //// TODO + ////faceIds = vtkIdList::New(); + ////case VTK_TETRA: + ////for (faceId = 0; faceId < 4; faceId++) + ////{ + ////faceIds->Reset(); + ////faceVerts = vtkTetra::GetFaceArray(faceId); + ////faceIds->InsertNextId(pts[faceVerts[0]]); + ////faceIds->InsertNextId(pts[faceVerts[1]]); + ////faceIds->InsertNextId(pts[faceVerts[2]]); + ////numFacePts = 3; + ////input->GetCellNeighbors(cellId, faceIds, cellIds); + ////if ( cellIds->GetNumberOfIds() <= 0 ) + ////{ + ////polys->InsertNextCell(numFacePts); + ////for ( int i=0; i < numFacePts; i++) + ////{ + ////polys->InsertCellPoint(pts[faceVerts[i]]); + ////} + ////polyCellIds.push_back(cellId); + ////} + ////} + ////break; + //} + //} + + //// Visit a hexahedron and create a hexahedron in the output + // void Visit(unsigned long, HexahedronCellType* cell) + //{ + //// TODO + ////case VTK_HEXAHEDRON: + ////for (faceId = 0; faceId < 6; faceId++) + ////{ + ////faceIds->Reset(); + ////faceVerts = vtkHexahedron::GetFaceArray(faceId); + ////faceIds->InsertNextId(pts[faceVerts[0]]); + ////faceIds->InsertNextId(pts[faceVerts[1]]); + ////faceIds->InsertNextId(pts[faceVerts[2]]); + ////faceIds->InsertNextId(pts[faceVerts[3]]); + ////numFacePts = 4; + ////input->GetCellNeighbors(cellId, faceIds, cellIds); + ////if ( cellIds->GetNumberOfIds() <= 0 ) + ////{ + ////polys->InsertNextCell(numFacePts); + ////for ( int i=0; i < numFacePts; i++) + ////{ + ////polys->InsertCellPoint(pts[faceVerts[i]]); + ////} + ////polyCellIds.push_back(cellId); + ////} + ////} + ////break; + //} + +private: + CellsContainerType * m_Vertices{ nullptr }; + CellsContainerType * m_Lines{ nullptr }; + CellsContainerType * m_Polygons{ nullptr }; + CellsContainerType * m_TriangleStrips{ nullptr }; + + CellsContainerType * m_VerticesCellIds{ nullptr }; + CellsContainerType * m_LinesCellIds{ nullptr }; + CellsContainerType * m_PolygonsCellIds{ nullptr }; + CellsContainerType * m_TriangleStripsCellIds{ nullptr }; +}; + +} // end anonymous namespace + +namespace itk +{ + +template +MeshToPolyDataFilter::MeshToPolyDataFilter() +{ + // Modify superclass default values, can be overridden by subclasses + this->SetNumberOfRequiredInputs(1); + + typename PolyDataType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); + this->ProcessObject::SetNumberOfRequiredOutputs(1); + this->ProcessObject::SetNthOutput(0, output.GetPointer()); +} + + +template +void +MeshToPolyDataFilter::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); +} + + +template +void +MeshToPolyDataFilter::SetInput(const TInputMesh * input) +{ + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput(0, const_cast(input)); +} + + +template +const typename MeshToPolyDataFilter::InputMeshType * +MeshToPolyDataFilter::GetInput() const +{ + return itkDynamicCastInDebugMode(this->GetPrimaryInput()); +} + + +template +const typename MeshToPolyDataFilter::InputMeshType * +MeshToPolyDataFilter::GetInput(unsigned int idx) const +{ + return dynamic_cast(this->ProcessObject::GetInput(idx)); +} + + +template +ProcessObject::DataObjectPointer +MeshToPolyDataFilter::MakeOutput(ProcessObject::DataObjectPointerArraySizeType) +{ + return PolyDataType::New().GetPointer(); +} + + +template +ProcessObject::DataObjectPointer +MeshToPolyDataFilter::MakeOutput(const ProcessObject::DataObjectIdentifierType &) +{ + return PolyDataType::New().GetPointer(); +} + + +template +typename MeshToPolyDataFilter::PolyDataType * +MeshToPolyDataFilter::GetOutput() +{ + // we assume that the first output is of the templated type + return itkDynamicCastInDebugMode(this->GetPrimaryOutput()); +} + + +template +const typename MeshToPolyDataFilter::PolyDataType * +MeshToPolyDataFilter::GetOutput() const +{ + // we assume that the first output is of the templated type + return itkDynamicCastInDebugMode(this->GetPrimaryOutput()); +} + + +template +typename MeshToPolyDataFilter::PolyDataType * +MeshToPolyDataFilter::GetOutput(unsigned int idx) +{ + auto * out = dynamic_cast(this->ProcessObject::GetOutput(idx)); + + if (out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr) + { + itkWarningMacro(<< "Unable to convert output number " << idx << " to type " << typeid(PolyDataType).name()); + } + return out; +} + + +template +void +MeshToPolyDataFilter::GenerateData() +{ + const InputMeshType * inputMesh = this->GetInput(); + PolyDataType * outputPolyData = this->GetOutput(); + + using MeshPointsContainerType = typename InputMeshType::PointsContainer; + using PolyDataPointsContainerType = typename PolyDataType::PointsContainer; + const MeshPointsContainerType * inputPoints = inputMesh->GetPoints(); + typename PolyDataPointsContainerType::Pointer outputPoints = PolyDataPointsContainerType::New(); + outputPoints->resize(inputPoints->Size()); + // 2D Mesh -> 3D PolyData, third dimension point locations defaults to 0.0. + typename PolyDataType::PointType nullPoint{ 0.0f }; + outputPoints->assign(inputPoints->Size(), nullPoint); + + typename MeshPointsContainerType::ConstIterator inputPointItr = inputPoints->Begin(); + typename MeshPointsContainerType::ConstIterator inputPointEnd = inputPoints->End(); + + typename PolyDataPointsContainerType::Iterator outputPointItr = outputPoints->Begin(); + while (inputPointItr != inputPointEnd) + { + for (unsigned int ii = 0; ii < InputMeshType::PointDimension; ++ii) + { + outputPointItr.Value()[ii] = inputPointItr.Value()[ii]; + } + ++inputPointItr; + ++outputPointItr; + } + outputPolyData->SetPoints(outputPoints); + + using PointDataContainerType = typename PolyDataType::PointDataContainer; + const PointDataContainerType * inputPointData = inputMesh->GetPointData(); + if (inputPointData) + { + typename PointDataContainerType::Pointer outputPointData = PointDataContainerType::New(); + outputPointData->Reserve(inputPointData->Size()); + + typename PointDataContainerType::ConstIterator inputPointDataItr = inputPointData->Begin(); + typename PointDataContainerType::ConstIterator inputPointDataEnd = inputPointData->End(); + + typename PointDataContainerType::Iterator outputPointDataItr = outputPointData->Begin(); + + while (inputPointDataItr != inputPointDataEnd) + { + outputPointDataItr.Value() = inputPointDataItr.Value(); + ++inputPointDataItr; + ++outputPointDataItr; + } + outputPolyData->SetPointData(outputPointData); + } + + GenerateDataDispatch(); +} + + +template +template ::value, int>::type> +void +MeshToPolyDataFilter::GenerateDataDispatch() +{ + // Nothing else to do +} + + +template +template ::value, int>::type> +void +MeshToPolyDataFilter::GenerateDataDispatch() +{ + // Also propagate cells and cell data + + const InputMeshType * inputMesh = this->GetInput(); + PolyDataType * outputPolyData = this->GetOutput(); + + const IdentifierType numberOfCells = inputMesh->GetNumberOfCells(); + + using CellsContainerType = typename PolyDataType::CellsContainer; + typename CellsContainerType::Pointer vertices = CellsContainerType::New(); + vertices->reserve(numberOfCells / 4 + 1); + typename CellsContainerType::Pointer lines = CellsContainerType::New(); + lines->reserve(numberOfCells / 4 + 1); + typename CellsContainerType::Pointer polylines = CellsContainerType::New(); + polylines->reserve(numberOfCells / 4 + 1); + typename CellsContainerType::Pointer polygons = CellsContainerType::New(); + polygons->reserve(numberOfCells / 4 + 1); + // typename CellsContainerType::Pointer triangleStrips = CellsContainerType::New(); + // triangleStrips->reserve( numberOfCells / 4 + 1 ); + + // These store the cell ids of the input that map to the + // new vert/line/poly/strip cells, for copying cell data + // in appropriate order. + typename CellsContainerType::Pointer verticesCellIds = CellsContainerType::New(); + verticesCellIds->reserve(numberOfCells / 4 + 1); + typename CellsContainerType::Pointer linesCellIds = CellsContainerType::New(); + linesCellIds->reserve(numberOfCells / 4 + 1); + typename CellsContainerType::Pointer polygonsCellIds = CellsContainerType::New(); + polygonsCellIds->reserve(numberOfCells / 4 + 1); + // typename CellsContainerType::Pointer triangleStripsCellIds = CellsContainerType::New(); + // triangleStripsCellIds->Reserve( numberOfCells / 4 + 1 ); + + using CellTraits = typename InputMeshType::CellTraits; + using PixelType = typename InputMeshType::PixelType; + + // Setup the vertex visitor + typedef CellInterfaceVisitorImplementation>, + VisitCellsClass> + VertexVisitor; + typename VertexVisitor::Pointer vertexVisitor = VertexVisitor::New(); + vertexVisitor->SetVertices(vertices); + vertexVisitor->SetLines(lines); + vertexVisitor->SetPolygons(polygons); + // vertexVisitor->SetTriangleStrips( triangleStrips ); + vertexVisitor->SetVerticesCellIds(verticesCellIds); + vertexVisitor->SetLinesCellIds(linesCellIds); + vertexVisitor->SetPolygonsCellIds(polygonsCellIds); + // vertexVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + // Setup the poly line visitor + typedef CellInterfaceVisitorImplementation>, + VisitCellsClass> + PolyLineVisitor; + typename PolyLineVisitor::Pointer polyLineVisitor = PolyLineVisitor::New(); + polyLineVisitor->SetVertices(vertices); + polyLineVisitor->SetLines(polylines); + polyLineVisitor->SetPolygons(polygons); + // lineVisitor->SetTriangleStrips( triangleStrips ); + polyLineVisitor->SetVerticesCellIds(verticesCellIds); + polyLineVisitor->SetLinesCellIds(linesCellIds); + polyLineVisitor->SetPolygonsCellIds(polygonsCellIds); + // lineVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + // Setup the line visitor + typedef CellInterfaceVisitorImplementation>, + VisitCellsClass> + LineVisitor; + typename LineVisitor::Pointer lineVisitor = LineVisitor::New(); + lineVisitor->SetVertices(vertices); + lineVisitor->SetLines(lines); + lineVisitor->SetPolygons(polygons); + // lineVisitor->SetTriangleStrips( triangleStrips ); + lineVisitor->SetVerticesCellIds(verticesCellIds); + lineVisitor->SetLinesCellIds(linesCellIds); + lineVisitor->SetPolygonsCellIds(polygonsCellIds); + // lineVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + // Setup the triangle visitor + typedef CellInterfaceVisitorImplementation>, + VisitCellsClass> + TriangleVisitor; + typename TriangleVisitor::Pointer triangleVisitor = TriangleVisitor::New(); + triangleVisitor->SetVertices(vertices); + triangleVisitor->SetLines(lines); + triangleVisitor->SetPolygons(polygons); + // triangleVisitor->SetTriangleStrips( triangleStrips ); + triangleVisitor->SetVerticesCellIds(verticesCellIds); + triangleVisitor->SetLinesCellIds(linesCellIds); + triangleVisitor->SetPolygonsCellIds(polygonsCellIds); + // triangleVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + // Setup the quadrilateral visitor + typedef CellInterfaceVisitorImplementation>, + VisitCellsClass> + QuadrilateralVisitor; + typename QuadrilateralVisitor::Pointer quadrilateralVisitor = QuadrilateralVisitor::New(); + quadrilateralVisitor->SetVertices(vertices); + quadrilateralVisitor->SetLines(lines); + quadrilateralVisitor->SetPolygons(polygons); + // quadrilateralVisitor->SetTriangleStrips( triangleStrips ); + quadrilateralVisitor->SetVerticesCellIds(verticesCellIds); + quadrilateralVisitor->SetLinesCellIds(linesCellIds); + quadrilateralVisitor->SetPolygonsCellIds(polygonsCellIds); + // quadrilateralVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + // Setup the polygon visitor + typedef CellInterfaceVisitorImplementation>, + VisitCellsClass> + PolygonVisitor; + typename PolygonVisitor::Pointer polygonVisitor = PolygonVisitor::New(); + polygonVisitor->SetVertices(vertices); + polygonVisitor->SetLines(lines); + polygonVisitor->SetPolygons(polygons); + // polygonVisitor->SetTriangleStrips( triangleStrips ); + polygonVisitor->SetVerticesCellIds(verticesCellIds); + polygonVisitor->SetLinesCellIds(linesCellIds); + polygonVisitor->SetPolygonsCellIds(polygonsCellIds); + // polygonVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + // TODO + // Setup the tetrahedron visitor + // typedef CellInterfaceVisitorImplementation< + // PixelType, CellTraits, + // TetrahedronCell< CellInterface< PixelType, CellTraits > >, + // VisitCellsClass< InputMeshType, PolyDataType > > TetrahedronVisitor; + // typename TetrahedronVisitor::Pointer tetrahedronVisitor = TetrahedronVisitor::New(); + // tetrahedronVisitor->SetVertices( vertices ); + // tetrahedronVisitor->SetLines( lines ); + // tetrahedronVisitor->SetPolygons( polygons ); + // tetrahedronVisitor->SetTriangleStrips( triangleStrips ); + // tetrahedronVisitor->SetVerticesCellIds( verticesCellIds ); + // tetrahedronVisitor->SetLinesCellIds( linesCellIds ); + // tetrahedronVisitor->SettetrahedronsCellIds( tetrahedronsCellIds ); + // tetrahedronVisitor->SetTriangleStripsCellIds( triangleStripsCellIds ); + + + typename InputMeshType::CellType::MultiVisitor::Pointer multiVisitor = InputMeshType::CellType::MultiVisitor::New(); + multiVisitor->AddVisitor(vertexVisitor.GetPointer()); + multiVisitor->AddVisitor(lineVisitor.GetPointer()); + multiVisitor->AddVisitor(polyLineVisitor.GetPointer()); + multiVisitor->AddVisitor(triangleVisitor.GetPointer()); + multiVisitor->AddVisitor(quadrilateralVisitor.GetPointer()); + multiVisitor->AddVisitor(polygonVisitor.GetPointer()); + // multiVisitor->AddVisitor(tetrahedronVisitor.GetPointer()); + + // Now ask the mesh to accept the multivisitor which + // will Call Visit for each cell in the mesh that matches the + // cell types of the visitors added to the MultiVisitor + if (numberOfCells) + { + inputMesh->Accept(multiVisitor); + } + + vertices->shrink_to_fit(); + outputPolyData->SetVertices(vertices); + + // Append lines to polylines before calling SetLines + lines->shrink_to_fit(); + polylines->shrink_to_fit(); + + auto iterator_start_lines = lines->begin(); + auto iterator_end_lines = lines->end(); + auto iterator_end_polylines = polylines->end(); + polylines->insert(iterator_end_polylines, iterator_start_lines, iterator_end_lines); + + outputPolyData->SetLines(polylines); + polygons->shrink_to_fit(); + outputPolyData->SetPolygons(polygons); + // triangleStrips->shrink_to_fit(); + // outputPolyData->SetTriangleStrips( triangleStrips ); + + using CellDataContainerType = typename PolyDataType::CellDataContainer; + const CellDataContainerType * inputCellData = inputMesh->GetCellData(); + if (inputCellData && inputCellData->Size()) + { + typename CellDataContainerType::Pointer outputCellData = CellDataContainerType::New(); + outputCellData->Reserve(inputCellData->Size()); + SizeValueType offset = 0; + SizeValueType size = verticesCellIds->Size(); + + // Copy the cell data in appropriate order : verts / lines / polys / strips + for (SizeValueType ii = 0; ii < verticesCellIds->Size(); ++ii) + { + typename CellDataContainerType::Element value; + if (inputCellData->GetElementIfIndexExists(verticesCellIds->ElementAt(ii), &value)) + { + outputCellData->InsertElement(ii, value); + } + } + offset += size; + size = linesCellIds->Size(); + for (SizeValueType ii = 0; ii < size; ++ii) + { + typename CellDataContainerType::Element value; + if (inputCellData->GetElementIfIndexExists(linesCellIds->ElementAt(ii), &value)) + { + outputCellData->InsertElement(offset + ii, value); + } + } + offset += size; + size = polygonsCellIds->Size(); + for (SizeValueType ii = 0; ii < size; ++ii) + { + typename CellDataContainerType::Element value; + if (inputCellData->GetElementIfIndexExists(polygonsCellIds->ElementAt(ii), &value)) + { + outputCellData->InsertElement(offset + ii, value); + } + } + offset += size; + // size = triangleStripsCellIds->Size(); + // for( SizeValueType ii = 0; ii < size; ++ii ) + //{ + // outputCellData->InsertElement( offset + ii, + // inputCellData->ElementAt( triangleStripsCellIds->ElementAt( ii ) ) ); + //} + + outputPolyData->SetCellData(outputCellData); + } +} + + +} // end namespace itk + +#endif // itkMeshToPolyDataFilter_hxx diff --git a/Modules/Filtering/MeshToPolyData/include/itkPolyData.h b/Modules/Filtering/MeshToPolyData/include/itkPolyData.h new file mode 100644 index 00000000000..708f69af334 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkPolyData.h @@ -0,0 +1,189 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkPolyData_h +#define itkPolyData_h + +#include "itkDataObject.h" +#include "itkObjectFactory.h" +#include "itkDefaultStaticMeshTraits.h" + +namespace itk +{ + +/** \class PolyData + * + * \brief Geometry class compatible with vtk.js PolyData + * + * \ingroup MeshToPolyData + */ +template +class ITK_TEMPLATE_EXPORT PolyData : public DataObject +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(PolyData); + + using Self = PolyData; + using Superclass = DataObject; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; + + static constexpr unsigned int PointDimension = 3; + + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkOverrideGetNameOfClassMacro(PolyData); + + /** Type of PointData or CellData */ + using PixelType = TPixel; + using CellPixelType = TCellPixel; + using MeshTraits = DefaultStaticMeshTraits; + + /** Convenient type alias obtained from TMeshTraits template parameter. */ + using CoordinateType = typename MeshTraits::CoordinateType; + using CoordRepType = CoordinateType; + using PointIdentifier = typename MeshTraits::PointIdentifier; + using PointType = typename MeshTraits::PointType; + using PointsContainer = typename MeshTraits::PointsContainer; + using PointDataContainer = typename MeshTraits::PointDataContainer; + using CellIdentifier = typename MeshTraits::CellIdentifier; + using CellDataContainer = typename MeshTraits::CellDataContainer; + using CellsContainer = VectorContainer; + + void + Initialize() override; + + PointIdentifier + GetNumberOfPoints() const; + + /** Define Set/Get access routines for each internal container. + * Methods also exist to add points, cells, etc. one at a time + * rather than through an entire container. */ + void + SetPoints(PointsContainer *); + PointsContainer * + GetPoints(); + const PointsContainer * + GetPoints() const; + + /** Vertices in format [1 pointIndex1 1 pointIndex2 1 pointIndex3 ... ] */ + void + SetVertices(CellsContainer *); + CellsContainer * + GetVertices(); + const CellsContainer * + GetVertices() const; + + /** Lines in format [nPointsLine1 pointIndex1 pointIndex2 nPointsLine2 pointIndex1 pointIndex2 ... ] */ + void + SetLines(CellsContainer *); + CellsContainer * + GetLines(); + const CellsContainer * + GetLines() const; + + /** Polygons in format [nPointsPolygon1 pointIndex1 pointIndex2 nPointsPolygon2 pointIndex1 pointIndex2 ... ] */ + void + SetPolygons(CellsContainer *); + CellsContainer * + GetPolygons(); + const CellsContainer * + GetPolygons() const; + + /** TriangleStrips in format [nPointsTriangleStrip1 pointIndex1 pointIndex2 nPointsTriangleStrip2 pointIndex1 + * pointIndex2 ... ] */ + void + SetTriangleStrips(CellsContainer *); + CellsContainer * + GetTriangleStrips(); + const CellsContainer * + GetTriangleStrips() const; + + void + SetPointData(PointDataContainer *); + PointDataContainer * + GetPointData(); + const PointDataContainer * + GetPointData() const; + + /** Access routines to fill the Points container, and get information + * from it. */ + void SetPoint(PointIdentifier, PointType); + bool + GetPoint(PointIdentifier, PointType *) const; + PointType GetPoint(PointIdentifier) const; + + /** Access routines to fill the PointData container, and get information + * from it. */ + void SetPointData(PointIdentifier, PixelType); + bool + GetPointData(PointIdentifier, PixelType *) const; + + /** Access m_CellDataContainer, which contains data associated with + * the mesh's cells. Optionally, this can be nullptr, indicating that + * no data are associated with the cells. The data for a cell can + * be accessed through its cell identifier. */ + void + SetCellData(CellDataContainer *); + CellDataContainer * + GetCellData(); + const CellDataContainer * + GetCellData() const; + + /** Access routines to fill the CellData container, and get information + * from it. */ + void SetCellData(CellIdentifier, CellPixelType); + bool + GetCellData(CellIdentifier, CellPixelType *) const; + +protected: + PolyData(); + ~PolyData() override = default; + + void + PrintSelf(std::ostream & os, Indent indent) const override; + + /** An object containing points used by the mesh. Individual points are + * accessed through point identifiers. */ + typename PointsContainer::Pointer m_PointsContainer; + + typename CellsContainer::Pointer m_VerticesContainer; + typename CellsContainer::Pointer m_LinesContainer; + typename CellsContainer::Pointer m_PolygonsContainer; + typename CellsContainer::Pointer m_TriangleStripsContainer; + + /** An object containing data associated with the mesh's points. + * Optionally, this can be nullptr, indicating that no data are associated with + * the points. The data for a point can be accessed through its point + * identifier. */ + typename PointDataContainer::Pointer m_PointDataContainer; + + typename CellDataContainer::Pointer m_CellDataContainer; + +private: +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkPolyData.hxx" +#endif + +#endif // itkPolyData_h diff --git a/Modules/Filtering/MeshToPolyData/include/itkPolyData.hxx b/Modules/Filtering/MeshToPolyData/include/itkPolyData.hxx new file mode 100644 index 00000000000..1ef8648abde --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkPolyData.hxx @@ -0,0 +1,488 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkPolyData_hxx +#define itkPolyData_hxx + +#include "itkPolyData.h" + +namespace itk +{ + +template +PolyData::PolyData() + : m_PointsContainer(nullptr) + , m_VerticesContainer(nullptr) + , m_LinesContainer(nullptr) + , m_PolygonsContainer(nullptr) + , m_TriangleStripsContainer(nullptr) + , m_PointDataContainer(nullptr) + , m_CellDataContainer(nullptr) +{} + + +template +void +PolyData::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); + os << indent << "Number Of Points: " << this->GetNumberOfPoints() << std::endl; + os << indent << "Point Data Container pointer: " + << ((this->m_PointDataContainer) ? this->m_PointDataContainer.GetPointer() : nullptr) << std::endl; + os << indent + << "Size of Point Data Container: " << ((this->m_PointDataContainer) ? this->m_PointDataContainer->Size() : 0) + << std::endl; + os << indent + << "Cell Data Container pointer: " << ((m_CellDataContainer) ? m_CellDataContainer.GetPointer() : nullptr) + << std::endl; + os << indent << "Size of Cell Data Container: " << ((m_CellDataContainer) ? m_CellDataContainer->Size() : 0) + << std::endl; +} + + +template +void +PolyData::SetPoints(PointsContainer * points) +{ + itkDebugMacro("setting Points container to " << points); + if (m_PointsContainer != points) + { + m_PointsContainer = points; + this->Modified(); + } +} + + +template +auto +PolyData::GetPoints() -> PointsContainer * +{ + itkDebugMacro("Starting GetPoints()"); + if (!m_PointsContainer) + { + this->SetPoints(PointsContainer::New()); + } + itkDebugMacro("returning Points container of " << m_PointsContainer); + return m_PointsContainer; +} + + +template +auto +PolyData::GetPoints() const -> const PointsContainer * +{ + itkDebugMacro("returning Points container of " << m_PointsContainer); + return m_PointsContainer.GetPointer(); +} + + +template +void +PolyData::SetVertices(CellsContainer * vertices) +{ + itkDebugMacro("setting Vertices container to " << vertices); + if (m_VerticesContainer != vertices) + { + m_VerticesContainer = vertices; + this->Modified(); + } +} + + +template +auto +PolyData::GetVertices() -> CellsContainer * +{ + itkDebugMacro("Starting GetVertices()"); + if (!m_VerticesContainer) + { + this->SetVertices(CellsContainer::New()); + } + itkDebugMacro("returning Vertices container of " << m_VerticesContainer); + return m_VerticesContainer.GetPointer(); +} + + +template +auto +PolyData::GetVertices() const -> const CellsContainer * +{ + itkDebugMacro("returning Vertices container of " << m_VerticesContainer); + return m_VerticesContainer.GetPointer(); +} + + +template +void +PolyData::SetLines(CellsContainer * lines) +{ + itkDebugMacro("setting Lines container to " << lines); + if (m_LinesContainer != lines) + { + m_LinesContainer = lines; + this->Modified(); + } +} + + +template +auto +PolyData::GetLines() -> CellsContainer * +{ + itkDebugMacro("Starting GetLines()"); + if (!m_LinesContainer) + { + this->SetLines(CellsContainer::New()); + } + itkDebugMacro("returning Lines container of " << m_LinesContainer); + return m_LinesContainer.GetPointer(); +} + + +template +auto +PolyData::GetLines() const -> const CellsContainer * +{ + itkDebugMacro("returning Lines container of " << m_LinesContainer); + return m_LinesContainer.GetPointer(); +} + + +template +void +PolyData::SetPolygons(CellsContainer * polygons) +{ + itkDebugMacro("setting Polygons container to " << polygons); + if (m_PolygonsContainer != polygons) + { + m_PolygonsContainer = polygons; + this->Modified(); + } +} + + +template +auto +PolyData::GetPolygons() -> CellsContainer * +{ + itkDebugMacro("Starting GetPolygons()"); + if (!m_PolygonsContainer) + { + this->SetPolygons(CellsContainer::New()); + } + itkDebugMacro("returning Polygons container of " << m_PolygonsContainer); + return m_PolygonsContainer.GetPointer(); +} + + +template +auto +PolyData::GetPolygons() const -> const CellsContainer * +{ + itkDebugMacro("returning Polygons container of " << m_PolygonsContainer); + return m_PolygonsContainer.GetPointer(); +} + + +template +void +PolyData::SetTriangleStrips(CellsContainer * polygons) +{ + itkDebugMacro("setting TriangleStrips container to " << polygons); + if (m_TriangleStripsContainer != polygons) + { + m_TriangleStripsContainer = polygons; + this->Modified(); + } +} + + +template +auto +PolyData::GetTriangleStrips() -> CellsContainer * +{ + itkDebugMacro("Starting GetTriangleStrips()"); + if (!m_TriangleStripsContainer) + { + this->SetTriangleStrips(CellsContainer::New()); + } + itkDebugMacro("returning TriangleStrips container of " << m_TriangleStripsContainer); + return m_TriangleStripsContainer.GetPointer(); +} + + +template +auto +PolyData::GetTriangleStrips() const -> const CellsContainer * +{ + itkDebugMacro("returning TriangleStrips container of " << m_TriangleStripsContainer); + return m_TriangleStripsContainer.GetPointer(); +} + + +template +void +PolyData::SetPointData(PointDataContainer * pointData) +{ + itkDebugMacro("setting PointData container to " << pointData); + if (m_PointDataContainer != pointData) + { + m_PointDataContainer = pointData; + this->Modified(); + } +} + + +template +auto +PolyData::GetPointData() -> PointDataContainer * +{ + if (!m_PointDataContainer) + { + this->SetPointData(PointDataContainer::New()); + } + itkDebugMacro("returning PointData container of " << m_PointDataContainer); + return m_PointDataContainer; +} + + +template +auto +PolyData::GetPointData() const -> const PointDataContainer * +{ + itkDebugMacro("returning PointData container of " << m_PointDataContainer); + return m_PointDataContainer.GetPointer(); +} + + +template +void +PolyData::SetPoint(PointIdentifier ptId, PointType point) +{ + /** + * Make sure a points container exists. + */ + if (!m_PointsContainer) + { + this->SetPoints(PointsContainer::New()); + } + + /** + * Insert the point into the container with the given identifier. + */ + m_PointsContainer->InsertElement(ptId, point); +} + + +template +bool +PolyData::GetPoint(PointIdentifier ptId, PointType * point) const +{ + /** + * If the points container doesn't exist, then the point doesn't either. + */ + if (!m_PointsContainer) + { + return false; + } + + /** + * Ask the container if the point identifier exists. + */ + return m_PointsContainer->GetElementIfIndexExists(ptId, point); +} + + +template +auto +PolyData::GetPoint(PointIdentifier ptId) const -> PointType +{ + /** + * If the points container doesn't exist, then the point doesn't either. + */ + if (!m_PointsContainer) + { + itkExceptionMacro("Point container doesn't exist."); + } + + /** + * Ask the container if the point identifier exists. + */ + PointType point; + bool exist = m_PointsContainer->GetElementIfIndexExists(ptId, &point); + if (!exist) + { + itkExceptionMacro("Point id doesn't exist: " << ptId); + } + return point; +} + + +template +void +PolyData::SetPointData(PointIdentifier ptId, PixelType data) +{ + /** + * Make sure a point data container exists. + */ + if (!m_PointDataContainer) + { + this->SetPointData(PointDataContainer::New()); + } + + /** + * Insert the point data into the container with the given identifier. + */ + m_PointDataContainer->InsertElement(ptId, data); +} + + +template +bool +PolyData::GetPointData(PointIdentifier ptId, PixelType * data) const +{ + /** + * If the point data container doesn't exist, then the point data doesn't + * either. + */ + if (!m_PointDataContainer) + { + return false; + } + + /** + * Ask the container if the point identifier exists. + */ + return m_PointDataContainer->GetElementIfIndexExists(ptId, data); +} + + +template +auto +PolyData::GetNumberOfPoints() const -> PointIdentifier +{ + if (m_PointsContainer) + { + return m_PointsContainer->Size(); + } + return 0; +} + + +template +void +PolyData::SetCellData(CellDataContainer * cellData) +{ + itkDebugMacro("setting CellData container to " << cellData); + if (m_CellDataContainer != cellData) + { + m_CellDataContainer = cellData; + this->Modified(); + } +} + + +template +auto +PolyData::GetCellData() -> CellDataContainer * +{ + itkDebugMacro("returning CellData container of " << m_CellDataContainer); + return m_CellDataContainer; +} + + +template +auto +PolyData::GetCellData() const -> const CellDataContainer * +{ + itkDebugMacro("returning CellData container of " << m_CellDataContainer); + return m_CellDataContainer; +} + + +template +void +PolyData::SetCellData(CellIdentifier cellId, TCellPixel data) +{ + /** + * Assign data to a cell identifier. If a spot for the cell identifier + * does not exist, it will be created automatically. There is no check if + * a cell with the same identifier exists. + */ + + /** + * Make sure a cell data container exists. + */ + if (!m_CellDataContainer) + { + this->SetCellData(CellDataContainer::New()); + } + + /** + * Insert the cell data into the container with the given identifier. + */ + m_CellDataContainer->InsertElement(cellId, data); +} + + +template +bool +PolyData::GetCellData(CellIdentifier cellId, TCellPixel * data) const +{ + /** + * Check if cell data exists for a given cell identifier. If a spot for + * the cell identifier exists, "data" is set, and true is returned. + * Otherwise, false is returned, and "data" is not modified. + * If "data" is nullptr, then it is never set, but the existence of the cell + * data is still returned. + */ + + /** + * If the cell data container doesn't exist, then the cell data doesn't + * either. + */ + if (!m_CellDataContainer) + { + return false; + } + + /** + * Ask the container if the cell identifier exists. + */ + return m_CellDataContainer->GetElementIfIndexExists(cellId, data); +} + + +template +void +PolyData::Initialize() +{ + Superclass::Initialize(); + + m_PointsContainer = nullptr; + m_PointDataContainer = nullptr; + m_CellDataContainer = nullptr; + + m_VerticesContainer = nullptr; + m_LinesContainer = nullptr; + m_PolygonsContainer = nullptr; + m_TriangleStripsContainer = nullptr; +} + +} // end namespace itk + +#endif // itkPolyData_hxx diff --git a/Modules/Filtering/MeshToPolyData/include/itkPolyDataToMeshFilter.h b/Modules/Filtering/MeshToPolyData/include/itkPolyDataToMeshFilter.h new file mode 100644 index 00000000000..8aeeabe9c1f --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkPolyDataToMeshFilter.h @@ -0,0 +1,118 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkPolyDataToMeshFilter_h +#define itkPolyDataToMeshFilter_h + +#include "itkProcessObject.h" +#include "itkPolyData.h" +#include "itkMesh.h" + +#include + +namespace itk +{ +/** \class PolyDataToMeshFilter + * + * \brief Convert PolyData to a Mesh + * + * Convert an itk::PolyData to an itk::PointSet or itk::Mesh + * + * \ingroup MeshToPolyData + * + */ +template +class PolyDataToMeshFilter : public ProcessObject +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(PolyDataToMeshFilter); + + /** Standard class typedefs. */ + using Self = PolyDataToMeshFilter; + using Superclass = ProcessObject; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; + + /** Standard New macro. */ + itkNewMacro(Self); + + /** Run-time type information. */ + itkOverrideGetNameOfClassMacro(PolyDataToMeshFilter); + + static constexpr unsigned int PointDimension = TInputPolyData::PointDimension; + + using InputPolyDataType = TInputPolyData; + using PolyDataType = InputPolyDataType; + using OutputMeshType = Mesh; + using MeshType = OutputMeshType; + + using OutputCoordinateType = typename OutputMeshType::CoordinateType; + using OutputPointPixelType = typename OutputMeshType::PixelType; + using OutputCellPixelType = typename OutputMeshType::CellPixelType; + using OutputPointType = typename OutputMeshType::PointType; + using OutputPointIdentifier = typename OutputMeshType::PointIdentifier; + using OutputCellIdentifier = typename OutputMeshType::CellIdentifier; + using OutputCellAutoPointer = typename OutputMeshType::CellAutoPointer; + using OutputCellType = typename OutputMeshType::CellType; + + /** Set the polydata input of this process object. */ + using Superclass::SetInput; + void + SetInput(const InputPolyDataType * input); + + /** Get the polydata input of this process object. */ + const InputPolyDataType * + GetInput() const; + + const InputPolyDataType * + GetInput(unsigned int idx) const; + + OutputMeshType * + GetOutput(); + const OutputMeshType * + GetOutput() const; + + OutputMeshType * + GetOutput(unsigned int idx); + + void + GenerateOutputInformation() override; + +protected: + PolyDataToMeshFilter(); + ~PolyDataToMeshFilter() override = default; + + void + PrintSelf(std::ostream & os, Indent indent) const override; + + void + GenerateData() override; + + ProcessObject::DataObjectPointer + MakeOutput(ProcessObject::DataObjectPointerArraySizeType idx) override; + ProcessObject::DataObjectPointer + MakeOutput(const ProcessObject::DataObjectIdentifierType &) override; + +private: +}; +} // namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkPolyDataToMeshFilter.hxx" +#endif + +#endif // itkPolyDataToMeshFilter diff --git a/Modules/Filtering/MeshToPolyData/include/itkPolyDataToMeshFilter.hxx b/Modules/Filtering/MeshToPolyData/include/itkPolyDataToMeshFilter.hxx new file mode 100644 index 00000000000..305e2a48b59 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/include/itkPolyDataToMeshFilter.hxx @@ -0,0 +1,384 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkPolyDataToMeshFilter_hxx +#define itkPolyDataToMeshFilter_hxx + +#include "itkPolyDataToMeshFilter.h" + +#include "itkVertexCell.h" +#include "itkLineCell.h" +#include "itkPolyLineCell.h" +#include "itkMesh.h" +#include "itkTriangleCell.h" +#include "itkQuadrilateralCell.h" +#include "itkPolygonCell.h" + +namespace itk +{ + +template +PolyDataToMeshFilter::PolyDataToMeshFilter() +{ + // Modify superclass default values, can be overridden by subclasses + this->SetNumberOfRequiredInputs(1); + + typename MeshType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); + this->ProcessObject::SetNumberOfRequiredOutputs(1); + this->ProcessObject::SetNthOutput(0, output.GetPointer()); +} + + +template +void +PolyDataToMeshFilter::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); +} + + +template +void +PolyDataToMeshFilter::SetInput(const TInputPolyData * input) +{ + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput(0, const_cast(input)); +} + + +template +const typename PolyDataToMeshFilter::InputPolyDataType * +PolyDataToMeshFilter::GetInput() const +{ + return itkDynamicCastInDebugMode(this->GetPrimaryInput()); +} + + +template +const typename PolyDataToMeshFilter::InputPolyDataType * +PolyDataToMeshFilter::GetInput(unsigned int idx) const +{ + return dynamic_cast(this->ProcessObject::GetInput(idx)); +} + + +template +ProcessObject::DataObjectPointer +PolyDataToMeshFilter::MakeOutput(ProcessObject::DataObjectPointerArraySizeType) +{ + return MeshType::New().GetPointer(); +} + + +template +ProcessObject::DataObjectPointer +PolyDataToMeshFilter::MakeOutput(const ProcessObject::DataObjectIdentifierType &) +{ + return MeshType::New().GetPointer(); +} + + +template +typename PolyDataToMeshFilter::MeshType * +PolyDataToMeshFilter::GetOutput() +{ + // we assume that the first output is of the templated type + return itkDynamicCastInDebugMode(this->GetPrimaryOutput()); +} + + +template +const typename PolyDataToMeshFilter::MeshType * +PolyDataToMeshFilter::GetOutput() const +{ + // we assume that the first output is of the templated type + return itkDynamicCastInDebugMode(this->GetPrimaryOutput()); +} + + +template +typename PolyDataToMeshFilter::MeshType * +PolyDataToMeshFilter::GetOutput(unsigned int idx) +{ + auto * out = dynamic_cast(this->ProcessObject::GetOutput(idx)); + + if (out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr) + { + itkWarningMacro(<< "Unable to convert output number " << idx << " to type " << typeid(MeshType).name()); + } + return out; +} + +template +void +PolyDataToMeshFilter::GenerateOutputInformation() +{ + // Polydata does not contain sufficient metadata to inform a PointSet or Mesh object + // so do nothing and leave default point set metadata values + + // m_MaximumNumberOfRegions = 1; + // m_NumberOfRegions = 1; + // m_BufferedRegion = -1; + // m_RequestedNumberOfRegions = 0; + // m_RequestedRegion = -1; +} + +template +void +PolyDataToMeshFilter::GenerateData() +{ + const InputPolyDataType * inputPolyData = this->GetInput(); + MeshType * outputMesh = this->GetOutput(); + + // Set points in output mesh + using PolyDataPointsContainerType = typename InputPolyDataType::PointsContainer; + using MeshPointsContainerType = typename OutputMeshType::PointsContainer; + const PolyDataPointsContainerType * inputPoints = inputPolyData->GetPoints(); + typename MeshPointsContainerType::Pointer outputPoints = MeshPointsContainerType::New(); + outputPoints->resize(inputPoints->Size()); + + typename PolyDataPointsContainerType::ConstIterator inputPointItr = inputPoints->Begin(); + typename PolyDataPointsContainerType::ConstIterator inputPointEnd = inputPoints->End(); + + typename MeshPointsContainerType::Iterator outputPointItr = outputPoints->Begin(); + while (inputPointItr != inputPointEnd) + { + for (unsigned int ii = 0; ii < InputPolyDataType::PointDimension; ++ii) + { + outputPointItr.Value()[ii] = inputPointItr.Value()[ii]; + } + ++inputPointItr; + ++outputPointItr; + } + outputMesh->SetPoints(outputPoints); + + // Set point data in output mesh + using PointDataContainerType = typename InputPolyDataType::PointDataContainer; + const PointDataContainerType * inputPointData = inputPolyData->GetPointData(); + if (inputPointData) + { + typename PointDataContainerType::Pointer outputPointData = PointDataContainerType::New(); + outputPointData->Reserve(inputPointData->Size()); + + typename PointDataContainerType::ConstIterator inputPointDataItr = inputPointData->Begin(); + typename PointDataContainerType::ConstIterator inputPointDataEnd = inputPointData->End(); + + typename PointDataContainerType::Iterator outputPointDataItr = outputPointData->Begin(); + + while (inputPointDataItr != inputPointDataEnd) + { + outputPointDataItr.Value() = inputPointDataItr.Value(); + ++inputPointDataItr; + ++outputPointDataItr; + } + outputMesh->SetPointData(outputPointData); + } + + // Set different cell types + using CellContainerType = typename InputPolyDataType::CellsContainer; + using CellType = typename OutputMeshType::CellType; + typename CellContainerType::ConstIterator inputCellItr; + typename CellContainerType::ConstIterator inputCellEnd; + + IdentifierType cellId = 0; + + // Set vertex cells + using VertexCellType = itk::VertexCell; + if (inputPolyData->GetVertices() != nullptr && !inputPolyData->GetVertices()->empty()) + { + const CellContainerType * inputVertices = inputPolyData->GetVertices(); + inputCellItr = inputVertices->Begin(); + inputCellEnd = inputVertices->End(); + + while (inputCellItr != inputCellEnd) + { +#ifndef NDEBUG + auto numPoints = inputCellItr.Value(); +#endif + ++inputCellItr; + + // Verify vertex contains exactly one point ID + itkAssertInDebugAndIgnoreInReleaseMacro(numPoints == VertexCellType::NumberOfPoints); + + // Create cell + typename CellType::CellAutoPointer cell; + cell.TakeOwnership(new VertexCellType); + + if (inputCellItr != inputCellEnd) + { + cell->SetPointId(0, inputCellItr.Value()); + ++inputCellItr; + } + + outputMesh->SetCell(cellId, cell); + cellId++; + } + } + + // Set line cells + using LineCellType = itk::LineCell; + using PolyLineCellType = itk::PolyLineCell; + + if (inputPolyData->GetLines() != nullptr && !inputPolyData->GetLines()->empty()) + { + const CellContainerType * inputLines = inputPolyData->GetLines(); + inputCellItr = inputLines->Begin(); + inputCellEnd = inputLines->End(); + + while (inputCellItr != inputCellEnd) + { + auto numPoints = inputCellItr.Value(); + ++inputCellItr; + + typename CellType::CellAutoPointer cell; + // Use PolyLineCell Type + if (numPoints > LineCellType::NumberOfPoints) + { + cell.TakeOwnership(new PolyLineCellType); + } + // Use LineCell Type + else + { + cell.TakeOwnership(new LineCellType); + } + + for (unsigned int i = 0; i < numPoints && inputCellItr != inputCellEnd; i++) + { + cell->SetPointId(i, inputCellItr.Value()); + ++inputCellItr; + } + + outputMesh->SetCell(cellId, cell); + cellId++; + } + } + + // Set triangle cells from strips + using TriangleCellType = itk::TriangleCell; + if (inputPolyData->GetTriangleStrips() != nullptr && !inputPolyData->GetTriangleStrips()->empty()) + { + const CellContainerType * inputStrips = inputPolyData->GetTriangleStrips(); + inputCellItr = inputStrips->Begin(); + inputCellEnd = inputStrips->End(); + + while (inputCellItr != inputCellEnd) + { + auto numPoints = inputCellItr.Value(); + ++inputCellItr; + + // A triangle strip needs at least three points; skip degenerate + // strips so the unsigned numPoints - 2 below cannot underflow. + if (numPoints < TriangleCellType::NumberOfPoints) + { + inputCellItr += numPoints; + continue; + } + + for (unsigned int i = 0; i < numPoints - 2; i++) + { + typename CellType::CellAutoPointer cell; + cell.TakeOwnership(new TriangleCellType); + + cell->SetPointId(0, inputCellItr.Value()); + inputCellItr++; + cell->SetPointId(1, inputCellItr.Value()); + inputCellItr++; + cell->SetPointId(2, inputCellItr.Value()); + inputCellItr--; + + outputMesh->SetCell(cellId, cell); + cellId++; + } + inputCellItr += 2; + } + } + + // Set polygons + const auto * inputPolygons = inputPolyData->GetPolygons(); + if (inputPolygons != nullptr && !inputPolygons->empty()) + { + inputCellItr = inputPolygons->Begin(); + inputCellEnd = inputPolygons->End(); + + using QuadrilateralCellType = itk::QuadrilateralCell; + using PolygonCellType = itk::PolygonCell; + + // Polygons are stored in a 1D list as [# points] [p1] [p2] ... [# points] [p1] [p2] ... etc + while (inputCellItr != inputCellEnd) + { + auto numPoints = inputCellItr.Value(); + ++inputCellItr; + + typename CellType::CellAutoPointer cell; + if (numPoints == VertexCellType::NumberOfPoints) + { + cell.TakeOwnership(new VertexCellType); + } + else if (numPoints == LineCellType::NumberOfPoints) + { + cell.TakeOwnership(new LineCellType); + } + else if (numPoints == TriangleCellType::NumberOfPoints) + { + cell.TakeOwnership(new TriangleCellType); + } + else if (numPoints == QuadrilateralCellType::NumberOfPoints) + { + cell.TakeOwnership(new QuadrilateralCellType); + } + else + { + cell.TakeOwnership(new PolygonCellType(numPoints)); + } + + for (unsigned int i = 0; i < numPoints && inputCellItr != inputCellEnd; i++) + { + cell->SetPointId(i, inputCellItr.Value()); + ++inputCellItr; + } + + outputMesh->SetCell(cellId, cell); + cellId++; + } + } + + // Set cell data in output mesh + using CellDataContainerType = typename InputPolyDataType::CellDataContainer; + const CellDataContainerType * inputCellData = inputPolyData->GetCellData(); + if (inputCellData) + { + typename CellDataContainerType::Pointer outputCellData = CellDataContainerType::New(); + outputCellData->Reserve(inputCellData->Size()); + + typename CellDataContainerType::ConstIterator inputCellDataItr = inputCellData->Begin(); + typename CellDataContainerType::ConstIterator inputCellDataEnd = inputCellData->End(); + + typename CellDataContainerType::Iterator outputCellDataItr = outputCellData->Begin(); + + while (inputCellDataItr != inputCellDataEnd) + { + outputCellDataItr.Value() = inputCellDataItr.Value(); + ++inputCellDataItr; + ++outputCellDataItr; + } + outputMesh->SetCellData(outputCellData); + } +} + +} // end namespace itk + +#endif // itkPolyDataToMeshFilter_hxx diff --git a/Modules/Filtering/MeshToPolyData/itk-module.cmake b/Modules/Filtering/MeshToPolyData/itk-module.cmake new file mode 100644 index 00000000000..cf5025363cc --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/itk-module.cmake @@ -0,0 +1,15 @@ +# Public headers expose itk::Mesh types (ITKMesh), so it is a DEPENDS, +# not a COMPILE_DEPENDS, for correct wrapping/link propagation. +itk_module( + MeshToPolyData + DEPENDS + ITKCommon + ITKMesh + TEST_DEPENDS + ITKTestKernel + ITKIOMeshVTK + DESCRIPTION + "Convert an ITK Mesh to a PolyData data structure compatible with vtkPolyData, and back, with supporting filters." + EXCLUDE_FROM_DEFAULT + ENABLE_SHARED +) diff --git a/Modules/Filtering/MeshToPolyData/test/CMakeLists.txt b/Modules/Filtering/MeshToPolyData/test/CMakeLists.txt new file mode 100644 index 00000000000..8bf8999e5fd --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/CMakeLists.txt @@ -0,0 +1,51 @@ +itk_module_test() + +set( + MeshToPolyDataTests + itkImageToPointSetFilterTest.cxx + itkMeshToPolyDataFilterTest.cxx + itkMeshToPolyDataFilterCellDataTest.cxx + itkPolyDataTest.cxx + itkPolyDataToMeshFilterTest.cxx +) + +createtestdriver(MeshToPolyData "${MeshToPolyData-Test_LIBRARIES}" "${MeshToPolyDataTests}") + +itk_add_test( + NAME itkMeshToPolyDataFilterTest + COMMAND + MeshToPolyDataTestDriver + itkMeshToPolyDataFilterTest + DATA{Input/cow.vtk} + ${ITK_TEST_OUTPUT_DIR}/itkMeshToPolyDataFilterTest.vtk +) + +itk_add_test( + NAME itkMeshToPolyDataFilterCellDataTest + COMMAND + MeshToPolyDataTestDriver + itkMeshToPolyDataFilterCellDataTest +) + +itk_add_test( + NAME itkPolyDataTest + COMMAND + MeshToPolyDataTestDriver + itkPolyDataTest +) + +itk_add_test( + NAME itkImageToPointSetFilterTest + COMMAND + MeshToPolyDataTestDriver + itkImageToPointSetFilterTest + DATA{Input/foot.mha} + ${ITK_TEST_OUTPUT_DIR}/itkImageToPointSetFilterTestOutput.vtk +) + +itk_add_test( + NAME itkPolyDataToMeshFilterTest + COMMAND + MeshToPolyDataTestDriver + itkPolyDataToMeshFilterTest +) diff --git a/Modules/Filtering/MeshToPolyData/test/Input/cow.vtk.cid b/Modules/Filtering/MeshToPolyData/test/Input/cow.vtk.cid new file mode 100644 index 00000000000..971bc850f99 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/Input/cow.vtk.cid @@ -0,0 +1 @@ +bafkreiftiexa5vfhjbjhinkeih4qvwvay5wahoe3jlcqoyp7mq4fvcjw2q diff --git a/Modules/Filtering/MeshToPolyData/test/Input/foot.mha.cid b/Modules/Filtering/MeshToPolyData/test/Input/foot.mha.cid new file mode 100644 index 00000000000..cb03d7b09a1 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/Input/foot.mha.cid @@ -0,0 +1 @@ +bafkreicsnenkzoqwpvk5qscxqdmavlmfjmb3totuplgy6btdkiauwj5gma diff --git a/Modules/Filtering/MeshToPolyData/test/itkImageToPointSetFilterTest.cxx b/Modules/Filtering/MeshToPolyData/test/itkImageToPointSetFilterTest.cxx new file mode 100644 index 00000000000..5c1e99e1148 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/itkImageToPointSetFilterTest.cxx @@ -0,0 +1,65 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#include "itkImageFileReader.h" +#include "itkMesh.h" +#include "itkMeshFileWriter.h" + +#include "itkImageToPointSetFilter.h" + +int +itkImageToPointSetFilterTest(int argc, char * argv[]) +{ + if (argc < 3) + { + std::cerr << "Usage: " << argv[0]; + std::cerr << " inputImage outputMesh"; + std::cerr << std::endl; + return EXIT_FAILURE; + } + + const unsigned int Dimension = 2; + using PixelType = float; + using InputImageType = itk::Image; + using OutputMeshType = itk::Mesh; + + using ReaderType = itk::ImageFileReader; + ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(argv[1]); + + using FilterType = itk::ImageToPointSetFilter; + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(0, reader->GetOutput()); + + using WriterType = itk::MeshFileWriter; + WriterType::Pointer writer = WriterType::New(); + writer->SetInput(filter->GetOutput()); + writer->SetFileName(argv[2]); + + try + { + writer->Update(); + } + catch (itk::ExceptionObject & ex) + { + std::cerr << "Exception caught!" << std::endl; + std::cerr << ex << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Modules/Filtering/MeshToPolyData/test/itkMeshToPolyDataFilterCellDataTest.cxx b/Modules/Filtering/MeshToPolyData/test/itkMeshToPolyDataFilterCellDataTest.cxx new file mode 100644 index 00000000000..36eb49f6741 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/itkMeshToPolyDataFilterCellDataTest.cxx @@ -0,0 +1,93 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include "itkMeshToPolyDataFilter.h" +#include "itkMesh.h" +#include "itkTriangleCell.h" +#include "itkTestingMacros.h" +#include "itkMath.h" + +// Regression test for cell-data preservation: an input mesh carrying +// per-cell data must round-trip through MeshToPolyDataFilter with the +// cell data copied 1:1 in cell-id order. Earlier the cell-id tracking +// containers were sized with VectorContainer::Reserve (which resizes +// with zero-filled entries) instead of the STL capacity hint reserve, +// producing spurious leading entries that corrupted the cell-data copy. +// The fixture-based test uses cow.vtk, which carries no cell data, so +// this in-memory test is the only coverage of that path. +int +itkMeshToPolyDataFilterCellDataTest(int, char *[]) +{ + constexpr unsigned int Dimension = 3; + using PixelType = float; + using MeshType = itk::Mesh; + using PointType = MeshType::PointType; + using CellType = MeshType::CellType; + using CellAutoPointer = CellType::CellAutoPointer; + using TriangleCellType = itk::TriangleCell; + + auto mesh = MeshType::New(); + + PointType p; + p[0] = 0.0; + p[1] = 0.0; + p[2] = 0.0; + mesh->SetPoint(0, p); + p[0] = 1.0; + mesh->SetPoint(1, p); + p[1] = 1.0; + mesh->SetPoint(2, p); + p[0] = 0.0; + mesh->SetPoint(3, p); + + // Two triangles sharing the (0,2) edge. + CellAutoPointer triangle; + triangle.TakeOwnership(new TriangleCellType); + triangle->SetPointId(0, 0); + triangle->SetPointId(1, 1); + triangle->SetPointId(2, 2); + mesh->SetCell(0, triangle); + + triangle.TakeOwnership(new TriangleCellType); + triangle->SetPointId(0, 0); + triangle->SetPointId(1, 2); + triangle->SetPointId(2, 3); + mesh->SetCell(1, triangle); + + mesh->SetCellData(0, 10.0f); + mesh->SetCellData(1, 20.0f); + + using FilterType = itk::MeshToPolyDataFilter; + auto filter = FilterType::New(); + filter->SetInput(mesh); + ITK_TRY_EXPECT_NO_EXCEPTION(filter->Update()); + + using PolyDataType = FilterType::PolyDataType; + const PolyDataType * polyData = filter->GetOutput(); + + // Both cells are triangles, so they land in the polygons container. + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(0), 3); + + const auto * cellData = polyData->GetCellData(); + ITK_TEST_EXPECT_TRUE(cellData != nullptr); + ITK_TEST_EXPECT_EQUAL(cellData->Size(), 2); + ITK_TEST_EXPECT_TRUE(itk::Math::FloatAlmostEqual(cellData->GetElement(0), 10.0f, 10, 1e-4)); + ITK_TEST_EXPECT_TRUE(itk::Math::FloatAlmostEqual(cellData->GetElement(1), 20.0f, 10, 1e-4)); + + return EXIT_SUCCESS; +} diff --git a/Modules/Filtering/MeshToPolyData/test/itkMeshToPolyDataFilterTest.cxx b/Modules/Filtering/MeshToPolyData/test/itkMeshToPolyDataFilterTest.cxx new file mode 100644 index 00000000000..2dacc5e19f3 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/itkMeshToPolyDataFilterTest.cxx @@ -0,0 +1,127 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include "itkMeshToPolyDataFilter.h" +#include "itkPolyDataToMeshFilter.h" + +#include "itkCommand.h" +#include "itkMeshFileReader.h" +#include "itkMeshFileWriter.h" +#include "itkMesh.h" +#include "itkTestingMacros.h" +#include "itkMath.h" + +namespace +{ +class ShowProgress : public itk::Command +{ +public: + itkNewMacro(ShowProgress); + + void + Execute(itk::Object * caller, const itk::EventObject & event) override + { + Execute((const itk::Object *)caller, event); + } + + void + Execute(const itk::Object * caller, const itk::EventObject & event) override + { + if (!itk::ProgressEvent().CheckEvent(&event)) + { + return; + } + const auto * processObject = dynamic_cast(caller); + if (!processObject) + { + return; + } + std::cout << " " << processObject->GetProgress(); + } +}; +} // namespace + + +int +itkMeshToPolyDataFilterTest(int argc, char * argv[]) +{ + if (argc < 3) + { + std::cerr << "Usage: " << argv[0]; + std::cerr << " inputMesh outputPolyData"; + std::cerr << std::endl; + return EXIT_FAILURE; + } + const char * inputMeshFileName = argv[1]; + const char * outputPolyDataFileName = argv[2]; + + const unsigned int Dimension = 3; + using PixelType = float; + using MeshType = itk::Mesh; + + using MeshReaderType = itk::MeshFileReader; + MeshReaderType::Pointer meshReader = MeshReaderType::New(); + meshReader->SetFileName(inputMeshFileName); + meshReader->Update(); + + using FilterType = itk::MeshToPolyDataFilter; + FilterType::Pointer filter = FilterType::New(); + + ITK_EXERCISE_BASIC_OBJECT_METHODS(filter, MeshToPolyDataFilter, ProcessObject); + + filter->SetInput(meshReader->GetOutput()); + + ShowProgress::Pointer showProgress = ShowProgress::New(); + filter->AddObserver(itk::ProgressEvent(), showProgress); + + ITK_TRY_EXPECT_NO_EXCEPTION(filter->Update()); + + using PolyDataType = FilterType::PolyDataType; + PolyDataType::ConstPointer polyData = filter->GetOutput(); + + ITK_TEST_EXPECT_EQUAL(polyData->GetNumberOfPoints(), 2903); + + PolyDataType::PointsContainer::ConstPointer points = polyData->GetPoints(); + ITK_TEST_EXPECT_TRUE(itk::Math::FloatAlmostEqual(points->GetElement(0)[0], 3.71636, 10, 1e-4)); + ITK_TEST_EXPECT_TRUE(itk::Math::FloatAlmostEqual(points->GetElement(0)[1], 2.34339, 10, 1e-4)); + ITK_TEST_EXPECT_TRUE(itk::Math::FloatAlmostEqual(points->GetElement(0)[2], 0.0, 10, 1e-4)); + + ITK_TEST_EXPECT_EQUAL(polyData->GetVertices()->size(), 0); + ITK_TEST_EXPECT_EQUAL(polyData->GetLines()->size(), 0); + + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->size(), 15593); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(0), 4); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(1), 250); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(2), 251); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(3), 210); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(4), 252); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(5), 4); + ITK_TEST_EXPECT_EQUAL(polyData->GetPolygons()->GetElement(6), 252); + + using PolyDataToMeshFilterType = itk::PolyDataToMeshFilter; + auto polyDataToMeshFilter = PolyDataToMeshFilterType::New(); + polyDataToMeshFilter->SetInput(polyData); + + using MeshWriterType = itk::MeshFileWriter; + auto meshWriter = MeshWriterType::New(); + meshWriter->SetFileName(outputPolyDataFileName); + meshWriter->SetInput(polyDataToMeshFilter->GetOutput()); + ITK_TRY_EXPECT_NO_EXCEPTION(meshWriter->Update()); + + return EXIT_SUCCESS; +} diff --git a/Modules/Filtering/MeshToPolyData/test/itkPolyDataTest.cxx b/Modules/Filtering/MeshToPolyData/test/itkPolyDataTest.cxx new file mode 100644 index 00000000000..08da5063752 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/itkPolyDataTest.cxx @@ -0,0 +1,135 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#include "itkPolyData.h" + +#include "itkTestingMacros.h" + +int +itkPolyDataTest(int, char *[]) +{ + using PixelType = double; + + using PolyDataType = itk::PolyData; + PolyDataType::Pointer polyData = PolyDataType::New(); + constexpr unsigned int PointDimension = PolyDataType::PointDimension; + + polyData->Initialize(); + + + PolyDataType::PointsContainer::Pointer pointsContainer = PolyDataType::PointsContainer::New(); + + PolyDataType::PointType point; + point[0] = 1.0; + point[1] = 3.0; + point[2] = 5.0; + pointsContainer->InsertElement(0, point); + point[0] = 2.0; + point[1] = 4.0; + point[2] = 6.0; + pointsContainer->InsertElement(1, point); + polyData->SetPoints(pointsContainer); + + for (unsigned int dim = 0; dim < PointDimension; ++dim) + { + ITK_TEST_SET_GET_VALUE(point[dim], polyData->GetPoint(1)[dim]); + } + + point[0] = 3.0; + point[1] = 5.0; + point[2] = 7.0; + polyData->SetPoint(2, point); + for (unsigned int dim = 0; dim < PointDimension; ++dim) + { + ITK_TEST_SET_GET_VALUE(point[dim], polyData->GetPoint(2)[dim]); + } + polyData->SetPoint(1, point); + for (unsigned int dim = 0; dim < PointDimension; ++dim) + { + ITK_TEST_SET_GET_VALUE(point[dim], polyData->GetPoint(1)[dim]); + } + + PolyDataType::CellsContainer::Pointer vertices = PolyDataType::CellsContainer::New(); + vertices->InsertElement(0, 1); + vertices->InsertElement(1, 4); + vertices->InsertElement(2, 1); + vertices->InsertElement(3, 7); + polyData->SetVertices(vertices); + ITK_TEST_SET_GET_VALUE(vertices.GetPointer(), polyData->GetVertices()); + + PolyDataType::CellsContainer::Pointer lines = PolyDataType::CellsContainer::New(); + lines->InsertElement(0, 2); + lines->InsertElement(1, 4); + lines->InsertElement(2, 5); + lines->InsertElement(3, 2); + lines->InsertElement(4, 7); + lines->InsertElement(5, 8); + polyData->SetLines(lines); + ITK_TEST_SET_GET_VALUE(lines.GetPointer(), polyData->GetLines()); + + PolyDataType::CellsContainer::Pointer polygons = PolyDataType::CellsContainer::New(); + polygons->InsertElement(0, 4); + polygons->InsertElement(1, 4); + polygons->InsertElement(2, 5); + polygons->InsertElement(3, 6); + polygons->InsertElement(4, 7); + polygons->InsertElement(5, 4); + polygons->InsertElement(6, 8); + polygons->InsertElement(7, 9); + polygons->InsertElement(8, 10); + polygons->InsertElement(9, 11); + polyData->SetPolygons(polygons); + ITK_TEST_SET_GET_VALUE(polygons.GetPointer(), polyData->GetPolygons()); + + PolyDataType::CellsContainer::Pointer triangleStrips = PolyDataType::CellsContainer::New(); + triangleStrips->InsertElement(0, 3); + triangleStrips->InsertElement(1, 4); + triangleStrips->InsertElement(2, 5); + triangleStrips->InsertElement(3, 6); + triangleStrips->InsertElement(4, 3); + triangleStrips->InsertElement(5, 8); + triangleStrips->InsertElement(6, 9); + triangleStrips->InsertElement(7, 10); + polyData->SetTriangleStrips(triangleStrips); + ITK_TEST_SET_GET_VALUE(triangleStrips.GetPointer(), polyData->GetTriangleStrips()); + + PolyDataType::PointDataContainer::Pointer pointDataContainer = PolyDataType::PointDataContainer::New(); + pointDataContainer->InsertElement(0, 2.0); + pointDataContainer->InsertElement(1, 7.0); + polyData->SetPointData(pointDataContainer); + double pointData; + polyData->GetPointData(1, &pointData); + ITK_TEST_SET_GET_VALUE(7.0, pointData); + polyData->SetPointData(2, 9.9); + polyData->GetPointData(2, &pointData); + ITK_TEST_SET_GET_VALUE(9.9, pointData); + + PolyDataType::CellDataContainer::Pointer cellDataContainer = PolyDataType::CellDataContainer::New(); + cellDataContainer->InsertElement(0, 2.0); + cellDataContainer->InsertElement(1, 7.0); + polyData->SetCellData(cellDataContainer); + double cellData; + polyData->GetCellData(1, &cellData); + ITK_TEST_SET_GET_VALUE(7.0, cellData); + polyData->SetCellData(2, 9.9); + polyData->GetCellData(2, &cellData); + ITK_TEST_SET_GET_VALUE(9.9, cellData); + + ITK_EXERCISE_BASIC_OBJECT_METHODS(polyData, PolyData, DataObject); + + return EXIT_SUCCESS; +} diff --git a/Modules/Filtering/MeshToPolyData/test/itkPolyDataToMeshFilterTest.cxx b/Modules/Filtering/MeshToPolyData/test/itkPolyDataToMeshFilterTest.cxx new file mode 100644 index 00000000000..df1d60afc09 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/test/itkPolyDataToMeshFilterTest.cxx @@ -0,0 +1,172 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#include "itkMesh.h" +#include "itkPolyData.h" +#include "itkPolyDataToMeshFilter.h" + +#include "itkTestingMacros.h" + +template +void +MakePolyDataSample(itk::PolyData *); + +int +itkPolyDataToMeshFilterTest(int, char *[]) +{ + + using PixelType = double; + + using PolyDataType = itk::PolyData; + PolyDataType::Pointer polyData = PolyDataType::New(); + + MakePolyDataSample(polyData); + + using FilterType = itk::PolyDataToMeshFilter; + FilterType::Pointer filter = FilterType::New(); + + filter->SetInput(polyData); + + ITK_TRY_EXPECT_NO_EXCEPTION(filter->Update()); + + using MeshType = FilterType::OutputMeshType; + MeshType::Pointer meshResult = filter->GetOutput(); + + ITK_TEST_EXPECT_EQUAL(meshResult->GetNumberOfPoints(), polyData->GetNumberOfPoints()); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(0)[0], 1); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(0)[1], 3); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(0)[2], 5); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(1)[0], 4); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(1)[1], 6); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(1)[2], 8); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(2)[0], 3); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(2)[1], 5); + ITK_TEST_EXPECT_EQUAL(meshResult->GetPoint(2)[2], 7); + + ITK_TEST_EXPECT_EQUAL(meshResult->GetNumberOfCells(), 7); + + // Verify vertex cells + typename MeshType::CellAutoPointer cellPtr; + meshResult->GetCell(0, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 1); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 4); + meshResult->GetCell(1, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 1); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 7); + + // Verify line cells + meshResult->GetCell(2, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 3); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 3); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[1], 4); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[2], 5); + meshResult->GetCell(3, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 2); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 7); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[1], 8); + + // Verify triangle strips (now cells + meshResult->GetCell(4, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 3); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 0); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[1], 2); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[2], 1); + meshResult->GetCell(5, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 3); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 2); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[1], 1); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[2], 3); + + + // Verify triangle cells + meshResult->GetCell(6, cellPtr); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetNumberOfPoints(), 3); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[0], 0); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[1], 1); + ITK_TEST_EXPECT_EQUAL(cellPtr->GetPointIdsContainer()[2], 2); + + return EXIT_SUCCESS; +} + +// From itkPolyDataTest.cxx +template +void +MakePolyDataSample(itk::PolyData * polyData) +{ + using PolyDataType = itk::PolyData; + + polyData->Initialize(); + + using PointsContainerType = typename PolyDataType::PointsContainer; + typename PointsContainerType::Pointer pointsContainer = PointsContainerType::New(); + + typename PolyDataType::PointType point; + point[0] = 1.0; + point[1] = 3.0; + point[2] = 5.0; + pointsContainer->InsertElement(0, point); + point[0] = 2.0; + point[1] = 4.0; + point[2] = 6.0; + pointsContainer->InsertElement(1, point); + point[0] = 3.0; + point[1] = 5.0; + point[2] = 7.0; + pointsContainer->InsertElement(2, point); + polyData->SetPoints(pointsContainer); + + point[0] = 4.0; + point[1] = 6.0; + point[2] = 8.0; + polyData->SetPoint(1, point); + + using CellContainerType = typename PolyDataType::CellsContainer; + typename CellContainerType::Pointer vertices = CellContainerType::New(); + vertices->InsertElement(0, 1); + vertices->InsertElement(1, 4); + vertices->InsertElement(2, 1); + vertices->InsertElement(3, 7); + polyData->SetVertices(vertices); + + typename CellContainerType::Pointer lines = CellContainerType::New(); + lines->InsertElement(0, 3); + lines->InsertElement(1, 3); + lines->InsertElement(2, 4); + lines->InsertElement(3, 5); + + lines->InsertElement(4, 2); + lines->InsertElement(5, 7); + lines->InsertElement(6, 8); + polyData->SetLines(lines); + + typename CellContainerType::Pointer strips = CellContainerType::New(); + strips->InsertElement(0, 4); + strips->InsertElement(1, 0); + strips->InsertElement(2, 2); + strips->InsertElement(3, 1); + strips->InsertElement(4, 3); + polyData->SetTriangleStrips(strips); + + typename CellContainerType::Pointer polygons = CellContainerType::New(); + polygons->InsertElement(0, 3); + polygons->InsertElement(1, 0); + polygons->InsertElement(2, 1); + polygons->InsertElement(3, 2); + polyData->SetPolygons(polygons); + + return; +} diff --git a/Modules/Filtering/MeshToPolyData/wrapping/CMakeLists.txt b/Modules/Filtering/MeshToPolyData/wrapping/CMakeLists.txt new file mode 100644 index 00000000000..b523084a390 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/CMakeLists.txt @@ -0,0 +1,3 @@ +itk_wrap_module(MeshToPolyData) +itk_auto_load_submodules() +itk_end_wrap_module() diff --git a/Modules/Filtering/MeshToPolyData/wrapping/itkImageToPointSetFilter.wrap b/Modules/Filtering/MeshToPolyData/wrapping/itkImageToPointSetFilter.wrap new file mode 100644 index 00000000000..5c179114c97 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/itkImageToPointSetFilter.wrap @@ -0,0 +1,10 @@ +itk_wrap_include("itkPointSet.h") +itk_wrap_include("itkDefaultStaticMeshTraits.h") + +itk_wrap_class("itk::ImageToPointSetFilter" POINTER) + foreach(t ${WRAP_ITK_SCALAR}) + foreach(d ${ITK_WRAP_IMAGE_DIMS}) + itk_wrap_template("${ITKM_I${t}${d}}PS${ITKM_${t}}${d}" "${ITKT_I${t}${d}}, itk::PointSet < ${ITKT_${t}},${d} >") + endforeach() + endforeach() +itk_end_wrap_class() diff --git a/Modules/Filtering/MeshToPolyData/wrapping/itkMeshToPolyDataFilter.wrap b/Modules/Filtering/MeshToPolyData/wrapping/itkMeshToPolyDataFilter.wrap new file mode 100644 index 00000000000..f5ee0d92372 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/itkMeshToPolyDataFilter.wrap @@ -0,0 +1,13 @@ +itk_wrap_include("itkPointSet.h") +itk_wrap_include("itkMesh.h") +itk_wrap_include("itkDefaultStaticMeshTraits.h") + +itk_wrap_class("itk::MeshToPolyDataFilter" POINTER) + UNIQUE(types "${WRAP_ITK_SCALAR};D") + foreach(d ${ITK_WRAP_IMAGE_DIMS}) + foreach(t ${types}) + itk_wrap_template("M${ITKM_${t}}${d}" "itk::Mesh< ${ITKT_${t}},${d} >") + itk_wrap_template("PS${ITKM_${t}}${d}" "itk::PointSet< ${ITKT_${t}},${d} >") + endforeach() + endforeach() +itk_end_wrap_class() diff --git a/Modules/Filtering/MeshToPolyData/wrapping/itkPolyData.wrap b/Modules/Filtering/MeshToPolyData/wrapping/itkPolyData.wrap new file mode 100644 index 00000000000..2b01b2f63bd --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/itkPolyData.wrap @@ -0,0 +1,6 @@ +itk_wrap_class("itk::PolyData" POINTER) + UNIQUE(types "${WRAP_ITK_SCALAR};D") + foreach(t ${types}) + itk_wrap_template("${ITKM_${t}}" "${ITKT_${t}}") + endforeach() +itk_end_wrap_class() diff --git a/Modules/Filtering/MeshToPolyData/wrapping/itkPolyDataToMeshFilter.wrap b/Modules/Filtering/MeshToPolyData/wrapping/itkPolyDataToMeshFilter.wrap new file mode 100644 index 00000000000..2a3d07ea5cc --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/itkPolyDataToMeshFilter.wrap @@ -0,0 +1,9 @@ +itk_wrap_include("itkPolyData.h") +itk_wrap_include("itkDefaultStaticMeshTraits.h") + +itk_wrap_class("itk::PolyDataToMeshFilter" POINTER) + UNIQUE(types "${WRAP_ITK_SCALAR};D") + foreach(t ${types}) + itk_wrap_template("PD${ITKM_${t}}" "itk::PolyData< ${ITKT_${t}} >") + endforeach() +itk_end_wrap_class() diff --git a/Modules/Filtering/MeshToPolyData/wrapping/test/CMakeLists.txt b/Modules/Filtering/MeshToPolyData/wrapping/test/CMakeLists.txt new file mode 100644 index 00000000000..233ac920203 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/test/CMakeLists.txt @@ -0,0 +1,30 @@ +itk_python_expression_add_test( + NAME itkMeshToPolyDataFilterPythonTest + EXPRESSION "filt = itk.MeshToPolyDataFilter.New()" +) +itk_python_add_test( + NAME itkMeshToPolyDataFilterPythonTest2 + COMMAND + itkMeshToPolyDataFilterTest2.py +) +itk_python_expression_add_test( + NAME itkPolyDataPythonTest + EXPRESSION "poly_data = itk.PolyData.New()" +) + +# The following Python tests require numpy. +execute_process( + COMMAND + ${PYTHON_EXECUTABLE} -c "import numpy" + RESULT_VARIABLE _have_numpy_return_code + OUTPUT_QUIET + ERROR_QUIET +) +if(_have_numpy_return_code EQUAL 0) + itk_python_add_test(NAME itkPolyLineCellTest COMMAND itkPolyLineCellTest.py) + itk_python_add_test( + NAME itkPyVectorContainerTest + COMMAND + ${CMAKE_CURRENT_SOURCE_DIR}/itkPyVectorContainerTest.py + ) +endif() diff --git a/Modules/Filtering/MeshToPolyData/wrapping/test/itkMeshToPolyDataFilterTest2.py b/Modules/Filtering/MeshToPolyData/wrapping/test/itkMeshToPolyDataFilterTest2.py new file mode 100644 index 00000000000..84986c9b1d4 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/test/itkMeshToPolyDataFilterTest2.py @@ -0,0 +1,47 @@ +# ========================================================================== +# +# Copyright NumFOCUS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ==========================================================================*/ + +import itk + +Dimension = 3 +PixelType = itk.ctype("double") +MeshType = itk.Mesh[PixelType, Dimension] +mesh = MeshType.New() +PointType = itk.Point[itk.F, Dimension] +point0 = PointType() +point0[0] = -1 +point0[1] = -1 +mesh.SetPoint(0, point0) +point1 = PointType() +point1[0] = 1 +point1[1] = -1 +mesh.SetPoint(1, point1) +point2 = PointType() +point2[0] = 1 +point2[1] = 1 +mesh.SetPoint(2, point2) +point3 = PointType() +point3[0] = 1 +point3[1] = 1 +mesh.SetPoint(3, point3) + +filt = itk.MeshToPolyDataFilter.New(mesh) +filt.Update() +polydata = filt.GetOutput() +print(polydata) +assert polydata.GetNumberOfPoints() == 4 diff --git a/Modules/Filtering/MeshToPolyData/wrapping/test/itkPolyLineCellTest.py b/Modules/Filtering/MeshToPolyData/wrapping/test/itkPolyLineCellTest.py new file mode 100644 index 00000000000..cf636303f07 --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/test/itkPolyLineCellTest.py @@ -0,0 +1,44 @@ +import itk +import numpy as np +import os + +# Create a test mesh +mesh_input = itk.Mesh[itk.D, 3].New() + +# Inserting 5 points in the Mesh +points_arr = np.array([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 2, 1, 2, 3]).astype("float32") +points_vc = itk.vector_container_from_array(points_arr.flatten()) +mesh_input.SetPoints(points_vc) + +# Inserting 3 cells comprising 1 PolyLine Cell and 2 Line Cells +# LINES 3 10 +# 2 0 1 +# 3 0 2 5 +# 2 3 4 +before_cells_array = np.array([1, 2, 0, 1, 10, 3, 0, 2, 5, 1, 2, 3, 4]).astype("uint64") +mesh_input.SetCellsArray(itk.vector_container_from_array(before_cells_array)) + +# Convert Mesh to PolyData +filter = itk.MeshToPolyDataFilter[type(mesh_input)].New(Input=mesh_input) +filter.Update() +poly_data = filter.GetOutput() +assert type(poly_data) == itk.PolyData[itk.D] +lines = poly_data.GetLines() + +# Check the count of points in line cells of polydata +assert lines.Size() == 10 + +filter = itk.PolyDataToMeshFilter[type(poly_data)].New(Input=poly_data) +filter.Update() +mesh_output = filter.GetOutput() + +assert mesh_output.GetNumberOfPoints() == mesh_input.GetNumberOfPoints() +assert mesh_output.GetNumberOfCells() == mesh_input.GetNumberOfCells() + +# Check if points are same +for i in range(0, mesh_output.GetNumberOfPoints()): + assert mesh_output.GetPoint(i) == mesh_input.GetPoint(i) + +# Check if cells are same +after_cells_array = itk.array_from_vector_container(mesh_output.GetCellsArray()) +assert np.array_equal(before_cells_array, after_cells_array) diff --git a/Modules/Filtering/MeshToPolyData/wrapping/test/itkPyVectorContainerTest.py b/Modules/Filtering/MeshToPolyData/wrapping/test/itkPyVectorContainerTest.py new file mode 100644 index 00000000000..fe91a8e647a --- /dev/null +++ b/Modules/Filtering/MeshToPolyData/wrapping/test/itkPyVectorContainerTest.py @@ -0,0 +1,98 @@ +# ========================================================================== +# +# Copyright NumFOCUS +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ==========================================================================*/ +import sys +import unittest +import datetime as dt + +import itk +import numpy as np + + +class TestNumpyVectorContainerMemoryviewInterface(unittest.TestCase): + """This tests numpy array <-> ITK VectorContainer conversion.""" + + def setUp(self): + pass + + def test_NumPyBridge_VectorContainer(self): + "Try to convert a itk.VectorContainer into a Numpy array and back." + v1 = itk.VectorContainer[itk.UL, itk.F].New() + v1.Reserve(4) + v1.SetElement(0, 1.2) + v1.SetElement(1, 2) + v1.SetElement(2, 4) + v1.SetElement(3, 5) + arr = itk.PyVectorContainer[itk.F].array_view_from_vector_container(v1) + v2 = itk.PyVectorContainer[itk.F].vector_container_from_array(arr) + self.assertEqual(v1.Size(), arr.shape[0]) + self.assertEqual(v1.Size(), v2.Size()) + # Compute difference between the original vector and numpy array view + diff = 0.0 + for ii in range(0, v1.Size()): + diff += abs(v1.GetElement(ii) - arr[ii]) + self.assertEqual(0, diff) + # Compute difference between the two vectors + diff = 0.0 + for ii in range(0, v1.Size()): + diff += abs(v1.GetElement(ii) - v2.GetElement(ii)) + self.assertEqual(0, diff) + # Test view + v1.SetElement(0, 1) + self.assertEqual(v1.GetElement(0), arr[0]) + # Test deep copy + arr_cp = itk.PyVectorContainer[itk.F].array_from_vector_container(v1) + self.assertEqual(v1.GetElement(0), arr_cp[0]) + v1.SetElement(0, 0) + self.assertNotEqual(v1.GetElement(0), arr_cp[0]) + v2_cp = itk.PyVectorContainer[itk.F].vector_container_from_array(arr_cp) + arr_cp[0] = 2 + self.assertNotEqual(v2_cp.GetElement(0), arr_cp[0]) + + PointType = itk.Point[itk.F, 3] + v_point = itk.VectorContainer[itk.UL, PointType].New() + v_point.Reserve(2) + point = PointType() + point[0] = 1.0 + point[1] = 2.0 + point[2] = 4.0 + v_point.SetElement(0, point) + point[0] = 6.0 + point[1] = 8.0 + point[2] = 9.0 + v_point.SetElement(1, point) + arr = itk.PyVectorContainer[PointType].array_view_from_vector_container(v_point) + self.assertTrue( + np.array_equal(arr, np.array([[1.0, 2.0, 4.0], [6.0, 8.0, 9.0]])) + ) + + PointType = itk.Point[itk.F, 2] + v_point = itk.VectorContainer[itk.UL, PointType].New() + v_point.Reserve(2) + point = PointType() + point[0] = 1.0 + point[1] = 2.0 + v_point.SetElement(0, point) + point[0] = 6.0 + point[1] = 8.0 + v_point.SetElement(1, point) + arr = itk.PyVectorContainer[PointType].array_view_from_vector_container(v_point) + self.assertTrue(np.array_equal(arr, np.array([[1.0, 2.0], [6.0, 8.0]]))) + + +if __name__ == "__main__": + unittest.main() diff --git a/Modules/Remote/MeshToPolyData.remote.cmake b/Modules/Remote/MeshToPolyData.remote.cmake deleted file mode 100644 index a7a669e89d7..00000000000 --- a/Modules/Remote/MeshToPolyData.remote.cmake +++ /dev/null @@ -1,51 +0,0 @@ -#-- # Grading Level Criteria Report -#-- EVALUATION DATE: 2022-03-08 -#-- EVALUATORS: [Matt McCormick -#-- -#-- ## Compliance level 5 star (AKA ITK main modules, or remote modules that could become core modules) -#-- - [ ] Widespread community dependance -#-- - [X] Above 90% code coverage -#-- - [X] CI dashboards and testing monitored rigorously -#-- - [X] Key API features are exposed in wrapping interface -#-- - [ ] All requirements of Levels 4,3,2,1 -#-- -#-- ## Compliance Level 4 star (Very high-quality code, perhaps small community dependance) -#-- - [X] Meets all ITK code style standards -#-- - [X] No external requirements beyond those needed by ITK proper -#-- - [X] Builds and passes tests on all supported platforms within 1 month of each core tagged release -#-- - [X] Windows Shared Library Build with Visual Studio -#-- - [X] Mac with clang compiller -#-- - [X] Linux with gcc compiler -#-- - [x] Active developer community dedicated to maintaining code-base -#-- - [X] 75% code coverage demonstrated for testing suite -#-- - [X] Continuous integration testing performed -#-- - [X] All requirements of Levels 3,2,1 -#-- -#-- ## Compliance Level 3 star (Quality beta code) -#-- - [X] API | executable interface is considered mostly stable and feature complete -#-- - [X] 10% C0-code coverage demonstrated for testing suite -#-- - [X] Some tests exist and pass on at least some platform -#-- - [X] All requirements of Levels 2,1 -#-- -#-- ## Compliance Level 2 star (Alpha code feature API development or niche community/execution environment dependance ) -#-- - [X] Compiles for at least 1 niche set of execution envirionments, and perhaps others -#-- (may depend on specific external tools like a java environment, or specific external libraries to work ) -#-- - [X] All requirements of Levels 1 -#-- -#-- ## Compliance Level 1 star (Pre-alpha features under development and code of unknown quality) -#-- - [X] Code complies on at least 1 platform -#-- -#-- ## Compliance Level 0 star ( Code/Feature of known poor-quality or deprecated status ) -#-- - [ ] Code reviewed and explicitly identified as not recommended for use -#-- -#-- ### Please document here any justification for the criteria above -# Code style enforced by clang-format on 2020-02-19, and clang-tidy modernizations completed - -# Contact: Matt McCormick -itk_fetch_module( - MeshToPolyData - "Convert an ITK Mesh to a data structure compatible with vtkPolyData." - MODULE_COMPLIANCE_LEVEL 3 - GIT_REPOSITORY https://github.com/InsightSoftwareConsortium/ITKMeshToPolyData.git - GIT_TAG 43338b42866c7e155d46c32d0658383d10ab3082 - ) diff --git a/pyproject.toml b/pyproject.toml index 21b8d59109b..4313f3db86a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ cmd = '''cmake \ -DModule_LabelErodeDilate:BOOL=ON \ -DModule_MGHIO:BOOL=ON \ -DModule_MeshNoise:BOOL=ON \ + -DModule_MeshToPolyData:BOOL=ON \ -DModule_MinimalPathExtraction:BOOL=ON \ -DModule_Montage:BOOL=ON \ -DModule_MorphologicalContourInterpolation:BOOL=ON \