diff --git a/vespa/PolygonMeshProcessing/CMakeLists.txt b/vespa/PolygonMeshProcessing/CMakeLists.txt index b25c02ec98ca90d33f9f47679fa4b033f6dda5df..3af4d1f08707c57b7ecd8d3f2198c90f01b2a4db 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 1666c1574b93323d6a60ce145382abf2f5bc0d17..c24f7e6f0a2f82134d0bfce76234624117c47537 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 7b3f8a60d1dc05a160b30563579e08be87a93a5e..b7ddfc7fde3c0342c4cc86c8989ee6e26e0fd1dc 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 0000000000000000000000000000000000000000..dc57029fb2092fd62873507b84b293759102a9c7 --- /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 c893aff5ac1b37e3ccd55b9e43c0df55ee07b2f6..3b27ae7fe1beb9de26953877348b6381f786ed06 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 7a48bf0e31682e8b2c6ec9b14544f2182900ebdc..28627ad142b0982d69a4f38180cfb70a004b0736 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 dd0b82ed2f3186b76a49f3120f1ed9d65f7cd440..ece4bf2dba4f3f1251488bd9eefe25ad9bba7b5d 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 7393c9f62a9d44b5ebdd799d93ae086ee323453a..522e45115209839a25abac5d4f95f252937ea306 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 f8f77236f990d78140598e0a97fa331cc2c9f66d..c1b61d6bef801ae407773c7b7f83d0dfb8f1e14a 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 6b469f28e0b94140c1732cfed2cb38cf22332f3d..ab8147b238a135386e5312e15510b0bf13107a30 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 0000000000000000000000000000000000000000..c0b88e6827a98ca74a10a674207a8da955679df2 --- /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 0000000000000000000000000000000000000000..2fcc35e1bc858de684e443c4ea4cb3b37221eaae --- /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