From 9262e975b3c23d759fa8b41ca496060937148577 Mon Sep 17 00:00:00 2001 From: Tiffany Chhim Date: Wed, 23 Mar 2022 15:33:51 +0100 Subject: [PATCH] Add shape smoothing filter Based on the CGAL method Polygon_mesh_processing::smooth_shape(). --- vespa/PolygonMeshProcessing/CMakeLists.txt | 1 + .../Testing/CMakeLists.txt | 1 + .../Testing/TestPMPIsotropicExecution.cxx | 2 +- .../Testing/TestPMPSmoothingExecution.cxx | 27 ++++++++ .../vtkCGALIsotropicRemesher.cxx | 4 +- .../vtkCGALIsotropicRemesher.h | 12 ++-- .../vtkCGALMeshDeformation.cxx | 4 +- .../vtkCGALMeshDeformation.h | 10 +-- .../vtkCGALMeshSubdivision.cxx | 10 +-- .../vtkCGALMeshSubdivision.h | 8 +-- .../vtkCGALShapeSmoothing.cxx | 63 +++++++++++++++++++ .../vtkCGALShapeSmoothing.h | 60 ++++++++++++++++++ 12 files changed, 177 insertions(+), 25 deletions(-) create mode 100644 vespa/PolygonMeshProcessing/Testing/TestPMPSmoothingExecution.cxx create mode 100644 vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.cxx create mode 100644 vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.h diff --git a/vespa/PolygonMeshProcessing/CMakeLists.txt b/vespa/PolygonMeshProcessing/CMakeLists.txt index b25c02e..3af4d1f 100644 --- a/vespa/PolygonMeshProcessing/CMakeLists.txt +++ b/vespa/PolygonMeshProcessing/CMakeLists.txt @@ -7,6 +7,7 @@ set(vtkcgalpmp_files vtkCGALMeshSubdivision vtkCGALPatchFilling vtkCGALRegionFairing + vtkCGALShapeSmoothing ) vtk_module_add_module(vtkCGALPMP CLASSES ${vtkcgalpmp_files} diff --git a/vespa/PolygonMeshProcessing/Testing/CMakeLists.txt b/vespa/PolygonMeshProcessing/Testing/CMakeLists.txt index 1666c15..c24f7e6 100644 --- a/vespa/PolygonMeshProcessing/Testing/CMakeLists.txt +++ b/vespa/PolygonMeshProcessing/Testing/CMakeLists.txt @@ -40,6 +40,7 @@ vtk_add_test_cxx(vtkCGALCxxTests no_data_tests TestPMPFairExecution.cxx TestPMPFillExecution.cxx TestPMPIsotropicExecution.cxx + TestPMPSmoothingExecution.cxx TestPMPSubdivisionExecution.cxx ${PROJECT_SOURCE_DIR}/Data/Testing/ diff --git a/vespa/PolygonMeshProcessing/Testing/TestPMPIsotropicExecution.cxx b/vespa/PolygonMeshProcessing/Testing/TestPMPIsotropicExecution.cxx index 7b3f8a6..b7ddfc7 100644 --- a/vespa/PolygonMeshProcessing/Testing/TestPMPIsotropicExecution.cxx +++ b/vespa/PolygonMeshProcessing/Testing/TestPMPIsotropicExecution.cxx @@ -20,7 +20,7 @@ int TestPMPIsotropicExecution(int, char* argv[]) vtkNew rm; rm->SetInputConnection(reader->GetOutputPort()); - rm->SetIterations(3); + rm->SetNumberOfIterations(3); // Save result diff --git a/vespa/PolygonMeshProcessing/Testing/TestPMPSmoothingExecution.cxx b/vespa/PolygonMeshProcessing/Testing/TestPMPSmoothingExecution.cxx new file mode 100644 index 0000000..dc57029 --- /dev/null +++ b/vespa/PolygonMeshProcessing/Testing/TestPMPSmoothingExecution.cxx @@ -0,0 +1,27 @@ +#include + +#include "vtkNew.h" +#include "vtkTestUtilities.h" +#include "vtkXMLPolyDataReader.h" +#include "vtkXMLPolyDataWriter.h" + +#include "vtkCGALShapeSmoothing.h" + +int TestPMPSmoothingExecution(int, char* argv[]) +{ + vtkNew reader; + std::string cfname(argv[1]); + cfname += "/hand.vtp"; + reader->SetFileName(cfname.c_str()); + + vtkNew smoother; + smoother->SetInputConnection(reader->GetOutputPort()); + smoother->SetNumberOfIterations(5); + + vtkNew writer; + writer->SetInputConnection(smoother->GetOutputPort()); + writer->SetFileName("smooth_hand.vtp"); + writer->Write(); + + return 0; +} diff --git a/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.cxx b/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.cxx index c893aff..3b27ae7 100644 --- a/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.cxx +++ b/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.cxx @@ -17,7 +17,7 @@ namespace pmp = CGAL::Polygon_mesh_processing; void vtkCGALIsotropicRemesher::PrintSelf(ostream& os, vtkIndent indent) { os << indent << "TargetLength :" << this->TargetLength << std::endl; - os << indent << "Iterations :" << this->Iterations << std::endl; + os << indent << "Number of Iterations :" << this->NumberOfIterations << std::endl; this->Superclass::PrintSelf(os, indent); } @@ -60,7 +60,7 @@ int vtkCGALIsotropicRemesher::RequestData( // remesh pmp::isotropic_remeshing(cgalMesh->surface.faces(), targetLength, cgalMesh->surface, - pmp::parameters::number_of_iterations(this->Iterations) + pmp::parameters::number_of_iterations(this->NumberOfIterations) .protect_constraints(true) .edge_is_constrained_map(featureEdges)); } diff --git a/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.h b/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.h index 7a48bf0..28627ad 100644 --- a/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.h +++ b/vespa/PolygonMeshProcessing/vtkCGALIsotropicRemesher.h @@ -43,12 +43,12 @@ public: //@{ /** - * Get / Set the number of iteration for the + * Get / Set the number of iterations for the * CGAL isotropic_remeshing. * Default is 1 **/ - vtkGetMacro(Iterations, int); - vtkSetMacro(Iterations, int); + vtkGetMacro(NumberOfIterations, int); + vtkSetMacro(NumberOfIterations, int); //@} protected: @@ -59,9 +59,9 @@ protected: // Fields - double TargetLength = -1; - double ProtectAngle = 45; - int Iterations = 1; + double TargetLength = -1; + double ProtectAngle = 45; + int NumberOfIterations = 1; private: vtkCGALIsotropicRemesher(const vtkCGALIsotropicRemesher&) = delete; diff --git a/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.cxx b/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.cxx index dd0b82e..ece4bf2 100644 --- a/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.cxx +++ b/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.cxx @@ -25,7 +25,7 @@ vtkCGALMeshDeformation::vtkCGALMeshDeformation() //------------------------------------------------------------------------------ void vtkCGALMeshDeformation::PrintSelf(ostream& os, vtkIndent indent) { - os << indent << "Iterations :" << this->Iterations << std::endl; + os << indent << "Number of Iterations :" << this->NumberOfIterations << std::endl; os << indent << "Tolerance :" << this->Tolerance << std::endl; this->Superclass::PrintSelf(os, indent); } @@ -183,7 +183,7 @@ int vtkCGALMeshDeformation::RequestData( try { // Deform mesh given ROI and control point targets - deformer.deform(this->Iterations, this->Tolerance); + deformer.deform(this->NumberOfIterations, this->Tolerance); } catch (std::exception& e) { diff --git a/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.h b/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.h index 7393c9f..522e451 100644 --- a/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.h +++ b/vespa/PolygonMeshProcessing/vtkCGALMeshDeformation.h @@ -35,8 +35,8 @@ public: * Get/set the number of iterations used in the deformation process. * Default is 5. **/ - vtkGetMacro(Iterations, double); - vtkSetMacro(Iterations, double); + vtkGetMacro(NumberOfIterations, unsigned int); + vtkSetMacro(NumberOfIterations, unsigned int); ///@} ///@{ @@ -64,9 +64,9 @@ protected: int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override; int FillInputPortInformation(int port, vtkInformation* info) override; - unsigned int Iterations = 5; - double Tolerance = 1e-4; - std::string GlobalIdArray = ""; + unsigned int NumberOfIterations = 5; + double Tolerance = 1e-4; + std::string GlobalIdArray = ""; private: vtkCGALMeshDeformation(const vtkCGALMeshDeformation&) = delete; diff --git a/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.cxx b/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.cxx index f8f7723..c1b61d6 100644 --- a/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.cxx +++ b/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.cxx @@ -39,7 +39,7 @@ void vtkCGALMeshSubdivision::PrintSelf(ostream& os, vtkIndent indent) break; } - os << indent << "Iterations :" << this->Iterations << std::endl; + os << indent << "Number of Iterations :" << this->NumberOfIterations << std::endl; this->Superclass::PrintSelf(os, indent); } @@ -71,19 +71,19 @@ int vtkCGALMeshSubdivision::RequestData( { case vtkCGALMeshSubdivision::CATMULL_CLARK: CGAL::Subdivision_method_3::CatmullClark_subdivision( - cgalMesh->surface, CGAL::parameters::number_of_iterations(this->Iterations)); + cgalMesh->surface, CGAL::parameters::number_of_iterations(this->NumberOfIterations)); break; case vtkCGALMeshSubdivision::LOOP: CGAL::Subdivision_method_3::Loop_subdivision( - cgalMesh->surface, CGAL::parameters::number_of_iterations(this->Iterations)); + cgalMesh->surface, CGAL::parameters::number_of_iterations(this->NumberOfIterations)); break; case vtkCGALMeshSubdivision::DOO_SABIN: CGAL::Subdivision_method_3::DooSabin_subdivision( - cgalMesh->surface, CGAL::parameters::number_of_iterations(this->Iterations)); + cgalMesh->surface, CGAL::parameters::number_of_iterations(this->NumberOfIterations)); break; case vtkCGALMeshSubdivision::SQRT3: CGAL::Subdivision_method_3::Sqrt3_subdivision( - cgalMesh->surface, CGAL::parameters::number_of_iterations(this->Iterations)); + cgalMesh->surface, CGAL::parameters::number_of_iterations(this->NumberOfIterations)); break; default: vtkErrorMacro("Unknown subdivision method!"); diff --git a/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.h b/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.h index 6b469f2..ab8147b 100644 --- a/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.h +++ b/vespa/PolygonMeshProcessing/vtkCGALMeshSubdivision.h @@ -51,8 +51,8 @@ public: * Get/set the number of iterations (subdivisions) used in the subdivision process. * Default is 1. **/ - vtkGetMacro(Iterations, double); - vtkSetMacro(Iterations, double); + vtkGetMacro(NumberOfIterations, double); + vtkSetMacro(NumberOfIterations, double); ///@} protected: @@ -61,8 +61,8 @@ protected: int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override; - int SubdivisionType = vtkCGALMeshSubdivision::SQRT3; - unsigned int Iterations = 1; + int SubdivisionType = vtkCGALMeshSubdivision::SQRT3; + unsigned int NumberOfIterations = 1; private: vtkCGALMeshSubdivision(const vtkCGALMeshSubdivision&) = delete; diff --git a/vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.cxx b/vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.cxx new file mode 100644 index 0000000..c0b88e6 --- /dev/null +++ b/vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.cxx @@ -0,0 +1,63 @@ +#include "vtkCGALShapeSmoothing.h" + +// VTK related includes +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkObjectFactory.h" + +// CGAL related includes +#include + +vtkStandardNewMacro(vtkCGALShapeSmoothing); + +namespace pmp = CGAL::Polygon_mesh_processing; + +//------------------------------------------------------------------------------ +void vtkCGALShapeSmoothing::PrintSelf(ostream& os, vtkIndent indent) +{ + os << indent << "Number of Iterations :" << this->NumberOfIterations << std::endl; + os << indent << "Time Step :" << this->TimeStep << std::endl; + this->Superclass::PrintSelf(os, indent); +} + +//------------------------------------------------------------------------------ +int vtkCGALShapeSmoothing::RequestData( + vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector) +{ + // Get the input and output data objects + vtkPolyData* input = vtkPolyData::GetData(inputVector[0]); + vtkPolyData* output = vtkPolyData::GetData(outputVector); + + if (!input || !output) + { + vtkErrorMacro("Missing input or output."); + } + + // Create the surface mesh for CGAL + // ---------------------------------- + + std::unique_ptr cgalInputMesh = this->toCGAL(input); + + // CGAL Processing + // --------------- + + try + { + pmp::smooth_shape(cgalInputMesh->surface, this->TimeStep, + pmp::parameters::number_of_iterations(this->NumberOfIterations)); + } + catch (std::exception& e) + { + vtkErrorMacro("CGAL Exception: " << e.what()); + return 0; + } + + // VTK Output + // ---------- + + output->ShallowCopy(this->toVTK(cgalInputMesh.get())); + + this->copyAttributes(input, output); + + return 1; +} diff --git a/vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.h b/vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.h new file mode 100644 index 0000000..2fcc35e --- /dev/null +++ b/vespa/PolygonMeshProcessing/vtkCGALShapeSmoothing.h @@ -0,0 +1,60 @@ +/** + * @class vtkCGALShapeSmoothing + * @brief Smoothes a surface mesh by moving its vertices. + * + * vtkCGALShapeSmoothing is a filter that smoothes the overall shape of a 3D mesh + * using the mean curvature. + * The degree of smoothing can be controlled using the number of iterations as well as + * the time step. The time step specifies the smoothing speed. + * A higher time step results in a stronger shape distortion than a higher number of iterations. + */ + +#ifndef vtkCGALShapeSmoothing_h +#define vtkCGALShapeSmoothing_h + +#include "vtkCGALPolyDataAlgorithm.h" + +#include "vtkCGALPMPModule.h" // For export macro + +class VTKCGALPMP_EXPORT vtkCGALShapeSmoothing : public vtkCGALPolyDataAlgorithm +{ +public: + static vtkCGALShapeSmoothing* New(); + vtkTypeMacro(vtkCGALShapeSmoothing, vtkCGALPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent) override; + + ///@{ + /** + * Get/set the number of iterations used in the smoothing process. + * Default is 1. + **/ + vtkGetMacro(NumberOfIterations, unsigned int); + vtkSetMacro(NumberOfIterations, unsigned int); + ///@} + + ///@{ + /** + * Get/set the time step indicating the smoothing speed. + * A higher value leads to a faster distortion of the shape. + * Standard values lie in the range 1e-6 to 1. + * Default is 1e-4. + **/ + vtkGetMacro(TimeStep, double); + vtkSetMacro(TimeStep, double); + ///@} + +protected: + vtkCGALShapeSmoothing() = default; + ~vtkCGALShapeSmoothing() override = default; + + int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override; + + unsigned int NumberOfIterations = 1; + double TimeStep = 1e-4; + +private: + vtkCGALShapeSmoothing(const vtkCGALShapeSmoothing&) = delete; + void operator=(const vtkCGALShapeSmoothing&) = delete; +}; + +#endif -- GitLab