diff --git a/Documentation/doc/biblio/geom.bib b/Documentation/doc/biblio/geom.bib index 8b08afe0107..7cfe999a4c6 100644 --- a/Documentation/doc/biblio/geom.bib +++ b/Documentation/doc/biblio/geom.bib @@ -152118,3 +152118,15 @@ keywords = {polygonal surface mesh, Surface reconstruction, kinetic framework, s pages={11}, year={2015} } + +@article{liu2025linequadrics, + journal = {Computer Graphics Forum}, + title = {{Controlling Quadric Error Simplification with Line Quadrics}}, + author = {Liu, Hsueh-Ti Derek and Rahimzadeh, Mehdi and Zordan, Victor}, + year = {2025}, + publisher = {The Eurographics Association and John Wiley & Sons Ltd.}, + ISSN = {1467-8659}, + DOI = {10.1111/cgf.70184}, + url = {https://doi.org/10.1111/cgf.70184}, + year={2025} +} diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 827bfc8a762..37950b4a3f9 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -12,6 +12,15 @@ Release date: July 2026 - `import_from_triangulation_3()` → `triangulation_3_to_lcc()` - The old function names are still available but marked as deprecated for backward compatibility. + +### [Surface Mesh Simplification](https://doc.cgal.org/6.2/Manual/packages.html#PkgSurfaceMeshSimplification) + +- Added the class `CGAL::Surface_mesh_simplification::GarlandHeckbert_plane_and_line_policies`, which provides improved output for `CGAL::Surface_mesh_simplification::edge_collapse()`. + That class works the same as previous `GarlandHeckbert_policies`. + Its constructor accepts a `Mesh` and optional named parameters to set the weight of the line policy relative to the plane policy, set the boundary cost multiplier or provide vertex normals. +- **Breaking change**: `CGAL::Surface_mesh_simplification::GarlandHeckbert_policies.h` is now an alias of `CGAL::Surface_mesh_simplification::GarlandHeckbert_plane_and_line_policies.h` and is no longer deprecated. + + ## [Release 6.1](https://github.com/CGAL/cgal/releases/tag/v6.1) Release date: Sept 2025 diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h index ffe386d086c..73c54858bf2 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -185,6 +185,8 @@ CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost) CGAL_add_named_parameter(get_placement_policy_t, get_placement_policy, get_placement) CGAL_add_named_parameter(filter_t, filter, filter) CGAL_add_named_parameter(use_relaxed_order_t, use_relaxed_order, use_relaxed_order) +CGAL_add_named_parameter(line_policies_weight_t, line_policies_weight, line_policies_weight) +CGAL_add_named_parameter(discontinuity_multiplier_t, discontinuity_multiplier, discontinuity_multiplier) //to be documented CGAL_add_named_parameter(face_normal_t, face_normal, face_normal_map) diff --git a/Surface_mesh_simplification/benchmark/Surface_mesh_simplification/CMakeLists.txt b/Surface_mesh_simplification/benchmark/Surface_mesh_simplification/CMakeLists.txt new file mode 100644 index 00000000000..cf960bece45 --- /dev/null +++ b/Surface_mesh_simplification/benchmark/Surface_mesh_simplification/CMakeLists.txt @@ -0,0 +1,17 @@ +# Created by the script cgal_create_cmake_script_with_options +# This is the CMake script for compiling a set of CGAL applications. + +cmake_minimum_required(VERSION 3.12...3.31) +project(Surface_mesh_simplification_Examples) + +# CGAL and its components +find_package(CGAL REQUIRED) + +find_package(Eigen3 3.1.0 QUIET) #(3.1.0 or greater) +include(CGAL_Eigen3_support) +if(TARGET CGAL::Eigen3_support) + create_single_source_cgal_program("bench_garland_heckbert.cpp") + target_link_libraries(bench_garland_heckbert PRIVATE CGAL::Eigen3_support) +else() + message(STATUS "NOTICE: Garland-Heckbert polices require the Eigen library, which has not been found; related examples will not be compiled.") +endif() diff --git a/Surface_mesh_simplification/benchmark/Surface_mesh_simplification/bench_garland_heckbert.cpp b/Surface_mesh_simplification/benchmark/Surface_mesh_simplification/bench_garland_heckbert.cpp new file mode 100644 index 00000000000..08b2cf3aeb2 --- /dev/null +++ b/Surface_mesh_simplification/benchmark/Surface_mesh_simplification/bench_garland_heckbert.cpp @@ -0,0 +1,156 @@ +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#define TAG CGAL::Parallel_if_available_tag + +typedef CGAL::Simple_cartesian Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point_3; +typedef CGAL::Surface_mesh Surface_mesh; + +namespace SMS = CGAL::Surface_mesh_simplification; +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef SMS::GarlandHeckbert_plane_policies Classic_plane; +typedef SMS::GarlandHeckbert_probabilistic_plane_policies Prob_plane; +typedef SMS::GarlandHeckbert_triangle_policies Classic_tri; +typedef SMS::GarlandHeckbert_probabilistic_triangle_policies Prob_tri; +typedef SMS::GarlandHeckbert_policies Classic_plane_and_line; + +// Old policies composed with line policies +typedef SMS::internal::GarlandHeckbert_line_policies Line_quadric; +typedef SMS::internal::GarlandHeckbert_composed_policies Proba_plane_and_line; +typedef SMS::internal::GarlandHeckbert_composed_policies Classic_tri_plus_line; +typedef SMS::internal::GarlandHeckbert_composed_policies Proba_tri_plus_line; + +double mean_aspect_ratio(Surface_mesh& mesh) +{ + double total_aspect_ratio = 0; + double total_area = 0; + for(auto f: faces(mesh)){ + // double a = PMP::face_area(f, mesh); + double a = 1.; + total_aspect_ratio += a*CGAL::to_double(PMP::face_aspect_ratio(f, mesh)); + total_area += a; + } + return total_aspect_ratio/total_area; +} + +template +double collapse_gh(Surface_mesh& mesh, + const double ratio, + GHPolicies gh_policies) +{ + std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); + + SMS::Edge_count_ratio_stop_predicate stop(ratio); + + // Garland&Heckbert simplification policies + + typedef typename GHPolicies::Get_cost GH_cost; + typedef typename GHPolicies::Get_placement GH_placement; + typedef SMS::Bounded_normal_change_filter<> Filter; + + const GH_cost& gh_cost = gh_policies.get_cost(); + const GH_placement& gh_placement = gh_policies.get_placement(); + + SMS::edge_collapse(mesh, stop, CGAL::parameters::get_cost(gh_cost).filter(Filter()).get_placement(gh_placement)); + + std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); + + return (std::chrono::duration_cast(end_time - start_time).count()) / 1000.; +} + +std::string getFileNameWithoutExtension(const std::string& filePath) +{ + std::filesystem::path path(filePath); + return path.stem().string(); // 'stem' gets the filename without extension +} + +int main(int argc, char** argv) +{ + Surface_mesh mesh; + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cube-meshed.off"); + PMP::duplicate_non_manifold_vertices(mesh); + PMP::autorefine(mesh); + + if(!CGAL::IO::read_polygon_mesh(filename, mesh)) + { + std::cerr << "Failed to read input mesh: " << filename << std::endl; + return EXIT_FAILURE; + } + + if(!CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Input geometry is not triangulated." << std::endl; + return EXIT_FAILURE; + } + + const double ratio = (argc > 2) ? std::stod(argv[2]) : 0.2; + std::cout << ratio << std::endl; + std::cout << "\n\nPolicy Approx_Hausdorff Dist_points_to_input mean_aspect_ratio running_time" << std::endl; + auto collapse=[&](std::string policy){ + using CGAL::Surface_mesh_simplification::make_GarlandHeckbert_plane_and_line_policies; + Surface_mesh tmesh(mesh); + double time; + if(policy == "Classic_plane") + time=collapse_gh(tmesh, ratio, Classic_plane(tmesh)); + else if(policy == "Classic_triangle") + time=collapse_gh(tmesh, ratio, Classic_tri(tmesh)); + else if(policy == "Prob_plane") + time=collapse_gh(tmesh, ratio, Prob_plane(tmesh)); + else if(policy == "Prob_triangle") + time=collapse_gh(tmesh, ratio, Prob_tri(tmesh)); + // else if(policy == "Plane_plus_line_0.1") + // time=collapse_gh(tmesh, ratio, make_GarlandHeckbert_plane_and_line_policies(tmesh, CGAL::parameters::line_policies_weight(0.1))); + else if(policy == "Plane_plus_line_0.01") + time=collapse_gh(tmesh, ratio, make_GarlandHeckbert_plane_and_line_policies(tmesh, CGAL::parameters::line_policies_weight(0.01))); + else if(policy == "Plane_plus_line_0.001") + time=collapse_gh(tmesh, ratio, make_GarlandHeckbert_plane_and_line_policies(tmesh, CGAL::parameters::line_policies_weight(0.001))); + // else if(policy == "Classic_tri_line") + // time=collapse_gh(tmesh, ratio, Classic_tri_plus_line(tmesh, 100)); + // else if(policy == "Proba_plane_line") + // time=collapse_gh(tmesh, ratio, Proba_plane_and_line(tmesh, Prob_plane(tmesh), Line_quadric(tmesh), 100)); + // else if(policy == "Proba_tri_line") + // time=collapse_gh(tmesh, ratio, Proba_tri_plus_line(tmesh, Prob_tri(tmesh), Line_quadric(tmesh), 100)); + + std::cout << policy << " " << PMP::approximate_symmetric_Hausdorff_distance(tmesh, mesh) + << " " << PMP::max_distance_to_triangle_mesh(tmesh.points(),mesh) + << " " << mean_aspect_ratio(tmesh) + << " " << time << std::endl; + CGAL::IO::write_polygon_mesh("out_"+policy+"_"+std::to_string(ratio)+".off", tmesh, CGAL::parameters::stream_precision(17)); + }; + + collapse("Classic_plane"); + collapse("Classic_triangle"); + collapse("Prob_plane"); + collapse("Prob_triangle"); + // collapse("Plane_plus_line_0.1"); + collapse("Plane_plus_line_0.01"); + collapse("Plane_plus_line_0.001"); + // collapse("Classic_tri_line"); + // collapse("Proba_plane_line"); + // collapse("Proba_tri_line"); + // collapse("Plane_plus_line_0.001"); + + return EXIT_SUCCESS; +} diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_and_line_policies.h b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_and_line_policies.h new file mode 100644 index 00000000000..0325f5093ff --- /dev/null +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_and_line_policies.h @@ -0,0 +1,96 @@ +namespace CGAL { +namespace Surface_mesh_simplification { + +/*! +\ingroup PkgSurfaceMeshSimplificationRef + +The class `GarlandHeckbert_plane_and_line_policies` regroups the cost and placement policies +based on the Garland-Heckbert "Plane and line" strategy of Liu and colleagues \cgalCite{liu2025linequadrics}. + +This policy enhances the original Garland-Heckbert quadric error metrics +by adding to the cost the distance to the line passing through the input vertices and aligned with their normals. +Compared to the "classic" plane strategy, this strategy improves the speed and the quality of the result +(see Section \ref SurfaceMeshSimplificationGarlandHeckbertStrategy). + +\note Both the cost and the placement policies must be used together as they internally use +and share information associating quadrics to vertices. +Note however, that they may still be wrapped with behavior-modifying classes +such as `Constrained_placement` or `Bounded_normal_change_placement`. + +\tparam TriangleMesh is the type of surface mesh being simplified, and must be a model + of the `MutableFaceGraph` and `HalfedgeListGraph` concepts. +\tparam GeomTraits must be a model of `Kernel`. If you have passed a traits class in the optional + named parameters in the call to `edge_collapse()`, the types must be identical. + +These policies depend on the third party \ref thirdpartyEigen library. + +\sa `GarlandHeckbert_policies` +\sa `GarlandHeckbert_plane_policies` +\sa `GarlandHeckbert_probabilistic_plane_policies` +\sa `GarlandHeckbert_triangle_policies` +\sa `GarlandHeckbert_probabilistic_triangle_policies` +*/ +template +class GarlandHeckbert_plane_and_line_policies +{ +public: + /// The type of the Garland-Heckbert cost functor, a model of the concept `GetCost` + typedef unspecified_type Get_cost; + + /// The type of the Garland-Heckbert placement functor, a model of the concept `GetPlacement` + typedef unspecified_type Get_placement; + + /// \name Creation + /// @{ + + /*! + initializes the Garland-Heckbert plane and line policies. + + \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" + + \param tmesh the triangle mesh + \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{vertex_normal_map} + \cgalParamDescription{a property map associating to each vertex of `tmesh` a normal direction for that vertex} + \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` + as key type and `Vector_3` as value type} + \cgalParamDefault{an internal map filled using `CGAL::Polygon_mesh_processing::compute_vertex_normals`} + \cgalParamNEnd + \cgalParamNBegin{discontinuity_multiplier} + \cgalParamDescription{a multiplier of the error value for boundary edges to preserve the boundaries} + \cgalParamType{double} + \cgalParamDefault{100} + \cgalParamNEnd + \cgalParamNBegin{line_policies_weight} + \cgalParamDescription{a value that defines the weight of the line policies compared to the plane policies} + \cgalParamType{double} + \cgalParamDefault{0.01} + \cgalParamNEnd + \cgalNamedParamsEnd + */ + template + GarlandHeckbert_plane_and_line_policies(TriangleMesh& tmesh, const NamedParameters &np = parameters::default_values()); + + /// @} + + /// \name Accessors + /// @{ + + const Get_cost& get_cost() const; + const Get_placement& get_placement() const; + + /// @} + +}; + +/*! +creates a Garland-Heckbert plane and line policies object. +*/ +template +GarlandHeckbert_plane_and_line_policies make_GarlandHeckbert_plane_and_line_policies(TriangleMesh& tmesh, + const NamedParameters& np = parameters::default_values()); + +} // namespace Surface_mesh_simplification +} // namespace CGAL diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h index 033c589de71..271502c26ca 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h @@ -25,6 +25,7 @@ These policies depend on the third party \ref thirdpartyEigen library. \sa `GarlandHeckbert_probabilistic_plane_policies` \sa `GarlandHeckbert_triangle_policies` \sa `GarlandHeckbert_probabilistic_triangle_policies` +\sa `GarlandHeckbert_plane_and_line_policies` */ template class GarlandHeckbert_plane_policies diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h index 5a5bc1edd4d..f5c464d524f 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h @@ -4,55 +4,19 @@ namespace Surface_mesh_simplification { /*! \ingroup PkgSurfaceMeshSimplificationRef -\deprecated This class is deprecated since \cgal 5.5 and the introduction of variations -of Garland-Heckbert policies (Section \ref SurfaceMeshSimplificationGarlandHeckbertStrategy). -The class `GarlandHeckbert_plane_policies` is the modern equivalent to this class. +This class is an alias for the current state-of-the-art Garland-Heckbert policies +(see Section \ref SurfaceMeshSimplificationGarlandHeckbertStrategy). +It is currently an alias of `GarlandHeckbert_plane_and_line_policies`. -The class `GarlandHeckbert_policies` regroups the cost and placement policies -based on the Garland-Heckbert strategy (Section \ref SurfaceMeshSimplificationGarlandHeckbertStrategy), -both oracles must indeed be used together as they internally use and share information -associating quadrics to vertices. - -Note however, that they may still be wrapped with behavior modifying classes -such as `Constrained_placement` or `Bounded_normal_change_placement`. - -\tparam TriangleMesh is the type of surface mesh being simplified, and must be a model - of the `MutableFaceGraph` and `HalfedgeListGraph` concepts. -\tparam GeomTraits must be a model of `Kernel`. If you have passed a traits class in the optional - named parameters in the call to `edge_collapse()`, the types must be identical. - -These policies depend on the third party \ref thirdpartyEigen library. +\sa `GarlandHeckbert_plane_policies` +\sa `GarlandHeckbert_probabilistic_plane_policies` +\sa `GarlandHeckbert_triangle_policies` +\sa `GarlandHeckbert_probabilistic_triangle_policies` +\sa `GarlandHeckbert_plane_and_line_policies` */ -template -class GarlandHeckbert_policies -{ -public: - /// The type of the Garland Heckbert cost functor, a model of the concept `GetCost` - typedef unspecified_type Get_cost; - - /// The type of the Garland Heckbert placement functor, a model of the concept `GetPlacement` - typedef unspecified_type Get_placement; - - /// \name Creation - /// @{ - - /*! - Initializes the Garland-Heckbert policies. - */ - GarlandHeckbert_policies(TriangleMesh& tmesh); - - /// @} - - /// \name Accessors - /// @{ - - const Get_cost& get_cost() const; - const Get_placement& get_placement() const; - - /// @} - -}; +template +using GarlandHeckbert_policies = GarlandHeckbert_plane_and_line_policies; } // namespace Surface_mesh_simplification } // namespace CGAL diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h index bb214bab663..63601750de0 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h @@ -26,6 +26,7 @@ These policies depend on the third party \ref thirdpartyEigen library. \sa `GarlandHeckbert_plane_policies` \sa `GarlandHeckbert_triangle_policies` \sa `GarlandHeckbert_probabilistic_triangle_policies` +\sa `GarlandHeckbert_plane_and_line_policies` */ template class GarlandHeckbert_probabilistic_plane_policies diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h index 7e3692cb275..5d0870d0c52 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h @@ -26,6 +26,7 @@ These policies depend on the third party \ref thirdpartyEigen library. \sa `GarlandHeckbert_plane_policies` \sa `GarlandHeckbert_probabilistic_plane_policies` \sa `GarlandHeckbert_triangle_policies` +\sa `GarlandHeckbert_plane_and_line_policies` */ template class GarlandHeckbert_probabilistic_triangle_policies diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/Doxyfile.in b/Surface_mesh_simplification/doc/Surface_mesh_simplification/Doxyfile.in index 377bdfd7c1a..64e5fa4ddf3 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/Doxyfile.in +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/Doxyfile.in @@ -2,4 +2,5 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Triangulated Surface Mesh Simplification" -HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/sappho.jpg +HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/sappho.png \ + ${CGAL_PACKAGE_DOC_DIR}/fig/octocat.png diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/PackageDescription.txt b/Surface_mesh_simplification/doc/Surface_mesh_simplification/PackageDescription.txt index a597bb02a77..548234de826 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/PackageDescription.txt +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/PackageDescription.txt @@ -49,7 +49,8 @@ - `CGAL::Surface_mesh_simplification::GarlandHeckbert_probabilistic_plane_policies` - `CGAL::Surface_mesh_simplification::GarlandHeckbert_triangle_policies` - `CGAL::Surface_mesh_simplification::GarlandHeckbert_probabilistic_triangle_policies` -- `CGAL::Surface_mesh_simplification::GarlandHeckbert_policies` (deprecated) +- `CGAL::Surface_mesh_simplification::GarlandHeckbert_plane_and_line_policies` +- `CGAL::Surface_mesh_simplification::GarlandHeckbert_policies` \cgalCRPSection{Policy Enhancements} - `CGAL::Surface_mesh_simplification::Constrained_placement` diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/Surface_mesh_simplification.txt b/Surface_mesh_simplification/doc/Surface_mesh_simplification/Surface_mesh_simplification.txt index 8ef4268e07f..9991fcfcdc7 100644 --- a/Surface_mesh_simplification/doc/Surface_mesh_simplification/Surface_mesh_simplification.txt +++ b/Surface_mesh_simplification/doc/Surface_mesh_simplification/Surface_mesh_simplification.txt @@ -188,21 +188,38 @@ face normals). This variance naturally deteriorates the tightness of the result, hand it enables creating more uniform triangulations and the approach is more tolerant to noise, while still maintaining feature sensitivity. +Another extension of the Garland–Heckbert approach was introduced by Liu et al. \cgalCite{liu2025linequadrics}, +who enhance the classic plane-based quadric error metrics with line quadrics. +More precisely, line quadrics, which encode the distance to a line, are defined at all input vertices using the supporting lines of the normals. +These quadrics are then combined with the classic plane quadrics, but with a small weight (0.01 by default). +This results in more uniform triangulations, while preserving the tightness of the approximation. + \cgalFigureAnchor{SurfaceMeshSimplification_GH}
- +
\cgalFigureCaptionBegin{SurfaceMeshSimplification_GH} Sappho's Head model (leftmost, 34882 vertices). From left to right, simplified output (1745 vertices) and symmetric Hausdorff distance -for the four Garland-Heckbert variations: plane (0.217912), probabilistic -plane (0.256801), triangle (0.268872), and probabilistic triangle (0.490846). +for the five Garland-Heckbert variations: plane (0.217912), probabilistic +plane (0.256801), triangle (0.268872), probabilistic triangle (0.490846) and plane and line (0.173344). +\cgalFigureCaptionEnd + +\cgalFigureAnchor{SurfaceMeshSimplification_GH_octocat} +
+ +
+\cgalFigureCaptionBegin{SurfaceMeshSimplification_GH_octocat} +Octocat model (top-left, 18944 vertices). +From top-left to bottom-right, simplified output (1895 vertices) and symmetric Hausdorff distance +for the five Garland-Heckbert variations: plane (0.19631), probabilistic +plane (0.59446), triangle (0.377533), probabilistic triangle (0.891286) and plane and line (0.304001). \cgalFigureCaptionEnd \subsection Surface_mesh_simplificationCostStrategyPolicies Cost Strategy Policies The cost strategy used by the algorithm is selected by means of three policies: -`GetPlacement`, `GetCost`, and `Filter`. +`GetPlacement`, `GetCost`, and `PlacementFilter`. The `GetPlacement` policy is called to compute the new position for the remaining vertex after the halfedge-collapse. It returns @@ -400,8 +417,8 @@ steps of the simplification algorithm. Each Garland-Heckbert simplification strategy is implemented with a single class that regroups both the cost and the placement policies, which must be used together as they share vertex quadric data. -The classic strategy of using plane-based quadric error metrics is implemented with the class -`Surface_mesh_simplification::GarlandHeckbert_plane_policies`. +The state-of-the-art strategy of combining plane and line-based quadric error metrics is implemented with the class +`Surface_mesh_simplification::GarlandHeckbert_policies`. Although both policies must be used together, it is still possible to wrap either policy using behavior modifiers such as `Surface_mesh_simplification::Bounded_normal_change_placement`. @@ -420,5 +437,7 @@ The implementation of the Garland-Heckbert simplification policies is the result of Baskın Şenbaşlar (Google Summer of Code 2019), and Julian Komaromy (Google Summer of Code 2021) They both were mentored by Mael Rouxel-Labbé, who also contributed to the code and to the documentation. +Léo Valque added the `Surface_mesh_simplification::GarlandHeckbert_plane_and_line_policies` functionality in \cgal 6.2. + */ } /* namespace CGAL */ diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/octocat.png b/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/octocat.png new file mode 100644 index 00000000000..413b2eaa0e6 Binary files /dev/null and b/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/octocat.png differ diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho.png b/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho.png new file mode 100644 index 00000000000..f8ba8ef513b Binary files /dev/null and b/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho.png differ diff --git a/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho.jpg b/Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho_old.jpg similarity index 100% rename from Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho.jpg rename to Surface_mesh_simplification/doc/Surface_mesh_simplification/fig/sappho_old.jpg diff --git a/Surface_mesh_simplification/examples/Surface_mesh_simplification/edge_collapse_garland_heckbert.cpp b/Surface_mesh_simplification/examples/Surface_mesh_simplification/edge_collapse_garland_heckbert.cpp index 7d2f9500b5d..3e0b632d0f8 100644 --- a/Surface_mesh_simplification/examples/Surface_mesh_simplification/edge_collapse_garland_heckbert.cpp +++ b/Surface_mesh_simplification/examples/Surface_mesh_simplification/edge_collapse_garland_heckbert.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -24,6 +24,7 @@ typedef SMS::GarlandHeckbert_plane_policies typedef SMS::GarlandHeckbert_probabilistic_plane_policies Prob_plane; typedef SMS::GarlandHeckbert_triangle_policies Classic_tri; typedef SMS::GarlandHeckbert_probabilistic_triangle_policies Prob_tri; +typedef SMS::GarlandHeckbert_plane_and_line_policies Plane_and_line; template void collapse_gh(Surface_mesh& mesh, @@ -37,16 +38,17 @@ void collapse_gh(Surface_mesh& mesh, typedef typename GHPolicies::Get_cost GH_cost; typedef typename GHPolicies::Get_placement GH_placement; - typedef SMS::Bounded_normal_change_placement Bounded_GH_placement; + typedef SMS::Bounded_normal_change_filter<> Filter; GHPolicies gh_policies(mesh); const GH_cost& gh_cost = gh_policies.get_cost(); const GH_placement& gh_placement = gh_policies.get_placement(); - Bounded_GH_placement placement(gh_placement); + Filter filter; int r = SMS::edge_collapse(mesh, stop, CGAL::parameters::get_cost(gh_cost) - .get_placement(placement)); + .filter(filter) + .get_placement(gh_placement)); std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); @@ -59,7 +61,7 @@ void collapse_gh(Surface_mesh& mesh, // Usage: // ./command [input] [ratio] [policy] [output] -// policy can be "cp" (classic plane), "ct" (classic triangle), "pp" (probabilistic plane), "pt" (probabilistic triangle) +// policy can be "cp" (classic plane), "ct" (classic triangle), "pp" (probabilistic plane), "pt" (probabilistic triangle), "pl" (plane and line) int main(int argc, char** argv) { Surface_mesh mesh; @@ -91,8 +93,10 @@ int main(int argc, char** argv) collapse_gh(mesh, ratio); else if(policy == "pp") collapse_gh(mesh, ratio); - else + else if(policy == "pt") collapse_gh(mesh, ratio); + else + collapse_gh(mesh, ratio); CGAL::IO::write_polygon_mesh((argc > 4) ? argv[4] : "out.off", mesh, CGAL::parameters::stream_precision(17)); diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_and_line_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_and_line_policies.h new file mode 100644 index 00000000000..ae415434165 --- /dev/null +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_and_line_policies.h @@ -0,0 +1,126 @@ +// Copyright (c) 2025 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Leo Valque + +#ifndef CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PLANE_AND_LINE_POLICIES_H +#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PLANE_AND_LINE_POLICIES_H + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace CGAL { +namespace Surface_mesh_simplification { + +namespace internal { + +// Helpers to perform computation in the constructor GH_plane_and_line_policies before calling the constructor of Base +template< typename TriangleMesh = void, typename NamedParameters = void> +struct GH_helper{ + typedef typename GetGeomTraits::type GT; + typedef typename GT::Vector_3 Vector_3; + typedef typename GT::FT FT; + + typedef dynamic_vertex_property_t Vertex_normal_tag; + typedef typename boost::property_map::type Vertex_normal_dmap; + typedef typename internal_np::Lookup_named_param_def::type Vertex_normal_map; + + NamedParameters np; + GH_helper(const NamedParameters &np_):np(np_){ } + + Vertex_normal_map vnm(TriangleMesh& tmesh) const{ + using parameters::choose_parameter; + using parameters::is_default_parameter; + using parameters::get_parameter; + constexpr bool must_compute_vertex_normals = is_default_parameter::value; + + Vertex_normal_map vertex_normals = choose_parameter(get_parameter(np, internal_np::vertex_normal_map), + get(Vertex_normal_tag(), tmesh)); + if constexpr(must_compute_vertex_normals){ + Polygon_mesh_processing::compute_vertex_normals(tmesh, vertex_normals, np); + } + return vertex_normals; + } + + double lw() const{ + using parameters::choose_parameter; + using parameters::get_parameter; + return choose_parameter(get_parameter(np, internal_np::line_policies_weight), 0.01); + } + + double dm() const{ + using parameters::choose_parameter; + using parameters::get_parameter; + + return choose_parameter(get_parameter(np, internal_np::discontinuity_multiplier), 100); + } +}; + +} // namespace internal + +template +class GarlandHeckbert_plane_and_line_policies + : public internal::GarlandHeckbert_composed_policies, + internal::GarlandHeckbert_line_policies, + true> +{ + typedef GarlandHeckbert_plane_policies GH_plane_polices; + typedef internal::GarlandHeckbert_line_policies GH_line_polices; + typedef internal::GarlandHeckbert_composed_policies Base; + +public: + typedef typename Base::Quadric_calculator Quadric_calculator; + + typedef typename GeomTraits::FT FT; + +public: + template + GarlandHeckbert_plane_and_line_policies(TriangleMesh& tmesh, const NP& np = parameters::default_values()): + Base(tmesh, + GH_plane_polices(tmesh), + GH_line_polices(tmesh, internal::GH_helper(np).vnm(tmesh)), + FT(1.)/internal::GH_helper(np).lw(), + FT(1.), + internal::GH_helper(np).dm()) + { } + +public: + using Base::operator(); + using Base::get_cost; + using Base::get_placement; +}; + +template +GarlandHeckbert_plane_and_line_policies::type> + make_GarlandHeckbert_plane_and_line_policies(TriangleMesh& tmesh, + const NamedParameters& np = parameters::default_values()){ + typedef typename GetGeomTraits::type GT; + return GarlandHeckbert_plane_and_line_policies(tmesh, np); +} + +} // namespace Surface_mesh_simplification +} // namespace CGAL + +#endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_PLANE_AND_LINE_POLICIES_H \ No newline at end of file diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h index 8fbc2cba3fc..3f99ca2c751 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_plane_policies.h @@ -32,6 +32,15 @@ class Plane_quadric_calculator public: Plane_quadric_calculator() { } + template + Mat_4 construct_quadric_from_vertex(typename boost::graph_traits::vertex_descriptor /*v*/, + const TriangleMesh& /*tmesh*/, + const VertexPointMap /*point_map*/, + const GeomTraits& /*gt*/) const + { + return Mat_4::Zero(); + } + template Mat_4 construct_quadric_from_edge(typename boost::graph_traits::halfedge_descriptor he, const TriangleMesh& tmesh, diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h index c45bfe2a65f..1be69571481 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_policies.h @@ -21,12 +21,13 @@ #include #include #include +#include namespace CGAL { namespace Surface_mesh_simplification { template -using GarlandHeckbert_policies CGAL_DEPRECATED = GarlandHeckbert_plane_policies; +using GarlandHeckbert_policies = GarlandHeckbert_plane_and_line_policies; } // namespace Surface_mesh_simplification } // namespace CGAL diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h index e533b381ded..f7c439c845a 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_plane_policies.h @@ -74,6 +74,15 @@ public: } public: + template + Mat_4 construct_quadric_from_vertex(typename boost::graph_traits::vertex_descriptor /*v*/, + const TriangleMesh& /*tmesh*/, + const VertexPointMap /*point_map*/, + const GeomTraits& /*gt*/) const + { + return Mat_4::Zero(); + } + template Mat_4 construct_quadric_from_edge(const halfedge_descriptor he, const TriangleMesh& tmesh, diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h index d8ef45f13f0..8d13f92ae63 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_probabilistic_triangle_policies.h @@ -48,7 +48,7 @@ private: // this is only used when we fall back to probabilistic planes for the discontinuous edges, // the actual triangle quadric calculation only uses the normal variance - static constexpr FT position_variance_factor = 1; + static constexpr FT position_variance_factor = 0.1; Face_variance_map m_face_variance_map; @@ -74,6 +74,15 @@ public: } public: + template + Mat_4 construct_quadric_from_vertex(typename boost::graph_traits::vertex_descriptor /*v*/, + const TriangleMesh& /*tmesh*/, + const VertexPointMap /*point_map*/, + const GeomTraits& /*gt*/) const + { + return Mat_4::Zero(); + } + // we don't have a sensible way to construct a triangle quadric // from an edge, so we fall back to probabilistic plane quadrics here template diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_triangle_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_triangle_policies.h index 1e8b2eaee10..5318623cf5d 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_triangle_policies.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/GarlandHeckbert_triangle_policies.h @@ -32,6 +32,15 @@ class Triangle_quadric_calculator public: Triangle_quadric_calculator() { } + template + Mat_4 construct_quadric_from_vertex(typename boost::graph_traits::vertex_descriptor /*v*/, + const TriangleMesh& /*tmesh*/, + const VertexPointMap /*point_map*/, + const GeomTraits& /*gt*/) const + { + return Mat_4::Zero(); + } + template Mat_4 construct_quadric_from_edge(typename boost::graph_traits::halfedge_descriptor he, const TriangleMesh& tmesh, diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_composed_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_composed_policies.h new file mode 100644 index 00000000000..dba5e665f87 --- /dev/null +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_composed_policies.h @@ -0,0 +1,144 @@ +// Copyright (c) 2025 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Leo Valque + +#ifndef CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_COMPOSED_POLICIES_H +#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_COMPOSED_POLICIES_H + +#include + +#include +#include +#include + +namespace CGAL { +namespace Surface_mesh_simplification { +namespace internal { + +/* +This policy was created to combine the line policy with the existing ones. +The composition of some quadrics is always invertible, but this is not necessarily true for all composed quadrics. +`invertible` boolean parameter reflects this distinction. +*/ +template +class Composed_quadric_calculator +{ + typedef typename GarlandHeckbert_matrix_types::Mat_4 Mat_4; + typedef typename GarlandHeckbert_matrix_types::Col_4 Col_4; + typedef typename GarlandHeckbert_matrix_types::Row_4 Row_4; + + Quadric_calculator_1 quadric_calculator_1; + Quadric_calculator_2 quadric_calculator_2; + double weight_1; + double weight_2; + +public: + Composed_quadric_calculator(const Quadric_calculator_1& qc1, const Quadric_calculator_2& qc2, double w1 = 1., double w2 = 1.) + : quadric_calculator_1(qc1) + , quadric_calculator_2(qc2) + , weight_1(w1) + , weight_2(w2) {} + Composed_quadric_calculator(double w1 = 1., double w2 = 1.) + : weight_1(w1) + , weight_2(w2) {} + + template + Mat_4 construct_quadric_from_vertex(typename boost::graph_traits::vertex_descriptor v, + const TriangleMesh& tmesh, + const VertexPointMap point_map, + const GeomTraits& gt) const + { + return weight_1 * quadric_calculator_1.construct_quadric_from_vertex(v, tmesh, point_map, gt) + + weight_2 * quadric_calculator_2.construct_quadric_from_vertex(v, tmesh, point_map, gt); + } + + template + Mat_4 construct_quadric_from_edge(typename boost::graph_traits::halfedge_descriptor he, + const TriangleMesh& tmesh, + const VertexPointMap point_map, + const GeomTraits& gt) const + { + return weight_1 * quadric_calculator_1.construct_quadric_from_edge(he, tmesh, point_map, gt) + + weight_2 * quadric_calculator_2.construct_quadric_from_edge(he, tmesh, point_map, gt); + } + + template + Mat_4 construct_quadric_from_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tmesh, + const VertexPointMap point_map, + const GeomTraits& gt) const + { + return weight_1 * quadric_calculator_1.construct_quadric_from_face(f, tmesh, point_map, gt) + + weight_2 * quadric_calculator_2.construct_quadric_from_face(f, tmesh, point_map, gt); + } + + + Col_4 construct_optimal_point(const Mat_4& quadric, + const Col_4& p0, + const Col_4& p1) const + { + if constexpr(invertible) + return construct_optimal_point_invertible(quadric); + else + return construct_optimal_point_singular(quadric, p0, p1); + } +}; + +template +class GarlandHeckbert_composed_policies + : public internal::GarlandHeckbert_cost_and_placement< + internal::Composed_quadric_calculator, + TriangleMesh, GeomTraits> +{ +public: + typedef internal::Composed_quadric_calculator + Quadric_calculator; + +private: + typedef internal::GarlandHeckbert_cost_and_placement< + Quadric_calculator, TriangleMesh, GeomTraits> Base; + typedef GarlandHeckbert_composed_policies Self; + +public: + typedef Self Get_cost; + typedef Self Get_placement; + + typedef typename GeomTraits::FT FT; + +public: + GarlandHeckbert_composed_policies(TriangleMesh& tmesh, + double w1=1., double w2=1.,const double dm = 100) + : Base(tmesh, Quadric_calculator(w1, w2), FT(dm)) + { } + + GarlandHeckbert_composed_policies(TriangleMesh& tmesh, + GH_policies_1 ghp1, + GH_policies_2 ghp2, + double w1=1., double w2=1.,const double dm = 100) + : Base(tmesh, Quadric_calculator(ghp1.quadric_calculator(), ghp2.quadric_calculator(), w1, w2), FT(dm)) + { } + +public: + const Get_cost& get_cost() const { return *this; } + const Get_placement& get_placement() const { return *this; } + + using Base::operator(); +}; + +} // namespace internal +} // namespace Surface_mesh_simplification +} // namespace CGAL + +#endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_COMPOSED_POLICIES_H \ No newline at end of file diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h index 6d954dd8b65..444053c2893 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_functions.h @@ -17,8 +17,13 @@ #include #include +#include + #include +#include +#include + #include namespace CGAL { @@ -236,8 +241,8 @@ construct_optimal_point_singular(const typename GarlandHeckbert_matrix_types svd pseudo-inverse + Eigen::JacobiSVD svd_decomp(mat, Eigen::ComputeThinU | Eigen::ComputeThinV); + svd_decomp.setThreshold(1e-5); + + opt_pt(0) = mean.x(); + opt_pt(1) = mean.y(); + opt_pt(2) = mean.z(); + opt_pt(3) = 1.; + + opt_pt += svd_decomp.solve(Col_4(0,0,0,1) - mat * opt_pt); +#endif + return opt_pt; } } @@ -508,6 +533,45 @@ construct_prob_triangle_quadric_from_face(typename boost::graph_traits +typename GarlandHeckbert_matrix_types::Mat_4 +construct_line_quadric_from_normal(const typename GeomTraits::Vector_3& normal, + const typename GeomTraits::Point_3& point, + const GeomTraits& gt) +{ + typedef typename GeomTraits::Vector_3 Vector_3; + + auto cp = gt.construct_cross_product_vector_3_object(); + auto plane = gt.construct_plane_3_object(); + auto c_point = gt.construct_point_3_object(); + auto base= gt.construct_base_vector_3_object(); + + Vector_3 x = base(plane(c_point(0,0,0),normal), 1); + CGAL::Polygon_mesh_processing::internal::normalize(x, gt); + Vector_3 y = cp(x,normal); + + return construct_classic_plane_quadric_from_normal(x, point, gt)+construct_classic_plane_quadric_from_normal(y, point, gt); +} + +/* +template +typename GarlandHeckbert_matrix_types::Mat_4 +construct_line_quadric_from_vertex(const typename boost::graph_traits::vertex_descriptor v, + const TriangleMesh& mesh, + const VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename GeomTraits::Vector_3 Vector_3; + + const Vector_3 normal = Polygon_mesh_processing::compute_vertex_normal(v, mesh, parameters::geom_traits(gt).vertex_point_map(vpm)); + return construct_line_quadric_from_normal(normal, get(vpm, v), gt); +} +*/ + //////////////////////////////////////////////////////////////////////////////////////////////////// /// PROB VARIANCE //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_line_policies.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_line_policies.h new file mode 100644 index 00000000000..03eed8a4235 --- /dev/null +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_line_policies.h @@ -0,0 +1,132 @@ +// Copyright (c) 2025 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Leo Valque + +#ifndef CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_LINE_POLICIES_H +#define CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_LINE_POLICIES_H + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +namespace CGAL { +namespace Surface_mesh_simplification { +namespace internal { + +/* +This policy is not useful on its own; it is designed to be combined with another policy using a small weight. +Therefore, it is kept internal. +*/ +template +class Line_quadric_calculator +{ + typedef typename GarlandHeckbert_matrix_types::Mat_4 Mat_4; + typedef typename GarlandHeckbert_matrix_types::Col_4 Col_4; + typedef typename GarlandHeckbert_matrix_types::Row_4 Row_4; + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename GeomTraits::Vector_3 Vector_3; + +private: + std::function m_vertex_normal_map; + +public: + Line_quadric_calculator() = delete; + + template + Line_quadric_calculator(const VNM vnm) + : m_vertex_normal_map([vnm](vertex_descriptor v) -> Vector_3{ return get(vnm, v); }) + { } + + template + Mat_4 construct_quadric_from_vertex(typename boost::graph_traits::vertex_descriptor v, + const TriangleMesh& /*tmesh*/, + const VertexPointMap point_map, + const GeomTraits& gt) const + { + return construct_line_quadric_from_normal(m_vertex_normal_map(v), get(point_map, v), gt); + } + + template + Mat_4 construct_quadric_from_edge(typename boost::graph_traits::halfedge_descriptor /*he*/, + const TriangleMesh& /*tmesh*/, + const VertexPointMap /*point_map*/, + const GeomTraits& /*gt*/) const + { + return Mat_4::Zero(); + } + + template + Mat_4 construct_quadric_from_face(typename boost::graph_traits::face_descriptor /*f*/, + const TriangleMesh& /*tmesh*/, + const VertexPointMap /*point_map*/, + const GeomTraits& /*gt*/) const + { + return Mat_4::Zero(); + } + + /* + // Since these policies are never used alone but always composed with another one, this code is not used + Col_4 construct_optimal_point(const Mat_4& quadric, + const Col_4& p0, + const Col_4& p1) const + { + return construct_optimal_point_singular(quadric, p0, p1); + } + */ +}; + +template >::type > +class GarlandHeckbert_line_policies + : public internal::GarlandHeckbert_cost_and_placement< + internal::Line_quadric_calculator, TriangleMesh, GeomTraits> +{ +public: + typedef internal::Line_quadric_calculator Quadric_calculator; + +private: + typedef internal::GarlandHeckbert_cost_and_placement< + Quadric_calculator, TriangleMesh, GeomTraits> Base; + typedef GarlandHeckbert_line_policies Self; + +public: + typedef Self Get_cost; + typedef Self Get_placement; + + typedef typename GeomTraits::FT FT; + +public: + template + GarlandHeckbert_line_policies(TriangleMesh& tmesh, const VNM vnm) + : Base(tmesh, Quadric_calculator(vnm), 0) + { } + +public: + const Get_cost& get_cost() const { return *this; } + const Get_placement& get_placement() const { return *this; } + + using Base::operator(); +}; + +} // namespace internal +} // namespace Surface_mesh_simplification +} // namespace CGAL + +#endif // CGAL_SURFACE_MESH_SIMPLIFICATION_POLICIES_GARLANDHECKBERT_LINE_POLICIES_H \ No newline at end of file diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h index 590747a252b..289a1e4125b 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Edge_collapse/internal/GarlandHeckbert_policy_base.h @@ -72,6 +72,10 @@ public: { m_cost_matrices = get(Cost_property(), tmesh); } + + Quadric_calculator quadric_calculator() const{ + return m_quadric_calculator; + } }; template @@ -129,6 +133,15 @@ public: } public: + template + Mat_4 construct_quadric(const vertex_descriptor v, + const TriangleMesh& tmesh, + const VertexPointMap vpm, + const GeomTraits& gt) const + { + return quadric_calculator().construct_quadric_from_vertex(v, tmesh, vpm, gt); + } + template Mat_4 construct_quadric(const halfedge_descriptor he, const TriangleMesh& tmesh, @@ -154,10 +167,8 @@ public: const VertexPointMap vpm, const GeomTraits& gt) const { - Mat_4 zero_mat = Mat_4::Zero(); - for(vertex_descriptor v : vertices(tmesh)) - put(vcm(), v, zero_mat); + put(vcm(), v, construct_quadric(v, tmesh, vpm, gt)); for(face_descriptor f : faces(tmesh)) { diff --git a/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies b/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies index e6a49c552eb..4237bce9f6f 100644 --- a/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies +++ b/Surface_mesh_simplification/package_info/Surface_mesh_simplification/dependencies @@ -18,3 +18,4 @@ STL_Extension Spatial_searching Stream_support Surface_mesh_simplification +Polygon_mesh_processing diff --git a/Surface_mesh_simplification/test/Surface_mesh_simplification/edge_collapse_garland_heckbert_variations.cpp b/Surface_mesh_simplification/test/Surface_mesh_simplification/edge_collapse_garland_heckbert_variations.cpp index 32505153d06..2384787c044 100644 --- a/Surface_mesh_simplification/test/Surface_mesh_simplification/edge_collapse_garland_heckbert_variations.cpp +++ b/Surface_mesh_simplification/test/Surface_mesh_simplification/edge_collapse_garland_heckbert_variations.cpp @@ -32,6 +32,7 @@ typedef SMS::GarlandHeckbert_plane_policies typedef SMS::GarlandHeckbert_probabilistic_plane_policies Prob_plane; typedef SMS::GarlandHeckbert_triangle_policies Classic_tri; typedef SMS::GarlandHeckbert_probabilistic_triangle_policies Prob_tri; +typedef SMS::GarlandHeckbert_plane_and_line_policies Classic_plane_and_line; // settings for benchmarking - throw away the first n_burns results and keep the n_samples samples constexpr int n_burns = 1; @@ -41,6 +42,7 @@ constexpr std::size_t classic_plane_index = 0; constexpr std::size_t prob_plane_index = 1; constexpr std::size_t classic_tri_index = 2; constexpr std::size_t prob_tri_index = 3; +constexpr std::size_t plane_and_line_index = 4; // ================================================================================================= // ================================================================================================= @@ -89,7 +91,8 @@ get_all_meshes(const std::vector& filenames) template Surface_mesh edge_collapse(Surface_mesh& mesh, - const double ratio = 0.2) + const double ratio, + const Policy p) { typedef typename Policy::Get_cost Cost; typedef typename Policy::Get_placement Placement; @@ -97,8 +100,6 @@ Surface_mesh edge_collapse(Surface_mesh& mesh, std::cout << "Edge collapse mesh of " << num_edges(mesh) << " edges. Policy: " << typeid(Policy).name() << std::endl; - const Policy p { mesh, 100 }; - const Cost& cost = p.get_cost(); const Placement& unbounded_placement = p.get_placement(); Bounded_placement bounded_placement(unbounded_placement); @@ -117,6 +118,12 @@ Surface_mesh edge_collapse(Surface_mesh& mesh, return mesh; } +template +Surface_mesh edge_collapse(Surface_mesh& mesh, + const double ratio = 0.2){ + return edge_collapse(mesh, ratio, Policy(mesh)); +} + // ================================================================================================= // ================================================================================================= // ================================================================================================= @@ -145,21 +152,6 @@ void time_mesh(const TriangleMesh mesh, << "Average: " << elapsed_ns / n_samples << " (ms)" << std::endl; } -template -void time_policy(const TriangleMesh& mesh, - std::ostream& out, - const std::string& policy) -{ - if(policy == "classic_plane") - time_mesh(mesh, out); - else if(policy == "classic_tri") - time_mesh(mesh, out); - else if(policy == "prob_plane") - time_mesh(mesh, out); - else if(policy == "prob_tri") - time_mesh(mesh, out); -} - template void time_all_policies(const TriangleMesh& mesh, std::ostream& out) @@ -170,6 +162,7 @@ void time_all_policies(const TriangleMesh& mesh, time_mesh(mesh, out); time_mesh(mesh, out); time_mesh(mesh, out); + time_mesh(mesh, out); } // ================================================================================================= @@ -196,15 +189,16 @@ double hausdorff_error(const TriangleMesh& mesh, // calculate approximate Hausdorff errors for all different policies at the same decimation ratio template -std::array hausdorff_errors(const TriangleMesh& mesh, +std::array hausdorff_errors(const TriangleMesh& mesh, double ratio) { - std::array ret { {0, 0, 0, 0} }; + std::array ret { {0, 0, 0, 0, 0} }; ret[classic_plane_index] = hausdorff_error(mesh, ratio); ret[prob_plane_index] = hausdorff_error(mesh, ratio); ret[classic_tri_index] = hausdorff_error(mesh, ratio); ret[prob_tri_index] = hausdorff_error(mesh, ratio); + ret[plane_and_line_index] = hausdorff_error(mesh, ratio); return ret; } @@ -218,13 +212,14 @@ void hausdorff_errors(const TriangleMesh& mesh, for(InputIt it=begin; it!=end; ++it) { - std::array errs = hausdorff_errors(mesh, *it); + std::array errs = hausdorff_errors(mesh, *it); out << " -- Hausdorff error for collapse with ratio: " << *it << '\n'; out << "classic plane: " << errs[classic_plane_index] << std::endl; out << "prob plane : " << errs[prob_plane_index] << std::endl; out << "classic tri : " << errs[classic_tri_index] << std::endl; out << "prob tri : " << errs[prob_tri_index] << std::endl; + out << "plane + line : " << errs[plane_and_line_index] << std::endl; } } @@ -252,11 +247,13 @@ void gather_face_aspect_ratio(const TriangleMesh& mesh, Surface_mesh pp = mesh; Surface_mesh ct = mesh; Surface_mesh pt = mesh; + Surface_mesh pl = mesh; edge_collapse(cp); edge_collapse(pp); edge_collapse(ct); edge_collapse(pt); + edge_collapse(pl); out << "Face aspect-ratio: classic plane\n"; write_aspect_ratios(cp, out); @@ -266,6 +263,8 @@ void gather_face_aspect_ratio(const TriangleMesh& mesh, write_aspect_ratios(ct, out); out << "Face aspect-ratio: prob triangle\n"; write_aspect_ratios(pt, out); + out << "Face aspect-ratio: classic plane and line\n"; + write_aspect_ratios(pl, out); } template @@ -287,6 +286,21 @@ void run(const std::pair& input) gather_face_aspect_ratio(input.first, out); } +template +void test_parameters_plane_and_line(const TriangleMesh& mesh){ + using CGAL::Surface_mesh_simplification::make_GarlandHeckbert_plane_and_line_policies; + using PMap = boost::associative_property_map::vertex_descriptor, typename Kernel::Vector_3>>; + std::map::vertex_descriptor, typename Kernel::Vector_3> map; + PMap pmap(map); + CGAL::Polygon_mesh_processing::compute_vertex_normals(mesh, pmap); + TriangleMesh cp = mesh; + edge_collapse(cp, 0.2, make_GarlandHeckbert_plane_and_line_policies(cp, + CGAL::parameters::line_policies_weight(0.001) + .discontinuity_multiplier(50) + .geom_traits(Kernel()) + .vertex_normal_map(pmap))); +} + int main(int argc, char** argv) { std::vector default_data = { "data/helmet.off", @@ -301,8 +315,10 @@ int main(int argc, char** argv) data = default_data; std::vector > named_meshes = get_all_meshes(data); - for(const auto& e : named_meshes) + for(const auto& e : named_meshes){ run(e); + test_parameters_plane_and_line(e.first); + } std::cout << "Done!" << std::endl;