From 1fb933ef633180a33c9ed0b23e8ac69db8c7d1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 2 Dec 2024 13:47:02 +0100 Subject: [PATCH 01/13] add a way to pass input normal to faces --- .../Least_squares_plane_fit_region.h | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h index fdc710665819..6a82114340b6 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h @@ -137,6 +137,13 @@ namespace Polygon_mesh { vertex to `Kernel::Point_3`} \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} \cgalParamNEnd + \cgalParamNBegin{face_normal_map} + \cgalParamDescription{a property map associating normal vectors to the faces of `pmesh`.} + \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` + as key type and `GT::Vector_3` as value type, `GT` being the type of the parameter `geom_traits`.} + \cgalParamDefault{If this parameter is omitted, face normals will be estimated using crossproducts of vectors created + from consecutive vertices of the face.} + \cgalParamNEnd \cgalParamNBegin{geom_traits} \cgalParamDescription{an instance of `GeomTraits`} \cgalParamDefault{`GeomTraits()`} @@ -161,10 +168,13 @@ namespace Polygon_mesh { m_squared_distance_3(m_traits.compute_squared_distance_3_object()), m_scalar_product_3(m_traits.compute_scalar_product_3_object()), m_cross_product_3(m_traits.construct_cross_product_vector_3_object()), - m_face_normals( get(CGAL::dynamic_face_property_t(), pmesh) ), + m_face_normals(get(CGAL::dynamic_face_property_t(), pmesh)), m_face_triangulations( get(CGAL::dynamic_face_property_t>(), pmesh) ) { + static constexpr bool use_input_face_normal = + parameters::is_default_parameter::value; + #ifdef CGAL_SD_RG_USE_PMP auto get_face_normal = [this](Item face, const PolygonMesh& pmesh) { @@ -193,8 +203,19 @@ namespace Polygon_mesh { }; #endif + if constexpr (use_input_face_normal) + { + for (const Item &i : faces(pmesh)) + put(m_face_normals, i, get_face_normal(i, pmesh)); + } + else + { + auto fnm = parameters::get_parameter(np, internal_np::face_normal); + for (const Item &i : faces(pmesh)) + put(m_face_normals, i, get(fnm, i)); + } + for (const Item &i : faces(pmesh)) { - put(m_face_normals, i, get_face_normal(i, pmesh)); std::vector pts; auto h = halfedge(i, pmesh); auto s = h; From aaf81af292a2314a16117a85afff17549ef7811b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 4 Dec 2024 09:28:48 +0100 Subject: [PATCH 02/13] correctly use the variable --- .../Polygon_mesh/Least_squares_plane_fit_region.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h index 6a82114340b6..9f7a9f74f770 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h @@ -173,7 +173,7 @@ namespace Polygon_mesh { { static constexpr bool use_input_face_normal = - parameters::is_default_parameter::value; + !parameters::is_default_parameter::value; #ifdef CGAL_SD_RG_USE_PMP auto get_face_normal = [this](Item face, const PolygonMesh& pmesh) @@ -203,7 +203,7 @@ namespace Polygon_mesh { }; #endif - if constexpr (use_input_face_normal) + if constexpr (!use_input_face_normal) { for (const Item &i : faces(pmesh)) put(m_face_normals, i, get_face_normal(i, pmesh)); From a82629dc6f6bcc1698870d800c1d41334a5f1f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 4 Dec 2024 10:15:59 +0100 Subject: [PATCH 03/13] add a new region type and sorting type meant to be used to expend from the largest face --- .../Region_growing/Polygon_mesh.h | 2 + .../Polygon_mesh/Face_area_sorting.h | 193 ++++++++ .../Polygon_mesh/Plane_face_region.h | 460 ++++++++++++++++++ 3 files changed, 655 insertions(+) create mode 100644 Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h create mode 100644 Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh.h index 73443a13b8f5..6b571861b82c 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh.h @@ -29,5 +29,7 @@ #include #include +#include +#include #endif // CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_H diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h new file mode 100644 index 000000000000..203f71143e4f --- /dev/null +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h @@ -0,0 +1,193 @@ +// Copyright (c) 2018 INRIA Sophia-Antipolis (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) : Florent Lafarge, Simon Giraudot, Thien Hoang, Dmitry Anisimov +// + +#ifndef CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_FACE_AREA_SORTING_H +#define CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_FACE_AREA_SORTING_H + +#include +#include + +namespace CGAL { +namespace Shape_detection { +namespace Polygon_mesh { + + /*! + \ingroup PkgShapeDetectionRGOnMesh + + \brief Sorting of polygon mesh faces with respect to the local plane fit quality. + + `Items` of faces in a polygon mesh are sorted with respect to the quality of the + least squares plane fit applied to the vertices of incident faces of each face. + + \tparam GeomTraits + a model of `Kernel` + + \tparam PolygonMesh + a model of `FaceListGraph` + + \tparam VertexToPointMap + a model of `ReadablePropertyMap` whose key type is the vertex type of a polygon mesh and + value type is `Kernel::Point_3` + */ + template< + typename GeomTraits, + typename PolygonMesh, + typename VertexToPointMap = typename boost::property_map::const_type> + class Face_area_sorting + { + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using FT = typename GeomTraits::FT; + + public: + /// \name Types + /// @{ + + /// Item type. + using Item = typename boost::graph_traits::face_descriptor; + + /// Seed range. + using Seed_range = std::vector; + + /// @} + + /// \name Initialization + /// @{ + + /*! + \brief initializes all internal data structures. + + \tparam NamedParameters + a sequence of \ref bgl_namedparameters "Named Parameters" + + \param pmesh + an instance of `PolygonMesh` that represents a polygon mesh + + \param np + a sequence of \ref bgl_namedparameters "Named Parameters" + among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{vertex_point_map} + \cgalParamDescription{an instance of `VertexToPointMap` that maps a polygon mesh + vertex to `Kernel::Point_3`} + \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} + \cgalParamNEnd + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of `GeomTraits`} + \cgalParamDefault{`GeomTraits()`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \pre `faces(pmesh).size() > 0` + */ + template + Face_area_sorting( + const PolygonMesh& pmesh, + const CGAL_NP_CLASS& np = parameters::default_values()) + : m_face_graph(pmesh) + , m_vpm(parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(CGAL::vertex_point, pmesh))) + , m_traits(parameters::choose_parameter(parameters::get_parameter(np, internal_np::geom_traits))) + { + CGAL_precondition(faces(pmesh).size() > 0); + + m_ordered.resize(faces(pmesh).size()); + + std::size_t index = 0; + for (Item item : faces(pmesh)) + m_ordered[index++] = item; + m_scores.resize(m_ordered.size(), 0.); + } + + /// @} + + /// \name Sorting + /// @{ + + /*! + \brief sorts `Items` of input faces. + */ + void sort() { + compute_scores(); + CGAL_precondition(m_scores.size() > 0); + + auto cmp = [this](const std::size_t i, const std::size_t j) + { + CGAL_precondition(i < m_scores.size()); + CGAL_precondition(j < m_scores.size()); + return m_scores[i] > m_scores[j]; + }; + std::vector order(m_ordered.size()); + std::iota(order.begin(), order.end(), 0); + std::sort(order.begin(), order.end(), cmp); + + std::vector tmp(m_ordered.size()); + for (std::size_t i = 0; i < m_ordered.size(); i++) + tmp[i] = m_ordered[order[i]]; + + m_ordered.swap(tmp); + } + /// @} + + /// \name Access + /// @{ + + /*! + \brief returns an instance of `Seed_range` to access the ordered `Items` + of input faces. + */ + const Seed_range &ordered() { + return m_ordered; + } + /// @} + + private: + const PolygonMesh& m_face_graph; + VertexToPointMap m_vpm; + const GeomTraits m_traits; + Seed_range m_ordered; + std::vector m_scores; + + void compute_scores() + { + auto squared_area = m_traits.compute_squared_area_3_object(); + std::size_t idx = 0; + for (Item item : m_ordered) + { + halfedge_descriptor hd = halfedge(item, m_face_graph); + std::vector pts; + + for (vertex_descriptor v : vertices_around_face(hd,m_face_graph)) + pts.push_back( get(m_vpm, v) ); + + if (pts.size()==3) + m_scores[idx++] = approximate_sqrt(squared_area(pts[0], pts[1], pts[2])); + else + { + std::vector triangles; + internal::triangulate_face(pts, triangles); + for (const typename GeomTraits::Triangle_3& tr : triangles) + m_scores[idx++] += approximate_sqrt(squared_area(tr[0], tr[1], tr[2])); + } + } + } + }; + +} // namespace Polygon_mesh +} // namespace Shape_detection +} // namespace CGAL + +#endif // CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_FACE_AREA_SORTING_H + diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h new file mode 100644 index 000000000000..92b42df1b0dc --- /dev/null +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h @@ -0,0 +1,460 @@ +// Copyright (c) 2018 INRIA Sophia-Antipolis (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) : Florent Lafarge, Simon Giraudot, Thien Hoang, Dmitry Anisimov +// + +#ifndef CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_PLANE_FACE_REGION_H +#define CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_PLANE_FACE_REGION_H + +#include + +// Internal includes. +#include +#include +#ifdef CGAL_SD_RG_USE_PMP +#include +#endif + +namespace CGAL { +namespace Shape_detection { +namespace Polygon_mesh { + + /*! + \ingroup PkgShapeDetectionRGOnMesh + + \brief Region type based on the quality of the least squares plane + fit applied to faces of a polygon mesh. + + This class fits a plane, using \ref PkgPrincipalComponentAnalysisDRef "PCA", + to chunks of faces in a polygon mesh and controls the quality of this fit. + If all quality conditions are satisfied, the chunk is accepted as a valid region, + otherwise rejected. + + \tparam GeomTraits + a model of `Kernel` + + \tparam PolygonMesh + a model of `FaceListGraph` + + \tparam VertexToPointMap + a model of `ReadablePropertyMap` whose key type is the vertex type of a polygon mesh and + value type is `Kernel::Point_3` + + \cgalModels{RegionType} + */ + template< + typename GeomTraits, + typename PolygonMesh, + typename VertexToPointMap = typename boost::property_map::const_type> + class Plane_face_region { + + public: + /// \name Types + /// @{ + + /// \cond SKIP_IN_MANUAL + using Face_graph = PolygonMesh; + using Vertex_to_point_map = VertexToPointMap; + + using face_descriptor = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + /// \endcond + + /// Number type. + typedef typename GeomTraits::FT FT; + + /// Item type. + using Item = face_descriptor; + using Region = std::vector; + + /// Primitive + using Primitive = typename GeomTraits::Plane_3; + + /// Region map + using Region_index_map = typename boost::property_map >::const_type; + + /// @} + + private: + using Point_3 = typename GeomTraits::Point_3; + using Vector_3 = typename GeomTraits::Vector_3; + using Plane_3 = typename GeomTraits::Plane_3; + using Triangle_3 = typename GeomTraits::Triangle_3; + + using Squared_length_3 = typename GeomTraits::Compute_squared_length_3; + using Squared_distance_3 = typename GeomTraits::Compute_squared_distance_3; + using Scalar_product_3 = typename GeomTraits::Compute_scalar_product_3; + using Cross_product_3 = typename GeomTraits::Construct_cross_product_vector_3; + + public: + /// \name Initialization + /// @{ + + /*! + \brief initializes all internal data structures. + + \tparam NamedParameters + a sequence of \ref bgl_namedparameters "Named Parameters" + + \param pmesh + an instance of `PolygonMesh` that represents a polygon mesh + + \param np + a sequence of \ref bgl_namedparameters "Named Parameters" + among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{maximum_distance} + \cgalParamDescription{the maximum distance from the furthest vertex of a face to a plane} + \cgalParamType{`GeomTraits::FT`} + \cgalParamDefault{1} + \cgalParamNEnd + \cgalParamNBegin{maximum_angle} + \cgalParamDescription{the maximum angle in degrees between + the normal of a face and the normal of a plane} + \cgalParamType{`GeomTraits::FT`} + \cgalParamDefault{25 degrees} + \cgalParamNEnd + \cgalParamNBegin{cosine_of_maximum_angle} + \cgalParamDescription{the cosine value `cos(maximum_angle * PI / 180)` to be used instead of the parameter `maximum_angle()`} + \cgalParamType{`GeomTraits::FT`} + \cgalParamDefault{`cos(25 * PI / 180)`} + \cgalParamNEnd + \cgalParamNBegin{minimum_region_size} + \cgalParamDescription{the minimum number of faces a region must have} + \cgalParamType{`std::size_t`} + \cgalParamDefault{1} + \cgalParamNEnd + \cgalParamNBegin{vertex_point_map} + \cgalParamDescription{an instance of `VertexToPointMap` that maps a polygon mesh + vertex to `Kernel::Point_3`} + \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} + \cgalParamNEnd + \cgalParamNBegin{face_normal_map} + \cgalParamDescription{a property map associating normal vectors to the faces of `pmesh`.} + \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` + as key type and `GT::Vector_3` as value type, `GT` being the type of the parameter `geom_traits`.} + \cgalParamDefault{If this parameter is omitted, face normals will be estimated using crossproducts of vectors created + from consecutive vertices of the face.} + \cgalParamNEnd + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of `GeomTraits`} + \cgalParamDefault{`GeomTraits()`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \pre `faces(tmesh).size() > 0` + \pre `maximum_distance >= 0` + \pre `maximum_angle >= 0 && maximum_angle <= 90` + \pre `cosine_of_maximum_angle >= 0 && cosine_of_maximum_angle <= 1` + \pre `minimum_region_size > 0` + */ + template + Plane_face_region( + const PolygonMesh& pmesh, + const CGAL_NP_CLASS& np = parameters::default_values()) : + m_face_graph(pmesh), + m_vpm(parameters::choose_parameter(parameters::get_parameter( + np, internal_np::vertex_point), get_const_property_map(CGAL::vertex_point, pmesh))), + m_traits(parameters::choose_parameter(parameters::get_parameter(np, internal_np::geom_traits))), + m_squared_length_3(m_traits.compute_squared_length_3_object()), + m_squared_distance_3(m_traits.compute_squared_distance_3_object()), + m_scalar_product_3(m_traits.compute_scalar_product_3_object()), + m_cross_product_3(m_traits.construct_cross_product_vector_3_object()), + m_face_normals(get(CGAL::dynamic_face_property_t(), pmesh)), + m_face_triangulations( get(CGAL::dynamic_face_property_t>(), pmesh) ) + { + + static constexpr bool use_input_face_normal = + parameters::is_default_parameter::value; + +#ifdef CGAL_SD_RG_USE_PMP + auto get_face_normal = [this](Item face, const PolygonMesh& pmesh) + { + return Polygon_mesh_processing::compute_face_normal(face, pmesh, parameters::vertex_point_map(m_vpm)); + }; +#else + auto get_face_normal = [this](Item face, const PolygonMesh& pmesh) -> Vector_3 + { + const auto hedge = halfedge(face, pmesh); + const auto vertices = vertices_around_face(hedge, pmesh); + CGAL_precondition(vertices.size() >= 3); + + auto vertex = vertices.begin(); + const Point_3& p1 = get(m_vpm, *vertex); ++vertex; + const Point_3& p2 = get(m_vpm, *vertex); ++vertex; + Point_3 p3 = get(m_vpm, *vertex); + while(collinear(p1, p2, p3)) + { + if (++vertex == vertices.end()) return NULL_VECTOR; + p3 = get(m_vpm, *vertex); + } + + const Vector_3 u = p2 - p1; + const Vector_3 v = p3 - p1; + return m_cross_product_3(u, v); + }; +#endif + + if constexpr (use_input_face_normal) + { + for (const Item &i : faces(pmesh)) + put(m_face_normals, i, get_face_normal(i, pmesh)); + } + else + { + auto fnm = parameters::get_parameter(np, internal_np::face_normal); + for (const Item &i : faces(pmesh)) + put(m_face_normals, i, get(fnm, i)); + } + + for (const Item &i : faces(pmesh)) { + std::vector pts; + auto h = halfedge(i, pmesh); + auto s = h; + + do { + pts.push_back(get(m_vpm, target(h, pmesh))); + h = next(h, pmesh); + } while (h != s); + + std::vector face_triangulation; + internal::triangulate_face(pts, face_triangulation); + put(m_face_triangulations, i, face_triangulation); + } + + CGAL_precondition(faces(m_face_graph).size() > 0); + const FT max_distance = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::maximum_distance), FT(1)); + CGAL_precondition(max_distance >= FT(0)); + m_distance_threshold = max_distance; + + const FT max_angle = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::maximum_angle), FT(25)); + CGAL_precondition(max_angle >= FT(0) && max_angle <= FT(90)); + + m_min_region_size = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::minimum_region_size), 1); + CGAL_precondition(m_min_region_size > 0); + + const FT default_cos_value = static_cast(std::cos(CGAL::to_double( + (max_angle * static_cast(CGAL_PI)) / FT(180)))); + const FT cos_value = parameters::choose_parameter( + parameters::get_parameter(np, internal_np::cosine_of_maximum_angle), default_cos_value); + CGAL_precondition(cos_value >= FT(0) && cos_value <= FT(1)); + m_cos_value_threshold = cos_value; + } + + /// @} + + /// \name Access + /// @{ + + /*! + \brief implements `RegionType::region_index_map()`. + + This function creates an empty property map that maps each face to a std::size_t + */ + Region_index_map region_index_map() { + return get(CGAL::dynamic_face_property_t(), m_face_graph); + } + + /*! + \brief implements `RegionType::primitive()`. + + This function provides the last primitive that has been fitted with the region. + + \return Primitive parameters that fits the region. + + \pre `successful fitted primitive via successful call of update(region) with a sufficient large region` + */ + + // TODO: we probably want to return the m_seed_face instead + Primitive primitive() const { + return m_plane_of_best_fit; + } + + /*! + \brief implements `RegionType::is_part_of_region()`. + + This function controls if the face `query` is within + the `maximum_distance` from the corresponding plane and if the angle between + its normal and the plane's normal is within the `maximum_angle`. If both conditions + are satisfied, it returns `true`, otherwise `false`. + + \param query + `Item` of the query face + + The last parameter is not used in this implementation. + + \return Boolean `true` or `false` + + \pre `query` is a valid const_iterator of `input_range` + */ + bool is_part_of_region( + const Item query, + const Region&) const + { + //TODO: that's a bit dummy that we retest points that are already in the region... + + halfedge_descriptor h = halfedge(m_seed_face, m_face_graph); + + + const typename GeomTraits::Point_3& p=get(m_vpm,source(h, m_face_graph)); + const typename GeomTraits::Point_3& q=get(m_vpm,target(h, m_face_graph)); + typename GeomTraits::Point_3 r; + //TODO: add safety checks for degenerate faces + do{ + h=next(h, m_face_graph); + r=get(m_vpm,target(h, m_face_graph)); + } + while(!collinear(p,q,r)); + + + if (m_cos_value_threshold==1 || m_distance_threshold == 0) + { + h = halfedge(query, m_face_graph); + for (vertex_descriptor v : vertices_around_face(h, m_face_graph)) + { + if (!coplanar(p,q,r, get(m_vpm, v))) + return false; + } + } + else + { + // test on distance of points to the plane of the seed face + const FT squared_distance_threshold = m_distance_threshold * m_distance_threshold; + h = halfedge(query, m_face_graph); + typename GeomTraits::Plane_3 plane(p,q,r); + for (vertex_descriptor v : vertices_around_face(h, m_face_graph)) + { + FT sd = m_squared_distance_3(plane, get(m_vpm, v)); + if (sd>squared_distance_threshold) return false; + } + + + // test on the normal of the query face to the normal of the seed face + const Vector_3 face_normal = get(m_face_normals, query); + const FT cos_value = m_scalar_product_3(face_normal, m_normal_of_best_fit); + const FT squared_cos_value = cos_value * cos_value; + + FT squared_cos_value_threshold = + m_cos_value_threshold * m_cos_value_threshold; + squared_cos_value_threshold *= m_squared_length_3(face_normal); + squared_cos_value_threshold *= m_squared_length_3(m_normal_of_best_fit); + + if (squared_cos_value < squared_cos_value_threshold) + return false; + } + + return true; + } + + /*! + \brief implements `RegionType::is_valid_region()`. + + This function controls if the `region` contains at least `minimum_region_size` faces. + + \param region + Faces of the region represented as `Items`. + + \return Boolean `true` or `false` + */ + inline bool is_valid_region(const Region& region) const { + return (region.size() >= m_min_region_size); + } + + /*! + \brief implements `RegionType::update()`. + + This function fits the least squares plane to all vertices of the faces + from the `region`. + + \param region + Faces of the region represented as `Items`. + + \return Boolean `true` if the plane fitting succeeded and `false` otherwise + + \pre `region.size() > 0` + */ + bool update(const Region& region) { + + CGAL_precondition(region.size() > 0); + if (region.size() == 1) { // create new reference plane and normal + m_seed_face = region[0]; + + // The best fit plane will be a plane through this face centroid with + // its normal being the face's normal. + const Point_3 face_centroid = get_face_centroid(m_seed_face); + const Vector_3 face_normal = get(m_face_normals, m_seed_face); + if (face_normal == CGAL::NULL_VECTOR) return false; + + CGAL_precondition(face_normal != CGAL::NULL_VECTOR); + m_plane_of_best_fit = Plane_3(face_centroid, face_normal); + m_normal_of_best_fit = face_normal; + } + //TODO: shall we try to find a better seed face in the region? + return true; + } + + /// @} + + private: + const Face_graph& m_face_graph; + const Vertex_to_point_map m_vpm; + const GeomTraits m_traits; + + FT m_distance_threshold; + FT m_cos_value_threshold; + std::size_t m_min_region_size; + + const Squared_length_3 m_squared_length_3; + const Squared_distance_3 m_squared_distance_3; + const Scalar_product_3 m_scalar_product_3; + const Cross_product_3 m_cross_product_3; + + typename boost::property_map >::const_type m_face_normals; + typename boost::property_map> >::const_type m_face_triangulations; + + Plane_3 m_plane_of_best_fit; + Vector_3 m_normal_of_best_fit; + face_descriptor m_seed_face; + + // Compute centroid of the face. + template + Point_3 get_face_centroid(const Face& face) const { + + const auto hedge = halfedge(face, m_face_graph); + const auto vertices = vertices_around_face(hedge, m_face_graph); + CGAL_precondition(vertices.size() > 0); + + FT sum = FT(0), x = FT(0), y = FT(0), z = FT(0); + for (const auto vertex : vertices) { + const Point_3& point = get(m_vpm, vertex); + x += point.x(); + y += point.y(); + z += point.z(); + sum += FT(1); + } + CGAL_precondition(sum > FT(0)); + x /= sum; + y /= sum; + z /= sum; + return Point_3(x, y, z); + } + }; + +} // namespace Polygon_mesh +} // namespace Shape_detection +} // namespace CGAL + +#endif // CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_PLANE_FACE_REGION_H From a2b77a2e20d8b0d597b7fa49c050ffe93cf49ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 4 Dec 2024 10:20:12 +0100 Subject: [PATCH 04/13] tmp commit so that we can easily test in the demo --- .../include/CGAL/Polygon_mesh_processing/region_growing.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h index d6f78085c448..4923b5cb6349 100644 --- a/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h +++ b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h @@ -192,13 +192,13 @@ region_growing_of_planes_on_faces(const PolygonMesh& mesh, Static_boolean_property_map()); using Neighbor_query = internal::One_ring_neighbor_query_with_constraints; - using Region_type = RG_PM::Least_squares_plane_fit_region; - using Sorting = RG_PM::Least_squares_plane_fit_sorting; + using Region_type = RG_PM::Plane_face_region; + using Sorting = RG_PM::Face_area_sorting; using Region_growing = CGAL::Shape_detection::Region_growing; Neighbor_query neighbor_query(mesh, ecm); Region_type region_type(mesh, np); - Sorting sorting(mesh, neighbor_query, np); + Sorting sorting(mesh, np); sorting.sort(); std::vector regions; From e817be7057fafef1ca17549b982e9503d66b7a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 20 Jan 2025 20:07:16 +0100 Subject: [PATCH 05/13] fix bugs + add a robust version of the inside region predicate --- .../include/CGAL/Kernel/function_objects.h | 45 +++++++++ .../Polygon_mesh/Plane_face_region.h | 93 ++++++++++++++++--- 2 files changed, 123 insertions(+), 15 deletions(-) diff --git a/Kernel_23/include/CGAL/Kernel/function_objects.h b/Kernel_23/include/CGAL/Kernel/function_objects.h index 5a7b0774e6df..55431d98d48d 100644 --- a/Kernel_23/include/CGAL/Kernel/function_objects.h +++ b/Kernel_23/include/CGAL/Kernel/function_objects.h @@ -284,6 +284,40 @@ namespace CommonKernelFunctors { * sq_length(ba)*sq_length(bc)); } } + + result_type + operator()(const Point_3& a1, const Point_3& b1, const Point_3& c1, + const Point_3& a2, const Point_3& b2, const Point_3& c2, + const FT& cosine) const + { + typename K::Compute_scalar_product_3 scalar_product = K().compute_scalar_product_3_object(); + typename K::Compute_squared_length_3 sq_length = K().compute_squared_length_3_object(); + typename K::Construct_normal_3 normal = K().construct_normal_3_object(); + + Vector_3 n1 = normal(a1,b1,c1); + Vector_3 n2 = normal(a2,b2,c2); + + typename K::FT sc_prod = scalar_product(n1, n2); + + if (sc_prod >= 0) + { + if (cosine >= 0) + return CGAL::compare(CGAL::square(cosine) + * sq_length(n1)*sq_length(n2), + CGAL::square(sc_prod)); + else + return SMALLER; + } + else + { + if (cosine >= 0) + return LARGER; + else + return CGAL::compare(CGAL::square(sc_prod), + CGAL::square(cosine) + * sq_length(n1)*sq_length(n2)); + } + } }; template @@ -930,6 +964,9 @@ namespace CommonKernelFunctors { class Compare_squared_distance_3 { typedef typename K::FT FT; + typedef typename K::Point_3 Point_3; + typedef typename K::Plane_3 Plane_3; + typename K::Construct_plane_3 construct_plane; public: typedef typename K::Comparison_result result_type; @@ -947,6 +984,14 @@ namespace CommonKernelFunctors { return CGAL::compare(internal::squared_distance(p, q, K()), internal::squared_distance(r, s, K())); } + + + Needs_FT + operator()(const Point_3& p, const Point_3& q, const Point_3& r, const Point_3& query, const FT& sqd) const + { + Plane_3 plane = construct_plane(p,q,r); + return this->operator()(plane, query, sqd); + } }; template diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h index 92b42df1b0dc..a36bfef6e20d 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h @@ -30,13 +30,12 @@ namespace Polygon_mesh { /*! \ingroup PkgShapeDetectionRGOnMesh - \brief Region type based on the quality of the least squares plane - fit applied to faces of a polygon mesh. + \brief Region type based on the plane of the first face selected. - This class fits a plane, using \ref PkgPrincipalComponentAnalysisDRef "PCA", - to chunks of faces in a polygon mesh and controls the quality of this fit. - If all quality conditions are satisfied, the chunk is accepted as a valid region, - otherwise rejected. + This class uses the supporting plane of the first face picked for the region + and expand it for all faces with a normal close that that of the first face + (close being defined by the input angle threshold) and such that vertices are + not far from that supporting plane (far being defined by the input distance threshold). \tparam GeomTraits a model of `Kernel` @@ -173,9 +172,8 @@ namespace Polygon_mesh { m_face_normals(get(CGAL::dynamic_face_property_t(), pmesh)), m_face_triangulations( get(CGAL::dynamic_face_property_t>(), pmesh) ) { - static constexpr bool use_input_face_normal = - parameters::is_default_parameter::value; + !parameters::is_default_parameter::value; #ifdef CGAL_SD_RG_USE_PMP auto get_face_normal = [this](Item face, const PolygonMesh& pmesh) @@ -205,7 +203,7 @@ namespace Polygon_mesh { }; #endif - if constexpr (use_input_face_normal) + if constexpr (!use_input_face_normal) { for (const Item &i : faces(pmesh)) put(m_face_normals, i, get_face_normal(i, pmesh)); @@ -280,7 +278,7 @@ namespace Polygon_mesh { // TODO: we probably want to return the m_seed_face instead Primitive primitive() const { - return m_plane_of_best_fit; + return m_plane; } /*! @@ -300,6 +298,7 @@ namespace Polygon_mesh { \pre `query` is a valid const_iterator of `input_range` */ +#ifdef FAST // TODO: bench me bool is_part_of_region( const Item query, const Region&) const @@ -358,6 +357,70 @@ namespace Polygon_mesh { return true; } +#else + bool is_part_of_region( + const Item query, + const Region&) const + { + halfedge_descriptor h = halfedge(m_seed_face, m_face_graph); + + //TODO: store me! + const typename GeomTraits::Point_3& p=get(m_vpm,source(h, m_face_graph)); + const typename GeomTraits::Point_3& q=get(m_vpm,target(h, m_face_graph)); + typename GeomTraits::Point_3 r; + //TODO: add safety checks for degenerate faces + halfedge_descriptor guard = prev(h, m_face_graph); + + do{ + h=next(h, m_face_graph); + if (h == guard) return false; + r=get(m_vpm,target(h, m_face_graph)); + } + while(collinear(p,q,r)); + + + if (m_cos_value_threshold==1 || m_distance_threshold == 0) + { + h = halfedge(query, m_face_graph); + for (vertex_descriptor v : vertices_around_face(h, m_face_graph)) + { + if (!coplanar(p,q,r, get(m_vpm, v))) + return false; + } + return true; + } + else + { + // test on distance of points to the plane of the seed face + const FT squared_distance_threshold = m_distance_threshold * m_distance_threshold; + h = halfedge(query, m_face_graph); + for (vertex_descriptor v : vertices_around_face(h, m_face_graph)) + { + //TODO: that's a bit dummy that we retest points that are already in the region... + // not sure caching in a vpm does worth it (need reset for each region) + if (typename GeomTraits::Compare_squared_distance_3()(p,q,r,get(m_vpm, v), squared_distance_threshold) != SMALLER) + return false; + } + + const typename GeomTraits::Point_3& p2=get(m_vpm,source(h, m_face_graph)); + const typename GeomTraits::Point_3& q2=get(m_vpm,target(h, m_face_graph)); + typename GeomTraits::Point_3 r2; + + halfedge_descriptor guard = prev(h, m_face_graph); + do{ + h=next(h, m_face_graph); + if (h == guard) return true; + r2=get(m_vpm,target(h, m_face_graph)); + } + while(collinear(p2,q2,r2)); + + // test on the normal of the query face to the normal of the seed face + return typename GeomTraits::Compare_angle_3()(p,q,r, + p2,q2,r2, + m_cos_value_threshold) == SMALLER; + } + } +#endif /*! \brief implements `RegionType::is_valid_region()`. @@ -389,7 +452,7 @@ namespace Polygon_mesh { bool update(const Region& region) { CGAL_precondition(region.size() > 0); - if (region.size() == 1) { // create new reference plane and normal + if (region.size() == 1) { // init reference plane and normal m_seed_face = region[0]; // The best fit plane will be a plane through this face centroid with @@ -399,8 +462,8 @@ namespace Polygon_mesh { if (face_normal == CGAL::NULL_VECTOR) return false; CGAL_precondition(face_normal != CGAL::NULL_VECTOR); - m_plane_of_best_fit = Plane_3(face_centroid, face_normal); - m_normal_of_best_fit = face_normal; + m_plane = Plane_3(face_centroid, face_normal); + m_normal = face_normal; } //TODO: shall we try to find a better seed face in the region? return true; @@ -425,8 +488,8 @@ namespace Polygon_mesh { typename boost::property_map >::const_type m_face_normals; typename boost::property_map> >::const_type m_face_triangulations; - Plane_3 m_plane_of_best_fit; - Vector_3 m_normal_of_best_fit; + Plane_3 m_plane; + Vector_3 m_normal; face_descriptor m_seed_face; // Compute centroid of the face. From e538d5e1034c2d65a34b2b33ed4b3aac54d8c2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 26 Mar 2025 14:15:16 +0100 Subject: [PATCH 06/13] keep on the certified version --- .../Polygon_mesh/Face_area_sorting.h | 5 +- .../Polygon_mesh/Plane_face_region.h | 65 +------------------ 2 files changed, 5 insertions(+), 65 deletions(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h index 203f71143e4f..fa928d2fcb3f 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018 INRIA Sophia-Antipolis (France). +// Copyright (c) 2024-2025 GeometryFactory (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -8,9 +8,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // -// Author(s) : Florent Lafarge, Simon Giraudot, Thien Hoang, Dmitry Anisimov +// Author(s) : Sébastien Loriot // + #ifndef CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_FACE_AREA_SORTING_H #define CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_FACE_AREA_SORTING_H diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h index a36bfef6e20d..ffc71fde41e3 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018 INRIA Sophia-Antipolis (France). +// Copyright (c) 2024-2025 GeometryFactory (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -8,7 +8,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // -// Author(s) : Florent Lafarge, Simon Giraudot, Thien Hoang, Dmitry Anisimov +// Author(s) : Sébastien Loriot // #ifndef CGAL_SHAPE_DETECTION_REGION_GROWING_POLYGON_MESH_PLANE_FACE_REGION_H @@ -298,66 +298,6 @@ namespace Polygon_mesh { \pre `query` is a valid const_iterator of `input_range` */ -#ifdef FAST // TODO: bench me - bool is_part_of_region( - const Item query, - const Region&) const - { - //TODO: that's a bit dummy that we retest points that are already in the region... - - halfedge_descriptor h = halfedge(m_seed_face, m_face_graph); - - - const typename GeomTraits::Point_3& p=get(m_vpm,source(h, m_face_graph)); - const typename GeomTraits::Point_3& q=get(m_vpm,target(h, m_face_graph)); - typename GeomTraits::Point_3 r; - //TODO: add safety checks for degenerate faces - do{ - h=next(h, m_face_graph); - r=get(m_vpm,target(h, m_face_graph)); - } - while(!collinear(p,q,r)); - - - if (m_cos_value_threshold==1 || m_distance_threshold == 0) - { - h = halfedge(query, m_face_graph); - for (vertex_descriptor v : vertices_around_face(h, m_face_graph)) - { - if (!coplanar(p,q,r, get(m_vpm, v))) - return false; - } - } - else - { - // test on distance of points to the plane of the seed face - const FT squared_distance_threshold = m_distance_threshold * m_distance_threshold; - h = halfedge(query, m_face_graph); - typename GeomTraits::Plane_3 plane(p,q,r); - for (vertex_descriptor v : vertices_around_face(h, m_face_graph)) - { - FT sd = m_squared_distance_3(plane, get(m_vpm, v)); - if (sd>squared_distance_threshold) return false; - } - - - // test on the normal of the query face to the normal of the seed face - const Vector_3 face_normal = get(m_face_normals, query); - const FT cos_value = m_scalar_product_3(face_normal, m_normal_of_best_fit); - const FT squared_cos_value = cos_value * cos_value; - - FT squared_cos_value_threshold = - m_cos_value_threshold * m_cos_value_threshold; - squared_cos_value_threshold *= m_squared_length_3(face_normal); - squared_cos_value_threshold *= m_squared_length_3(m_normal_of_best_fit); - - if (squared_cos_value < squared_cos_value_threshold) - return false; - } - - return true; - } -#else bool is_part_of_region( const Item query, const Region&) const @@ -420,7 +360,6 @@ namespace Polygon_mesh { m_cos_value_threshold) == SMALLER; } } -#endif /*! \brief implements `RegionType::is_valid_region()`. From 51e4b9add2b2230ba2cc56e0c06d0499cd01653a Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 27 Mar 2025 12:16:39 +0100 Subject: [PATCH 07/13] pass on doc --- .../doc/Shape_detection/PackageDescription.txt | 2 ++ Shape_detection/doc/Shape_detection/Shape_detection.txt | 8 +++++--- .../Region_growing/Polygon_mesh/Face_area_sorting.h | 5 ++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Shape_detection/doc/Shape_detection/PackageDescription.txt b/Shape_detection/doc/Shape_detection/PackageDescription.txt index e1efc07b3dd8..acc7d9177a98 100644 --- a/Shape_detection/doc/Shape_detection/PackageDescription.txt +++ b/Shape_detection/doc/Shape_detection/PackageDescription.txt @@ -96,7 +96,9 @@ to simplify the definition and creation of the different classes. The naming con - `CGAL::Shape_detection::Polygon_mesh::One_ring_neighbor_query` - `CGAL::Shape_detection::Polygon_mesh::Polyline_graph` - `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region` +- `CGAL::Shape_detection::Polygon_mesh::Plane_face_region` - `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting` +- `CGAL::Shape_detection::Polygon_mesh::Face_area_sorting` \defgroup PkgShapeDetectionRGConcepts Concepts diff --git a/Shape_detection/doc/Shape_detection/Shape_detection.txt b/Shape_detection/doc/Shape_detection/Shape_detection.txt index c51a84aad197..6371ac4da912 100644 --- a/Shape_detection/doc/Shape_detection/Shape_detection.txt +++ b/Shape_detection/doc/Shape_detection/Shape_detection.txt @@ -261,7 +261,7 @@ their own propagation and seeding conditions (see \ref Shape_detection_RegionGro \subsubsection Shape_detection_RegionGrowingFramework_examples Examples -This toy example shows how to define a custom models of`NeighborQuery` and +This toy example shows how to define a custom models of `NeighborQuery` and `RegionType` concept, which are used to parameterize the `CGAL::Shape_detection::Region_growing`. @@ -483,6 +483,7 @@ In particular, it provides - `CGAL::Shape_detection::Polygon_mesh::One_ring_neighbor_query` class that retrieves all edge-adjacent faces of a face, and - `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region` class that fits a 3D plane to the vertices of all mesh faces, which have been added to the region so far, and controls the quality of this fit. +- `CGAL::Shape_detection::Polygon_mesh::Plane_face_region` is another `RegionType` that uses the 3D plane of the seed face and does not refit the plane to the region. This component accepts any model of the concept `FaceListGraph` as a polygon mesh. Figure \cgalFigureRef{Region_growing_on_surface_mesh} gives an \ref Shape_detection_RegionGrowingMesh_examples "example" of the region growing algorithm @@ -495,10 +496,11 @@ A surface mesh depicted with one color per detected plane. The quality of region growing on a polygon mesh can be improved by slightly increasing the running time. To achieve this, one can sort the input faces with respect to some quality criteria. These quality criteria can be included in region growing through the seed range (see \ref Shape_detection_RegionGrowingFramework for more details). -We provide such a quality sorting: +We provide two sortings: - `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting` - the input faces are sorted with respect to the quality of the least squares plane fit applied to the neighbors of each face. +- `CGAL::Shape_detection::Polygon_mesh::Face_area_sorting` - the input faces are sorted with respect to the area. The largest face are chosen as the first seed. \subsubsection Shape_detection_RegionGrowingMesh_parameters Parameters @@ -528,7 +530,7 @@ and only then detect regions. \subsubsection Shape_detection_RegionGrowingMesh_performance Performance Since accessing neighbors of a face in a polygon mesh is fast, performance of the region growing on a polygon mesh -mostly depends on the `RegionType` model's implementation, which is usually fast, too. +mostly depends on the `RegionType` model's implementation, which is usually fast, too. `CGAL::Shape_detection::Polygon_mesh::Plane_face_region` is faster than `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region` as it does not perform a refitting of the plane to the region, whereas the latter may provide a tighter fit of the plane to the region and thus a lower number of larger regions. \subsection Shape_detection_RegionGrowingSegments Segment Set diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h index fa928d2fcb3f..a73b8b816484 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h @@ -25,10 +25,9 @@ namespace Polygon_mesh { /*! \ingroup PkgShapeDetectionRGOnMesh - \brief Sorting of polygon mesh faces with respect to the local plane fit quality. + \brief Sorting of polygon mesh faces with respect to their area. - `Items` of faces in a polygon mesh are sorted with respect to the quality of the - least squares plane fit applied to the vertices of incident faces of each face. + `Items` of faces in a polygon mesh are sorted in descending order of area. \tparam GeomTraits a model of `Kernel` From 18f4e24150c728635f9a5cf95704d1d9f7b9013c Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 27 Mar 2025 12:39:50 +0100 Subject: [PATCH 08/13] modified CHANGES.md added missing named parameter --- Installation/CHANGES.md | 7 +++++++ .../include/CGAL/Polygon_mesh_processing/region_growing.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index a393148dc40c..cfca66e9bbb7 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -7,6 +7,13 @@ - **Breaking change**: Classes based on the RS Library are no longer provided. +### [Shape Detection](https://doc.cgal.org/6.1/Manual/packages.html#PkgShapeDetection) + +- Added the region type [`CGAL::Shape_detection::Polygon_mesh::Plane_face_region`](https://doc.cgal.org/6.1/Shape_detection/class_c_g_a_l_1_1_shape__detection_1_1_polygon__mesh_1_1_plane__face__region.html) that extends the support plane of the seed face without refitting the plane to the region +- Added the sorting [`CGAL::Shape_detection::Polygon_mesh::Face_area_sorting`](https://doc.cgal.org/6.1/Shape_detection/class_c_g_a_l_1_1_shape__detection_1_1_polygon__mesh_1_1_face__area__sorting.html) that provides a sorting of faces in descending order of area +- Added the named parameter `face_normal_map` to [`CGAL::Shape_detection::Polygon_mesh::Linear_least_squares_fit_region`](https://doc.cgal.org/6.1/Shape_detection/class_c_g_a_l_1_1_shape__detection_1_1_polygon__mesh_1_1_least__squares__plane__fit__region.html) ,[`CGAL::Shape_detection::Polygon_mesh::Plane_face_region`](https://doc.cgal.org/6.1/Shape_detection/class_c_g_a_l_1_1_shape__detection_1_1_polygon__mesh_1_1_plane__face__region.html) and [`CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces`](https://doc.cgal.org/6.1/Polygon_mesh_processing/group___pkg_polygon_mesh_processing_ref.html#ga50dcd2f6295f584d2e378b57290ae2af) to allow the use of face normal property maps instead of calculating the face normals. +- The [`CGAL::Shape_detection::Polygon_mesh::Plane_face_region`](https://doc.cgal.org/6.1/Shape_detection/class_c_g_a_l_1_1_shape__detection_1_1_polygon__mesh_1_1_plane__face__region.html) and [`CGAL::Shape_detection::Polygon_mesh::Face_area_sorting`](https://doc.cgal.org/6.1/Shape_detection/class_c_g_a_l_1_1_shape__detection_1_1_polygon__mesh_1_1_face__area__sorting.html) are now used as the default in [`CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces`](https://doc.cgal.org/6.1/Polygon_mesh_processing/group___pkg_polygon_mesh_processing_ref.html#ga50dcd2f6295f584d2e378b57290ae2af) + ## [Release 6.0.1](https://github.com/CGAL/cgal/releases/tag/v6.0.1) diff --git a/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h index 4923b5cb6349..8c936623e2d0 100644 --- a/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h +++ b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h @@ -156,6 +156,13 @@ class One_ring_neighbor_query_with_constraints \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} \cgalParamNEnd + \cgalParamNBegin{face_normal_map} + \cgalParamDescription{a property map associating normal vectors to the faces of `pmesh`.} + \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` + as key type and `GT::Vector_3` as value type, `GT` being the type of the parameter `geom_traits`.} + \cgalParamDefault{If this parameter is omitted, face normals will be estimated using crossproducts of vectors created + from consecutive vertices of the face.} + \cgalParamNEnd \cgalParamNBegin{region_primitive_map} \cgalParamDescription{a property map filled by this function and that will contain for each region the plane (or only its orthognonal vector) estimated that approximates it.} From 019c0a5f78a1b43a54dbb4368c6248c766d2f9db Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 1 Apr 2025 09:43:19 +0200 Subject: [PATCH 09/13] integrating reviews --- .../Point_set/Least_squares_circle_fit_region.h | 5 ++--- .../Point_set/Least_squares_cylinder_fit_region.h | 5 ++--- .../Point_set/Least_squares_line_fit_region.h | 3 +-- .../Point_set/Least_squares_plane_fit_region.h | 5 ++--- .../Point_set/Least_squares_sphere_fit_region.h | 5 ++--- .../Region_growing/Polygon_mesh/Face_area_sorting.h | 2 +- .../Polygon_mesh/Least_squares_plane_fit_region.h | 5 ++--- .../Region_growing/Polygon_mesh/Plane_face_region.h | 7 +++---- .../Segment_set/Least_squares_line_fit_region.h | 5 ++--- 9 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_circle_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_circle_fit_region.h index 15c5d39e13c7..8eae00029974 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_circle_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_circle_fit_region.h @@ -220,7 +220,7 @@ namespace Point_set { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps iterators on the input range `Item` to std::size_t + This function creates an empty property map that maps iterators on the input range `Item` to `std::size_t`. */ Region_index_map region_index_map() { return Region_index_map(m_region_map); @@ -247,8 +247,7 @@ namespace Point_set { its normal and the circle radius is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - item of the query point + \param query item of the query point \param region inlier items of the region diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_cylinder_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_cylinder_fit_region.h index 12543ef500f8..b23d32b33b88 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_cylinder_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_cylinder_fit_region.h @@ -219,7 +219,7 @@ namespace Point_set { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps iterators on the input range `Item` to std::size_t + This function creates an empty property map that maps iterators on the input range `Item` to `std::size_t`. */ Region_index_map region_index_map() { return Region_index_map(m_region_map); @@ -246,8 +246,7 @@ namespace Point_set { its normal and the cylinder radius is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - item of the query point + \param query item of the query point \param region inlier items of the region diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_line_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_line_fit_region.h index d61ecad6b1d6..c4f7e6db8d89 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_line_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_line_fit_region.h @@ -214,8 +214,7 @@ namespace Point_set { its normal and the line's normal is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - item of the query point + \param query item of the query point The last parameter is not used in this implementation. diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_plane_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_plane_fit_region.h index 912114e693ae..6c113283019f 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_plane_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_plane_fit_region.h @@ -188,7 +188,7 @@ namespace Point_set { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps iterators on the input range `Item` to std::size_t. + This function creates an empty property map that maps iterators on the input range `Item` to `std::size_t`. */ Region_index_map region_index_map() { return Region_index_map(m_region_map); @@ -215,8 +215,7 @@ namespace Point_set { its normal and the plane's normal is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - `Item` of the query point + \param query item of the query point The last parameter is not used in this implementation. diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_sphere_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_sphere_fit_region.h index 55b935c357ed..d00c27de6a5d 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_sphere_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Point_set/Least_squares_sphere_fit_region.h @@ -209,7 +209,7 @@ namespace Point_set { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps iterators on the input range `Item` to std::size_t + This function creates an empty property map that maps iterators on the input range `Item` to `std::size_t`. */ Region_index_map region_index_map() { @@ -238,8 +238,7 @@ namespace Point_set { its normal and the sphere radius is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - item of the query point + \param query item of the query point \param region inlier items of the region diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h index a73b8b816484..b3306904532c 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h @@ -27,7 +27,7 @@ namespace Polygon_mesh { \brief Sorting of polygon mesh faces with respect to their area. - `Items` of faces in a polygon mesh are sorted in descending order of area. + `Items` of faces in a polygon mesh are sorted in decreasing area. \tparam GeomTraits a model of `Kernel` diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h index 9f7a9f74f770..4375cbada0f2 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Least_squares_plane_fit_region.h @@ -260,7 +260,7 @@ namespace Polygon_mesh { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps each face to a std::size_t + This function creates an empty property map that maps each face to a `std::size_t`. */ Region_index_map region_index_map() { return get(CGAL::dynamic_face_property_t(), m_face_graph); @@ -288,8 +288,7 @@ namespace Polygon_mesh { its normal and the plane's normal is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - `Item` of the query face + \param query item of the query face The last parameter is not used in this implementation. diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h index ffc71fde41e3..76e02e79c291 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Plane_face_region.h @@ -33,7 +33,7 @@ namespace Polygon_mesh { \brief Region type based on the plane of the first face selected. This class uses the supporting plane of the first face picked for the region - and expand it for all faces with a normal close that that of the first face + and expands it for all faces with a normal close to that of the first face (close being defined by the input angle threshold) and such that vertices are not far from that supporting plane (far being defined by the input distance threshold). @@ -260,7 +260,7 @@ namespace Polygon_mesh { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps each face to a std::size_t + This function creates an empty property map that maps each face to a `std::size_t`. */ Region_index_map region_index_map() { return get(CGAL::dynamic_face_property_t(), m_face_graph); @@ -289,8 +289,7 @@ namespace Polygon_mesh { its normal and the plane's normal is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - `Item` of the query face + \param query item of the query face The last parameter is not used in this implementation. diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Segment_set/Least_squares_line_fit_region.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Segment_set/Least_squares_line_fit_region.h index 702cbaa0fb95..552ff6dd72fa 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Segment_set/Least_squares_line_fit_region.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Segment_set/Least_squares_line_fit_region.h @@ -193,7 +193,7 @@ namespace Segment_set { /*! \brief implements `RegionType::region_index_map()`. - This function creates an empty property map that maps iterators on the input range `Item` to std::size_t. + This function creates an empty property map that maps iterators on the input range `Item` to `std::size_t`. */ Region_index_map region_index_map() { return Region_index_map(m_region_map); @@ -220,8 +220,7 @@ namespace Segment_set { direction of this segment and the line's direction is within the `maximum_angle`. If both conditions are satisfied, it returns `true`, otherwise `false`. - \param query - `Item` of the query segment + \param query item of the query segment The last parameter is not used in this implementation. From 7e2b312994ff721640910133c24835e5f44746dc Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 9 Apr 2025 10:51:24 +0200 Subject: [PATCH 10/13] merge fix --- Kernel_23/include/CGAL/Kernel/function_objects.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Kernel_23/include/CGAL/Kernel/function_objects.h b/Kernel_23/include/CGAL/Kernel/function_objects.h index 6d64e98e8fe2..7d875d069db6 100644 --- a/Kernel_23/include/CGAL/Kernel/function_objects.h +++ b/Kernel_23/include/CGAL/Kernel/function_objects.h @@ -276,7 +276,7 @@ namespace CommonKernelFunctors { } } - result_type + Comparison_result operator()(const Point_3& a1, const Point_3& b1, const Point_3& c1, const Point_3& a2, const Point_3& b2, const Point_3& c2, const FT& cosine) const @@ -937,7 +937,6 @@ namespace CommonKernelFunctors { typedef typename K::Plane_3 Plane_3; typename K::Construct_plane_3 construct_plane; public: - typedef typename K::Comparison_result result_type; public: template @@ -955,7 +954,7 @@ namespace CommonKernelFunctors { } - Needs_FT + Needs_FT operator()(const Point_3& p, const Point_3& q, const Point_3& r, const Point_3& query, const FT& sqd) const { Plane_3 plane = construct_plane(p,q,r); From 737c0502f7cc5cdd48a354bf2c09713202abfad5 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 9 Apr 2025 14:51:08 +0200 Subject: [PATCH 11/13] extend example to cover Plane_face_region and Face_area_sorting --- .../doc/Shape_detection/Shape_detection.txt | 2 +- .../region_growing_planes_on_polygon_mesh.cpp | 79 ++++++++++--------- .../Polygon_mesh/Face_area_sorting.h | 51 ++++++++++++ 3 files changed, 95 insertions(+), 37 deletions(-) diff --git a/Shape_detection/doc/Shape_detection/Shape_detection.txt b/Shape_detection/doc/Shape_detection/Shape_detection.txt index 29a70b122abf..e1b4e3af8903 100644 --- a/Shape_detection/doc/Shape_detection/Shape_detection.txt +++ b/Shape_detection/doc/Shape_detection/Shape_detection.txt @@ -522,7 +522,7 @@ as the one explained in Section \ref Shape_detection_RegionGrowingPoints_paramet In the example below, we show how to use region growing to detect planes on a polygon mesh that can be either stored as `CGAL::Surface_mesh` or `CGAL::Polyhedron_3`. We can improve the quality of region growing by providing a different seeding order (analogously to \ref Shape_detection_RegionGrowingPoints "Point Sets") that is why in this example we also sort the input faces using the `CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting` -and only then detect regions. +and only then detect regions. The example also shows the use of `CGAL::Shape_detection::Polygon_mesh::Plane_face_region` which expands a region using the support plane of a face without refitting and `CGAL::Shape_detection::Polygon_mesh::Face_area_sorting` which sorts the faces in decreasing area. \cgalExample{Shape_detection/region_growing_planes_on_polygon_mesh.cpp} diff --git a/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp b/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp index 43d7b5b291ae..592c0eae399a 100644 --- a/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp +++ b/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp @@ -21,56 +21,38 @@ using Polygon_mesh = CGAL::Surface_mesh; #endif using Neighbor_query = CGAL::Shape_detection::Polygon_mesh::One_ring_neighbor_query; -using Region_type = CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region; -using Sorting = CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting; -using Region_growing = CGAL::Shape_detection::Region_growing; - -int main(int argc, char *argv[]) { - - // Load data either from a local folder or a user-provided file. - const bool is_default_input = argc > 1 ? false : true; - const std::string filename = is_default_input ? CGAL::data_file_path("meshes/building.off") : argv[1]; - std::ifstream in(filename); - CGAL::IO::set_ascii_mode(in); - - Polygon_mesh polygon_mesh; - if (!CGAL::IO::read_polygon_mesh(filename, polygon_mesh)) { - std::cerr << "ERROR: cannot read the input file!" << std::endl; - return EXIT_FAILURE; - } - const auto& face_range = faces(polygon_mesh); - std::cout << "* number of input faces: " << face_range.size() << std::endl; - assert(!is_default_input || face_range.size() == 32245); - - // Default parameter values for the data file building.off. - const FT max_distance = FT(1); - const FT max_angle = FT(45); - const std::size_t min_region_size = 5; +using LS_region_type = CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_region; +using LS_sorting = CGAL::Shape_detection::Polygon_mesh::Least_squares_plane_fit_sorting; +using Face_region_type = CGAL::Shape_detection::Polygon_mesh::Plane_face_region; +using Face_area_Sorting = CGAL::Shape_detection::Polygon_mesh::Face_area_sorting; +template +void detect(Polygon_mesh &mesh, FT max_distance, FT max_angle, std::size_t min_region_size, const std::string &out_filename) { // Create instances of the classes Neighbor_query and Region_type. - Neighbor_query neighbor_query(polygon_mesh); + Neighbor_query neighbor_query(mesh); - Region_type region_type( - polygon_mesh, + RegionType region_type( + mesh, CGAL::parameters:: maximum_distance(max_distance). maximum_angle(max_angle). minimum_region_size(min_region_size)); // Sort face indices. - Sorting sorting( - polygon_mesh, neighbor_query); + Sorting_type sorting( + mesh, neighbor_query); sorting.sort(); + using Region_growing = CGAL::Shape_detection::Region_growing; + // Create an instance of the region growing class. Region_growing region_growing( - face_range, sorting.ordered(), neighbor_query, region_type); + faces(mesh), sorting.ordered(), neighbor_query, region_type); // Run the algorithm. std::vector regions; region_growing.detect(std::back_inserter(regions)); - std::cout << "* number of found planes: " << regions.size() << std::endl; - assert(!is_default_input || regions.size() == 365); + std::cout << regions.size() << " regions found" << std::endl; const Region_growing::Region_map& map = region_growing.region_map(); @@ -82,7 +64,7 @@ int main(int argc, char *argv[]) { } std::vector unassigned; - region_growing.unassigned_items(face_range, std::back_inserter(unassigned)); + region_growing.unassigned_items(faces(mesh), std::back_inserter(unassigned)); for (auto& item : unassigned) { if (std::size_t(-1) != get(map, item)) { @@ -91,8 +73,33 @@ int main(int argc, char *argv[]) { } // Save regions to a file. - const std::string fullpath = (argc > 2 ? argv[2] : "planes_polygon_mesh.ply"); - utils::save_polygon_mesh_regions(polygon_mesh, regions, fullpath); + utils::save_polygon_mesh_regions(mesh, regions, out_filename); +} + +int main(int argc, char *argv[]) { + + // Load data either from a local folder or a user-provided file. + const std::string filename = argc == 1 ? CGAL::data_file_path("meshes/building.off") : argv[1]; + std::ifstream in(filename); + CGAL::IO::set_ascii_mode(in); + + Polygon_mesh polygon_mesh; + if (!CGAL::IO::read_polygon_mesh(filename, polygon_mesh)) { + std::cerr << "ERROR: cannot read the input file!" << std::endl; + return EXIT_FAILURE; + } + const auto& face_range = faces(polygon_mesh); + std::cout << "* number of input faces: " << face_range.size() << std::endl; + + // Default parameter values for the data file building.off. + const FT max_distance = FT(1); + const FT max_angle = FT(45); + const std::size_t min_region_size = 5; + + std::cout << "Region growing with Least_squares_plane_fit_region: "; + detect(polygon_mesh, max_distance, max_angle, min_region_size, "least_squares_planes_polygon_mesh.ply"); + std::cout << "Region growing with Plane_face_region: "; + detect(polygon_mesh, max_distance, max_angle, min_region_size, "face_planes_polygon_mesh.ply"); return EXIT_SUCCESS; } diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h index b3306904532c..a7b93ecfb138 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h @@ -111,6 +111,57 @@ namespace Polygon_mesh { m_scores.resize(m_ordered.size(), 0.); } + /*! + \brief initializes all internal data structures. + 3 Parameter constructor with dummy parameter provided for compatibility with other sorting types. + + \tparam Dummy + Dummy parameter, not used. + + \tparam NamedParameters + a sequence of \ref bgl_namedparameters "Named Parameters" + + \param pmesh + an instance of `PolygonMesh` that represents a polygon mesh + + \param np + a sequence of \ref bgl_namedparameters "Named Parameters" + among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{vertex_point_map} + \cgalParamDescription{an instance of `VertexToPointMap` that maps a polygon mesh + vertex to `Kernel::Point_3`} + \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} + \cgalParamNEnd + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of `GeomTraits`} + \cgalParamDefault{`GeomTraits()`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \pre `faces(pmesh).size() > 0` + */ + template + Face_area_sorting( + const PolygonMesh& pmesh, + const Dummy&, + const CGAL_NP_CLASS& np = parameters::default_values()) + : m_face_graph(pmesh) + , m_vpm(parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(CGAL::vertex_point, pmesh))) + , m_traits(parameters::choose_parameter(parameters::get_parameter(np, internal_np::geom_traits))) + { + CGAL_precondition(faces(pmesh).size() > 0); + + m_ordered.resize(faces(pmesh).size()); + + std::size_t index = 0; + for (Item item : faces(pmesh)) + m_ordered[index++] = item; + m_scores.resize(m_ordered.size(), 0.); + } + /// @} /// \name Sorting From 7c3103c00e5a3a082f0d76bb5afe2da9965f1a09 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 11 Apr 2025 10:47:12 +0200 Subject: [PATCH 12/13] added missing typename --- .../Shape_detection/region_growing_planes_on_polygon_mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp b/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp index 592c0eae399a..b7faf914dd86 100644 --- a/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp +++ b/Shape_detection/examples/Shape_detection/region_growing_planes_on_polygon_mesh.cpp @@ -54,7 +54,7 @@ void detect(Polygon_mesh &mesh, FT max_distance, FT max_angle, std::size_t min_r region_growing.detect(std::back_inserter(regions)); std::cout << regions.size() << " regions found" << std::endl; - const Region_growing::Region_map& map = region_growing.region_map(); + const typename Region_growing::Region_map& map = region_growing.region_map(); for (std::size_t i = 0; i < regions.size(); i++) for (auto& item : regions[i].second) { From 977348fa4c9f9c30f18af80cbcde6fb4fcf32c48 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 15 Apr 2025 16:08:26 +0200 Subject: [PATCH 13/13] move ++idx outside the for loop --- .../Region_growing/Polygon_mesh/Face_area_sorting.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h index a7b93ecfb138..c63f3e9be4a6 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h +++ b/Shape_detection/include/CGAL/Shape_detection/Region_growing/Polygon_mesh/Face_area_sorting.h @@ -230,7 +230,8 @@ namespace Polygon_mesh { std::vector triangles; internal::triangulate_face(pts, triangles); for (const typename GeomTraits::Triangle_3& tr : triangles) - m_scores[idx++] += approximate_sqrt(squared_area(tr[0], tr[1], tr[2])); + m_scores[idx] += approximate_sqrt(squared_area(tr[0], tr[1], tr[2])); + ++idx; } } }