From dd19b506ed8b4c38953dc5b6d784138e442256e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sat, 3 Apr 2021 11:14:15 +0200 Subject: [PATCH 1/8] first version to add epsilon per face --- .../CGAL/boost/graph/parameters_interface.h | 1 + .../include/CGAL/Polyhedral_envelope.h | 62 ++++++++++++++++--- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index eebd236eaca..1ce1ce3900f 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -120,6 +120,7 @@ CGAL_add_named_parameter(do_not_modify_t, do_not_modify, do_not_modify) CGAL_add_named_parameter(allow_self_intersections_t, allow_self_intersections, allow_self_intersections) CGAL_add_named_parameter(non_manifold_feature_map_t, non_manifold_feature_map, non_manifold_feature_map) CGAL_add_named_parameter(polyhedral_envelope_epsilon_t, polyhedral_envelope_epsilon, polyhedral_envelope_epsilon) +CGAL_add_named_parameter(face_epsilon_map_t, face_epsilon_map, face_epsilon_map) // List of named parameters that we use in the package 'Surface Mesh Simplification' CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost) diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h index b1d76c12950..16b3e554e76 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h @@ -333,8 +333,10 @@ public: { using parameters::choose_parameter; using parameters::get_parameter; + using parameters::is_default_parameter; typedef boost::graph_traits Graph_traits; + typedef typename Graph_traits::face_descriptor face_descriptor; typename GetVertexPointMap::const_type vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), @@ -353,7 +355,7 @@ public: } GeomTraits gt; - for(typename Graph_traits::face_descriptor f : faces(tmesh)){ + for(face_descriptor f : faces(tmesh)){ if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))){ typename Graph_traits::halfedge_descriptor h = halfedge(f, tmesh); int i = get(vim, source(h, tmesh)); @@ -364,7 +366,26 @@ public: env_faces.push_back(face); } } - init(epsilon); + if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) + init(std::vector(env_faces.size(), epsilon)); + else + { + std::vector epsilon_values; + epsilon_values.reserve(env_faces.size()); + + typedef typename internal_np::Lookup_named_param_def< + internal_np::face_epsilon_map_t, + NamedParameters, + Constant_property_map + > ::type Epsilon_map; + + Epsilon_map epsilon_map = choose_parameter(get_parameter(np, internal_np::face_epsilon_map), + Constant_property_map(epsilon)); + + for(face_descriptor f : faces(tmesh)) + if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))) + epsilon_values.push_back( get(epsilon_map, f) ); + } } /** @@ -407,6 +428,7 @@ public: { using parameters::choose_parameter; using parameters::get_parameter; + using parameters::is_default_parameter; typename GetVertexPointMap::const_type vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), @@ -442,7 +464,27 @@ public: env_faces.push_back(face); } } - init(epsilon); + + if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) + init(std::vector(env_faces.size(), epsilon)); + else + { + std::vector epsilon_values; + epsilon_values.reserve(env_faces.size()); + + typedef typename internal_np::Lookup_named_param_def< + internal_np::face_epsilon_map_t, + NamedParameters, + Constant_property_map + > ::type Epsilon_map; + + Epsilon_map epsilon_map = choose_parameter(get_parameter(np, internal_np::face_epsilon_map), + Constant_property_map(epsilon)); + + for(face_descriptor f : face_range) + if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))) + epsilon_values.push_back( get(epsilon_map, f) ); + } } /** @@ -499,7 +541,7 @@ public: Vector3i face = { int(t[0]), int(t[1]), int(t[2]) }; env_faces.emplace_back(face); } - init(epsilon); + init(std::vector(env_faces.size(), epsilon)); } /// @} @@ -530,9 +572,9 @@ public: private: - void init(double epsilon) + void init(const std::vector& epsilon_values) { - halfspace_generation(env_vertices, env_faces, halfspace, bounding_boxes, epsilon); + halfspace_generation(env_vertices, env_faces, halfspace, bounding_boxes, epsilon_values); Datum_map datum_map(bounding_boxes); Point_map point_map(bounding_boxes); @@ -1775,10 +1817,8 @@ private: void halfspace_generation(const std::vector &ver, const std::vector &faces, std::vector& halfspace, - std::vector& bounding_boxes, const double epsilon) + std::vector& bounding_boxes, const std::vector& epsilon_values) { - double tolerance = epsilon / std::sqrt(3);// the envelope thickness, to be conservative - double bbox_tolerance = epsilon *(1 + 1e-6); Vector_3 AB, AC, BC, normal; Plane plane; std::array box; @@ -1804,6 +1844,10 @@ private: bounding_boxes.resize(faces.size()); for (unsigned int i = 0; i < faces.size(); ++i) { + const double epsilon = epsilon_values[i]; + double tolerance = epsilon / std::sqrt(3);// the envelope thickness, to be conservative + double bbox_tolerance = epsilon *(1 + 1e-6); + Bbox bb = ver[faces[i][0]].bbox () + ver[faces[i][1]].bbox() + ver[faces[i][2]].bbox(); // todo: Add a grow() function to Bbox bounding_boxes[i] = Iso_cuboid_3(Point_3(bb.xmin()-bbox_tolerance, bb.ymin()-bbox_tolerance, bb.zmin()-bbox_tolerance), From b7bafa1a3451ede4c4b50093406689748402cd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sat, 3 Apr 2021 11:22:14 +0200 Subject: [PATCH 2/8] do not build vector if not needed --- .../include/CGAL/Polyhedral_envelope.h | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h index 16b3e554e76..4aa6b496347 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h @@ -321,6 +321,11 @@ public: * \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` * must be available in `TriangleMesh`.} * \cgalParamNEnd + * \cgalParamNBegin{face_epsilon_map} + * \cgalParamDescription{a property map associating to each face of `tm` a epsilon value} + * \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` + * as key type and `double` as value type} + * \cgalParamDefault{Use `epsilon` for all faces} * \cgalNamedParamsEnd * * \note The triangle mesh gets copied internally, that is it can be modifed after having passed as argument, @@ -367,7 +372,7 @@ public: } } if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) - init(std::vector(env_faces.size(), epsilon)); + init(epsilon); else { std::vector epsilon_values; @@ -385,6 +390,7 @@ public: for(face_descriptor f : faces(tmesh)) if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))) epsilon_values.push_back( get(epsilon_map, f) ); + init(epsilon_values); } } @@ -411,6 +417,12 @@ public: * \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` * must be available in `TriangleMesh`.} * \cgalParamNEnd + * \cgalParamNBegin{face_epsilon_map} + * \cgalParamDescription{a property map associating to each face of `tm` a epsilon value} + * \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` + * as key type and `double` as value type} + * \cgalParamDefault{Use `epsilon` for all faces} + * \cgalParamNEnd * \cgalNamedParamsEnd * * \note The triangle mesh gets copied internally, that is it can be modifed after having passed as argument, @@ -466,7 +478,7 @@ public: } if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) - init(std::vector(env_faces.size(), epsilon)); + init(epsilon); else { std::vector epsilon_values; @@ -484,6 +496,7 @@ public: for(face_descriptor f : face_range) if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))) epsilon_values.push_back( get(epsilon_map, f) ); + init(epsilon_values); } } @@ -541,7 +554,7 @@ public: Vector3i face = { int(t[0]), int(t[1]), int(t[2]) }; env_faces.emplace_back(face); } - init(std::vector(env_faces.size(), epsilon)); + init(epsilon); } /// @} @@ -572,7 +585,8 @@ public: private: - void init(const std::vector& epsilon_values) + template + void init(const Epsilons& epsilon_values) { halfspace_generation(env_vertices, env_faces, halfspace, bounding_boxes, epsilon_values); @@ -1812,12 +1826,22 @@ private: return Plane(plane0, plane1,plane2); } + double get_epsilon(double epsilon, std::size_t) + { + return epsilon; + } + + double get_epsilon(const std::vector& epsilon_values, std::size_t i) + { + return epsilon_values[i]; + } // build prisms for a list of triangles. each prism is represented by 7-8 planes, which are represented by 3 points + template void halfspace_generation(const std::vector &ver, const std::vector &faces, std::vector& halfspace, - std::vector& bounding_boxes, const std::vector& epsilon_values) + std::vector& bounding_boxes, const Epsilons& epsilon_values) { Vector_3 AB, AC, BC, normal; Plane plane; @@ -1844,7 +1868,7 @@ private: bounding_boxes.resize(faces.size()); for (unsigned int i = 0; i < faces.size(); ++i) { - const double epsilon = epsilon_values[i]; + const double epsilon = get_epsilon(epsilon_values,i); double tolerance = epsilon / std::sqrt(3);// the envelope thickness, to be conservative double bbox_tolerance = epsilon *(1 + 1e-6); From 2a88ca2082109c3c53f0fadf91a4faec760d599c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 7 Apr 2021 14:45:57 +0200 Subject: [PATCH 3/8] test new named param --- BGL/test/BGL/test_cgal_bgl_named_params.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index 277e4a1153d..e1adcb3a563 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -109,6 +109,7 @@ void test(const NamedParameters& np) assert(get_parameter(np, CGAL::internal_np::maximum_number_of_faces).v == 78910); assert(get_parameter(np, CGAL::internal_np::non_manifold_feature_map).v == 60); assert(get_parameter(np, CGAL::internal_np::filter).v == 61); + assert(get_parameter(np, CGAL::internal_np::face_epsilon_map).v == 62); // Named parameters that we use in the package 'Surface Mesh Simplification' assert(get_parameter(np, CGAL::internal_np::get_cost_policy).v == 34); @@ -225,6 +226,7 @@ void test(const NamedParameters& np) check_same_type<78910>(get_parameter(np, CGAL::internal_np::maximum_number_of_faces)); check_same_type<60>(get_parameter(np, CGAL::internal_np::non_manifold_feature_map)); check_same_type<61>(get_parameter(np, CGAL::internal_np::filter)); + check_same_type<62>(get_parameter(np, CGAL::internal_np::face_epsilon_map)); // Named parameters that we use in the package 'Surface Mesh Simplification' check_same_type<34>(get_parameter(np, CGAL::internal_np::get_cost_policy)); @@ -353,6 +355,7 @@ int main() .use_compact_clipper(A<45>(45)) .non_manifold_feature_map(A<60>(60)) .filter(A<61>(61)) + .face_epsilon_map(A<62>(62)) .apply_per_connected_component(A<46>(46)) .output_iterator(A<47>(47)) .erase_all_duplicates(A<48>(48)) From 5b554abce81027d2c2c189804933f96f6fc34682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 7 Apr 2021 14:52:51 +0200 Subject: [PATCH 4/8] add named parameter for triangle soup --- .../include/CGAL/Polyhedral_envelope.h | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h index 4aa6b496347..153e7c518f5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h @@ -521,6 +521,10 @@ public: * \cgalParamType{a model of `ReadablePropertyMap` whose value type is `Point_3` and whose key * is the value type of `PointRange::const_iterator`} * \cgalParamDefault{`CGAL::Identity_property_map`} + * \cgalParamNBegin{face_epsilon_map} + * \cgalParamDescription{a property map associating to each triangle a epsilon value} + * \cgalParamType{a class model of `ReadablePropertyMap` with `std::size_t` as key type and `double` as value type} + * \cgalParamDefault{Use `epsilon` for all triangles} * \cgalParamNEnd * \cgalNamedParamsEnd * @@ -537,6 +541,7 @@ public: { using parameters::choose_parameter; using parameters::get_parameter; + using parameters::is_default_parameter; typedef typename CGAL::GetPointMap::const_type Point_map; Point_map pm = choose_parameter(get_parameter(np, internal_np::point_map)); @@ -554,7 +559,27 @@ public: Vector3i face = { int(t[0]), int(t[1]), int(t[2]) }; env_faces.emplace_back(face); } - init(epsilon); + + if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) + init(epsilon); + else + { + std::vector epsilon_values; + epsilon_values.reserve(env_faces.size()); + + typedef typename internal_np::Lookup_named_param_def< + internal_np::face_epsilon_map_t, + NamedParameters, + Constant_property_map + > ::type Epsilon_map; + + Epsilon_map epsilon_map = choose_parameter(get_parameter(np, internal_np::face_epsilon_map), + Constant_property_map(epsilon)); + + for(std::size_t i=0; i Date: Wed, 7 Apr 2021 15:10:32 +0200 Subject: [PATCH 5/8] avoid an extra is-degenerate test --- .../include/CGAL/Polyhedral_envelope.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h index 153e7c518f5..2f1e3ed6152 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h @@ -360,6 +360,7 @@ public: } GeomTraits gt; + std::unordered_set deg_faces; for(face_descriptor f : faces(tmesh)){ if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))){ typename Graph_traits::halfedge_descriptor h = halfedge(f, tmesh); @@ -370,6 +371,8 @@ public: Vector3i face = { i, j, k }; env_faces.push_back(face); } + else + deg_faces.insert(f); } if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) init(epsilon); @@ -388,7 +391,7 @@ public: Constant_property_map(epsilon)); for(face_descriptor f : faces(tmesh)) - if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))) + if(deg_faces.count(f)==0) epsilon_values.push_back( get(epsilon_map, f) ); init(epsilon_values); } @@ -465,6 +468,7 @@ public: return insert_res.first->second; }; + std::unordered_set deg_faces; for(face_descriptor f : face_range){ if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))){ typename boost::graph_traits::halfedge_descriptor h = halfedge(f, tmesh); @@ -475,6 +479,8 @@ public: Vector3i face = { i, j, k }; env_faces.push_back(face); } + else + deg_faces.insert(f); } if (is_default_parameter(get_parameter(np, internal_np::face_epsilon_map))) @@ -494,7 +500,7 @@ public: Constant_property_map(epsilon)); for(face_descriptor f : face_range) - if(! Polygon_mesh_processing::is_degenerate_triangle_face(f, tmesh, parameters::geom_traits(gt).vertex_point_map(vpm))) + if(deg_faces.count(f)==0) epsilon_values.push_back( get(epsilon_map, f) ); init(epsilon_values); } From 667817ece04468632079683963bf1786df86c5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 7 Apr 2021 15:27:56 +0200 Subject: [PATCH 6/8] test new API --- .../test_pmp_polyhedral_envelope.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_polyhedral_envelope.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_polyhedral_envelope.cpp index d43b996c255..7127b22e3e0 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_polyhedral_envelope.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_polyhedral_envelope.cpp @@ -22,6 +22,7 @@ void test_API() CGAL::Polyhedron_3 poly; PMP::polygon_soup_to_polygon_mesh(points, triangles, sm); PMP::polygon_soup_to_polygon_mesh(points, triangles, poly); + auto epsilon_map = sm.add_property_map::Face_index, double>("f:em", 0.0001).first; // build from the same kernel { @@ -32,12 +33,23 @@ void test_API() assert(envelope(sm)); assert(envelope(poly)); assert(envelope(points, triangles)); + envelope = CGAL::Polyhedral_envelope(sm,0, CGAL::parameters::face_epsilon_map(epsilon_map)); + assert(!envelope.is_empty()); + assert(envelope(sm)); + assert(envelope(poly)); + assert(envelope(points, triangles)); envelope=CGAL::Polyhedral_envelope(points,triangles,0.0001); assert(!envelope.is_empty()); assert(envelope(sm)); assert(envelope(poly)); assert(envelope(points, triangles)); + std::vector eps(triangles.size(), 0.0001); + envelope=CGAL::Polyhedral_envelope(points,triangles,0,CGAL::parameters::face_epsilon_map(CGAL::make_property_map(eps))); + assert(!envelope.is_empty()); + assert(envelope(sm)); + assert(envelope(poly)); + assert(envelope(points, triangles)); std::vector subfaces(faces(sm).begin(), std::next(faces(sm).begin(), 150)); envelope=CGAL::Polyhedral_envelope(subfaces,sm,0.0001); @@ -45,6 +57,11 @@ void test_API() assert(!envelope(sm)); assert(!envelope(poly)); assert(!envelope(points, triangles)); + envelope=CGAL::Polyhedral_envelope(subfaces,sm,0, CGAL::parameters::face_epsilon_map(epsilon_map)); + assert(!envelope.is_empty()); + assert(!envelope(sm)); + assert(!envelope(poly)); + assert(!envelope(points, triangles)); } // build from different kernels From dfe70d17e7ca8ef258273e47482b3de0b85202d3 Mon Sep 17 00:00:00 2001 From: Sebastien Loriot Date: Thu, 8 Apr 2021 08:56:54 +0200 Subject: [PATCH 7/8] Fix typo --- Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h index 2f1e3ed6152..39e197da225 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h @@ -322,7 +322,7 @@ public: * must be available in `TriangleMesh`.} * \cgalParamNEnd * \cgalParamNBegin{face_epsilon_map} - * \cgalParamDescription{a property map associating to each face of `tm` a epsilon value} + * \cgalParamDescription{a property map associating to each face of `tm` an epsilon value} * \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` * as key type and `double` as value type} * \cgalParamDefault{Use `epsilon` for all faces} @@ -421,7 +421,7 @@ public: * must be available in `TriangleMesh`.} * \cgalParamNEnd * \cgalParamNBegin{face_epsilon_map} - * \cgalParamDescription{a property map associating to each face of `tm` a epsilon value} + * \cgalParamDescription{a property map associating to each face of `tm` an epsilon value} * \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` * as key type and `double` as value type} * \cgalParamDefault{Use `epsilon` for all faces} @@ -528,7 +528,7 @@ public: * is the value type of `PointRange::const_iterator`} * \cgalParamDefault{`CGAL::Identity_property_map`} * \cgalParamNBegin{face_epsilon_map} - * \cgalParamDescription{a property map associating to each triangle a epsilon value} + * \cgalParamDescription{a property map associating to each triangle an epsilon value} * \cgalParamType{a class model of `ReadablePropertyMap` with `std::size_t` as key type and `double` as value type} * \cgalParamDefault{Use `epsilon` for all triangles} * \cgalParamNEnd From 0e6754b15e98114f8feb26362c86ddaea94faabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sat, 10 Apr 2021 09:19:46 +0200 Subject: [PATCH 8/8] typos --- Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h index 39e197da225..42979608d69 100644 --- a/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h +++ b/Polygon_mesh_processing/include/CGAL/Polyhedral_envelope.h @@ -183,7 +183,7 @@ private: // The class `Plane` is used for the 7-8 walls of a prism. - // We store at the same time threee points and a plane. + // We store at the same time three points and a plane. // That is easier than retrieving the 3 points of a lazy plane. struct Plane { Plane()