From 4f6d249c1f86f50c1369787c5f6665a80a0211e2 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 23 Mar 2023 09:58:20 +0100 Subject: [PATCH 001/520] Eliminate heap allocated `Data *m_data` This required changing return types to `Node *` in many cases. All unit tests have been updated to account for the new interface. --- Orthtree/include/CGAL/Octree.h | 10 +- Orthtree/include/CGAL/Orthtree.h | 273 +++++++++--------- Orthtree/include/CGAL/Orthtree/Node.h | 261 +++++++++-------- .../CGAL/Orthtree/Traversal_iterator.h | 10 +- Orthtree/include/CGAL/Orthtree/Traversals.h | 135 ++++----- Orthtree/test/Orthtree/test_node_adjacent.cpp | 43 +-- Orthtree/test/Orthtree/test_octree_grade.cpp | 4 +- .../Orthtree/test_octree_intersecting.cpp | 32 +- .../Orthtree/test_octree_nearest_neighbor.cpp | 10 +- Orthtree/test/Orthtree/test_octree_refine.cpp | 10 +- 10 files changed, 387 insertions(+), 401 deletions(-) diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index bd7e50b3b881..848b2731c730 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -35,11 +35,13 @@ namespace CGAL { \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` */ -template ::value_type> > +template < + typename GeomTraits, + typename PointRange, + typename PointMap = Identity_property_map::value_type> +> #ifdef DOXYGEN_RUNNING -class Octree; + class Octree; #else using Octree = Orthtree, PointRange, PointMap>; #endif diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index afc958b97547..43b42a53782b 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -40,7 +40,8 @@ #include #include -namespace CGAL { +namespace CGAL +{ /*! \ingroup PkgOrthtreeClasses @@ -63,8 +64,8 @@ namespace CGAL { \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap_` \tparam PointMap_ must be a model of `ReadablePropertyMap` whose value type is `Traits_::Point_d` */ -template > +template > class Orthtree { @@ -88,9 +89,9 @@ class Orthtree /// \cond SKIP_IN_MANUAL typedef typename Traits::Array Array; ///< Array type. typedef typename Traits::Construct_point_d_from_array - Construct_point_d_from_array; + Construct_point_d_from_array; typedef typename Traits::Construct_bbox_d - Construct_bbox_d; + Construct_bbox_d; /// \endcond /// @} @@ -105,7 +106,7 @@ class Orthtree /*! * \brief Degree of the tree (number of children of non-leaf nodes). */ - typedef Dimension_tag<(2 << (Dimension::value-1))> Degree; + typedef Dimension_tag<(2 << (Dimension::value - 1))> Degree; /*! * \brief The Sub-tree / Orthant type. @@ -123,7 +124,7 @@ class Orthtree #ifdef DOXYGEN_RUNNING typedef unspecified_type Node_range; #else - typedef boost::iterator_range > Node_range; + typedef boost::iterator_range> Node_range; #endif /// \cond SKIP_IN_MANUAL @@ -131,7 +132,7 @@ class Orthtree /*! * \brief A function that determines the next node in a traversal given the current one. */ - typedef std::function Node_traversal_method_const; + typedef std::function Node_traversal_method_const; /// \endcond @@ -193,48 +194,48 @@ class Orthtree PointMap point_map = PointMap(), const FT enlarge_ratio = 1.2, Traits traits = Traits()) - : m_traits (traits) - , m_range (point_range) - , m_point_map (point_map) - , m_root(Node(), 0) + : m_traits(traits) + , m_range(point_range) + , m_point_map(point_map) + , m_root() // todo: can this be default-constructed? { Array bbox_min; Array bbox_max; // init bbox with first values found { - const Point& p = get (m_point_map, *(point_range.begin())); + const Point& p = get(m_point_map, *(point_range.begin())); std::size_t i = 0; - for (const FT& x : cartesian_range(p)) + for (const FT& x: cartesian_range(p)) { bbox_min[i] = x; bbox_max[i] = x; - ++ i; + ++i; } } - for (const Range_type& r : point_range) + for (const Range_type& r: point_range) { - const Point& p = get (m_point_map, r); + const Point& p = get(m_point_map, r); std::size_t i = 0; - for (const FT& x : cartesian_range(p)) + for (const FT& x: cartesian_range(p)) { bbox_min[i] = (std::min)(x, bbox_min[i]); bbox_max[i] = (std::max)(x, bbox_max[i]); - ++ i; + ++i; } } Array bbox_centroid; FT max_length = FT(0); - for (std::size_t i = 0 ; i < Dimension::value; ++ i) + for (std::size_t i = 0; i < Dimension::value; ++i) { bbox_centroid[i] = (bbox_min[i] + bbox_max[i]) / FT(2); max_length = (std::max)(max_length, bbox_max[i] - bbox_min[i]); } max_length *= enlarge_ratio / FT(2); - for (std::size_t i = 0 ; i < Dimension::value; ++ i) + for (std::size_t i = 0; i < Dimension::value; ++i) { bbox_min[i] = bbox_centroid[i] - max_length; bbox_max[i] = bbox_centroid[i] + max_length; @@ -254,47 +255,31 @@ class Orthtree /// \cond SKIP_IN_MANUAL // copy constructor - Orthtree (const Orthtree& other) - : m_traits (other.m_traits) - , m_range (other.m_range) - , m_point_map (other.m_point_map) - , m_root (other.m_root.deep_copy()) - , m_bbox_min (other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) - { } + Orthtree(const Orthtree& other) + : m_traits(other.m_traits) + , m_range(other.m_range) + , m_point_map(other.m_point_map) + , m_root(other.m_root.deep_copy()) + , m_bbox_min(other.m_bbox_min) + , m_side_per_depth(other.m_side_per_depth) {} // move constructor - Orthtree (Orthtree&& other) - : m_traits (other.m_traits) - , m_range (other.m_range) - , m_point_map (other.m_point_map) - , m_root (other.m_root) - , m_bbox_min (other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) - { - other.m_root = Node(Node(), 0); + Orthtree(Orthtree&& other) + : m_traits(other.m_traits) + , m_range(other.m_range) + , m_point_map(other.m_point_map) + , m_root(other.m_root) + , m_bbox_min(other.m_bbox_min) + , m_side_per_depth(other.m_side_per_depth) { + other.m_root = Node{}; } // Non-necessary but just to be clear on the rule of 5: // assignment operators deleted (PointRange is a ref) - Orthtree& operator= (const Orthtree& other) = delete; - Orthtree& operator= (Orthtree&& other) = delete; - // Destructor - ~Orthtree() - { - std::queue nodes; - nodes.push(m_root); - while (!nodes.empty()) - { - Node node = nodes.front(); - nodes.pop(); - if (!node.is_leaf()) - for (std::size_t i = 0; i < Degree::value; ++ i) - nodes.push (node[i]); - node.free(); - } - } + Orthtree& operator=(const Orthtree& other) = delete; + + Orthtree& operator=(Orthtree&& other) = delete; // move constructor /// \endcond @@ -320,52 +305,39 @@ class Orthtree void refine(const Split_predicate& split_predicate) { // If the tree has already been refined, reset it - if (!m_root.is_leaf()){ - std::queue nodes; - for (std::size_t i = 0; i < Degree::value; ++ i) - nodes.push (m_root[i]); - while (!nodes.empty()) - { - Node node = nodes.front(); - nodes.pop(); - if (!node.is_leaf()) - for (std::size_t i = 0; i < Degree::value; ++ i) - nodes.push (node[i]); - node.free(); - } + if (!m_root.is_leaf()) m_root.unsplit(); - } // Reset the side length map, too m_side_per_depth.resize(1); // Initialize a queue of nodes that need to be refined - std::queue todo; - todo.push(m_root); + std::queue todo; + todo.push(&m_root); // Process items in the queue until it's consumed fully while (!todo.empty()) { // Get the next element - Node current = todo.front(); + auto current = todo.front(); todo.pop(); // Check if this node needs to be processed - if (split_predicate(current)) { + if (split_predicate(*current)) { // Check if we've reached a new max depth - if (current.depth() == depth()) { + if (current->depth() == depth()) { // Update the side length map m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); } // Split the node, redistributing its points to its children - split(current); + split((*current)); // Process each of its children for (int i = 0; i < Degree::value; ++i) - todo.push(current[i]); + todo.push(&(*current)[i]); } } @@ -401,52 +373,52 @@ class Orthtree void grade() { // Collect all the leaf nodes - std::queue leaf_nodes; - for (Node leaf : traverse(Orthtrees::Leaves_traversal())) { + std::queue leaf_nodes; + for (const Node &leaf : traverse(Orthtrees::Leaves_traversal())) { // TODO: I'd like to find a better (safer) way of doing this - leaf_nodes.push(leaf); + leaf_nodes.push(const_cast(&leaf)); } // Iterate over the nodes while (!leaf_nodes.empty()) { // Get the next node - Node node = leaf_nodes.front(); + Node *node = leaf_nodes.front(); leaf_nodes.pop(); // Skip this node if it isn't a leaf anymore - if (!node.is_leaf()) + if (!node->is_leaf()) continue; // Iterate over each of the neighbors for (int direction = 0; direction < 6; ++direction) { // Get the neighbor - Node neighbor = node.adjacent_node(direction); + auto *neighbor = node->adjacent_node(direction); // If it doesn't exist, skip it - if (neighbor.is_null()) + if (!neighbor) continue; // Skip if this neighbor is a direct sibling (it's guaranteed to be the same depth) // TODO: This check might be redundant, if it doesn't affect performance maybe I could remove it - if (neighbor.parent() == node.parent()) + if (neighbor->parent() == node->parent()) continue; // If it's already been split, skip it - if (!neighbor.is_leaf()) + if (!neighbor->is_leaf()) continue; // Check if the neighbor breaks our grading rule // TODO: could the rule be parametrized? - if ((node.depth() - neighbor.depth()) > 1) { + if ((node->depth() - neighbor->depth()) > 1) { // Split the neighbor - split(neighbor); + split(*neighbor); // Add newly created children to the queue for (int i = 0; i < Degree::value; ++i) { - leaf_nodes.push(neighbor[i]); + leaf_nodes.push(&(*neighbor)[i]); } } } @@ -459,9 +431,22 @@ class Orthtree /// @{ /*! - \brief returns the root node. + \brief provides read-only access to the root node, and by + extension the rest of the tree. + + \return a const reference to the root node of the tree. + */ + const Node &root() const { return m_root; } + + /*! + \brief provides read-write access to the root node, and by + extension the rest of the tree. + + todo: why wasn't this provided previously? + + \return a reference to the root node of the tree. */ - Node root() const { return m_root; } + Node &root() { return m_root; } /*! \brief Convenience function to access the child nodes of the root @@ -472,9 +457,9 @@ class Orthtree \sa `Node::operator[]()` \param index the index of the child node. - \return the accessed node. + \return a reference to the node. */ - Node operator[](std::size_t index) const { return m_root[index]; } + const Node &operator[](std::size_t index) const { return m_root[index]; } /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). @@ -497,13 +482,13 @@ class Orthtree template Node_range traverse(const Traversal &traversal = Traversal()) const { - Node first = traversal.first(m_root); + const Node *first = traversal.first(&m_root); Node_traversal_method_const next - = [&](const Node& n) -> Node { return traversal.next(n); }; + = [&](const Node* n) -> const Node * { return traversal.next(n); }; - return boost::make_iterator_range(Traversal_iterator(first, next), - Traversal_iterator()); + return boost::make_iterator_range(Traversal_iterator(first, next), + Traversal_iterator()); } /*! @@ -547,19 +532,19 @@ class Orthtree \param point query point. \return the node which contains the point. */ - Node locate(const Point &point) const { + const Node& locate(const Point &point) const { // Make sure the point is enclosed by the orthtree CGAL_precondition (CGAL::do_intersect(point, bbox(m_root))); // Start at the root node - auto node_for_point = m_root; + auto *node_for_point = &m_root; // Descend the tree until reaching a leaf node - while (!node_for_point.is_leaf()) { + while (!node_for_point->is_leaf()) { // Find the point to split around - Point center = barycenter(node_for_point); + Point center = barycenter(*node_for_point); // Find the index of the correct sub-node typename Node::Local_coordinates index; @@ -568,11 +553,11 @@ class Orthtree index[dimension ++] = (get<0>(r) < get<1>(r)); // Find the correct sub-node of the current node - node_for_point = node_for_point[index.to_ulong()]; + node_for_point = &(*node_for_point)[index.to_ulong()]; } // Return the result - return node_for_point; + return *node_for_point; } /*! @@ -582,15 +567,15 @@ class Orthtree `query`. \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. - \param query a query point. - \param k the number of neighbors. - \param output the output iterator. + \param query query point. + \param k number of neighbors. + \param output output iterator. */ - template - OutputIterator nearest_neighbors (const Point& query, - std::size_t k, - OutputIterator output) const { - Sphere query_sphere (query, (std::numeric_limits::max)()); + template + OutputIterator nearest_neighbors(const Point& query, + std::size_t k, + OutputIterator output) const { + Sphere query_sphere(query, (std::numeric_limits::max)()); return nearest_k_neighbors_in_radius(query_sphere, k, output); } @@ -604,8 +589,8 @@ class Orthtree \param query query sphere. \param output output iterator. */ - template - OutputIterator nearest_neighbors (const Sphere& query, OutputIterator output) const { + template + OutputIterator nearest_neighbors(const Sphere& query, OutputIterator output) const { Sphere query_sphere = query; return nearest_k_neighbors_in_radius(query_sphere, (std::numeric_limits::max)(), output); @@ -625,7 +610,7 @@ class Orthtree \param output output iterator. */ template - OutputIterator intersected_nodes (const Query& query, OutputIterator output) const { + OutputIterator intersected_nodes(const Query& query, OutputIterator output) const { return intersected_nodes_recursive(query, root(), output); } @@ -666,7 +651,7 @@ class Orthtree // TODO: Document this // TODO: Could this method name be reduced to just "center" ? - Point barycenter(const Node& node) const { + Point barycenter(const Node &node) const { // Determine the side length of this node FT size = m_side_per_depth[node.depth()]; @@ -677,7 +662,7 @@ class Orthtree for (const FT& f : cartesian_range(m_bbox_min)) { bary[i] = FT(node.global_coordinates()[i]) * size + size / FT(2) + f; - ++ i; + ++i; } // Convert that location into a point @@ -703,11 +688,12 @@ class Orthtree // Split the point collection around the center point on this dimension Range_iterator split_point = std::partition (begin, end, - [&](const Range_type &a) -> bool { - // This should be done with cartesian iterator but it seems - // complicated to do efficiently + [&](const Range_type& a) -> bool + { + // This should be done with cartesian iterator but it seems + // complicated to do efficiently return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); - }); + }); // Further subdivide the first side of the split std::bitset coord_left = coord; @@ -721,7 +707,7 @@ class Orthtree } - void split(Node& node) { + void split(Node &node) { // Make sure the node hasn't already been split CGAL_precondition (node.is_leaf()); @@ -755,18 +741,19 @@ class Orthtree Point point; FT distance; }; - struct Node_index_with_distance { + + struct Node_index_with_distance + { typename Node::Local_coordinates index; FT distance; - Node_index_with_distance (const typename Node::Local_coordinates& index, - const FT& distance) - : index(index), distance(distance) - { } + Node_index_with_distance(const typename Node::Local_coordinates& index, + const FT& distance) + : index(index), distance(distance) {} }; - void nearest_k_neighbors_recursive(Sphere& search_bounds, const Node &node, - std::vector &results, FT epsilon = 0) const { + void nearest_k_neighbors_recursive(Sphere &search_bounds, const Node& node, + std::vector& results, FT epsilon = 0) const { // Check whether the node has children if (node.is_leaf()) { @@ -820,7 +807,7 @@ class Orthtree // Fill the list with child nodes for (int index = 0; index < Degree::value; ++index) { - Node child_node = node[index]; + auto &child_node = node[index]; // Add a child to the list, with its distance children_with_distances.emplace_back(typename Node::Local_coordinates(index), @@ -834,7 +821,7 @@ class Orthtree // Loop over the children for (auto child_with_distance : children_with_distances) { - Node child_node = node[child_with_distance.index.to_ulong()]; + auto &child_node = node[child_with_distance.index.to_ulong()]; // Check whether the bounding box of the child intersects with the search bounds if (do_intersect(child_node, search_bounds)) { @@ -853,9 +840,9 @@ class Orthtree // Check if the current node intersects with the query if (CGAL::do_intersect(query, bbox(node))) { - // if this node is a leaf, than it's considered an intersecting node + // if this node is a leaf, then it's considered an intersecting node if (node.is_leaf()) { - *output++ = node; + *output++ = &node; return output; } @@ -884,9 +871,9 @@ class Orthtree \param k the number of points to find \param output the output iterator to add the found points to (in order of increasing distance) */ - template + template OutputIterator nearest_k_neighbors_in_radius - (Sphere& query_sphere, + (Sphere &query_sphere, std::size_t k, OutputIterator output) const { // Create an empty list of points @@ -907,18 +894,16 @@ class Orthtree public: /// \cond SKIP_IN_MANUAL - void dump_to_polylines (std::ostream& os) const - { - for (const Node& n : traverse()) + void dump_to_polylines(std::ostream& os) const { + for (const Node& n: traverse()) if (n.is_leaf()) { Bbox box = bbox(n); - dump_box_to_polylines (box, os); + dump_box_to_polylines(box, os); } } - void dump_box_to_polylines (const Bbox_2& box, std::ostream& os) const - { + void dump_box_to_polylines(const Bbox_2& box, std::ostream& os) const { // dump in 3D for visualisation os << "5 " << box.xmin() << " " << box.ymin() << " 0 " @@ -927,8 +912,8 @@ class Orthtree << box.xmax() << " " << box.ymin() << " 0 " << box.xmin() << " " << box.ymin() << " 0" << std::endl; } - void dump_box_to_polylines (const Bbox_3& box, std::ostream& os) const - { + + void dump_box_to_polylines(const Bbox_3& box, std::ostream& os) const { // Back face os << "5 " << box.xmin() << " " << box.ymin() << " " << box.zmin() << " " @@ -960,12 +945,12 @@ class Orthtree << box.xmax() << " " << box.ymax() << " " << box.zmax() << std::endl; } - friend std::ostream& operator<< (std::ostream& os, const Self& orthtree) - { + friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { // Create a range of nodes auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal()); // Iterate over the range - for (auto &n : nodes) { + for (auto& n: nodes) + { // Show the depth for (int i = 0; i < n.depth(); ++i) os << ". "; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index a16d2fff1928..42f439b5598f 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -27,42 +27,20 @@ namespace CGAL { /// \cond SKIP_IN_MANUAL -namespace Orthtrees -{ +namespace Orthtrees { // Non-documented, or testing purpose only -struct Node_access -{ +struct Node_access { template - static Node create_node (Node parent, LC local_coordinates) - { + static Node create_node(Node* parent, LC local_coordinates) { return Node(parent, local_coordinates); } template - static typename Node::Point_range& points(Node node) { return node.points(); } + static typename Node::Point_range& points(Node& node) { return node.points(); } template - static void split(Node node) { return node.split(); } - - template - static void free(Node node) - { - typedef Dimension_tag<(2 << (Node::Dimension::value - 1))> Degree; - std::queue nodes; - nodes.push(node); - while (!nodes.empty()) - { - Node node = nodes.front(); - nodes.pop(); - if (!node.is_leaf()){ - for (std::size_t i = 0; i < Degree::value; ++ i){ - nodes.push (node[i]); - } - } - node.free(); - } - } + static void split(Node& node) { return node.split(); } }; @@ -79,9 +57,8 @@ struct Node_access \cgalModels `ConstRange` */ -template -class Orthtree::Node -{ +template +class Orthtree::Node { public: @@ -144,20 +121,25 @@ class Orthtree::Node typedef boost::iterator_range Point_range; /// \endcond - // make Node trivially copiabled - struct Data - { - Point_range points; - Self parent; - std::uint8_t depth; - Global_coordinates global_coordinates; - std::unique_ptr children; - - Data (Self parent) - : parent (parent), depth (0) { } - }; + // make Node trivially copiable +// struct Data +// { +// Point_range points; +// Self parent; +// std::uint8_t depth; +// Global_coordinates global_coordinates; +// std::unique_ptr children; +// +// Data (Self parent) +// : parent (parent), depth (0) { } +// }; +// Data* m_data; - Data* m_data; + Point_range m_points; + Self* m_parent; // todo: use optional> instead of Self * + std::uint8_t m_depth; + Global_coordinates m_global_coordinates; + std::shared_ptr m_children; /// \cond SKIP_IN_MANUAL @@ -173,8 +155,9 @@ class Orthtree::Node * \brief Access to the content held by this node * \return a reference to the collection of point indices */ - Point_range &points() { return m_data->points; } - const Point_range &points() const { return m_data->points; } + Point_range& points() { return m_points; } + + const Point_range& points() const { return m_points; } /// \name Construction /// @{ @@ -194,42 +177,38 @@ class Orthtree::Node \param parent the node containing this one \param index this node's relationship to its parent */ - explicit Node(Self parent, Local_coordinates local_coordinates) - : m_data (new Data(parent)) { - - if (!parent.is_null()) { + explicit Node(Self* parent, Local_coordinates local_coordinates) + : m_parent(parent) { - m_data->depth = parent.m_data->depth + 1; + if (parent != nullptr) { + m_depth = parent->m_depth + 1; for (int i = 0; i < Dimension::value; i++) - m_data->global_coordinates[i] = (2 * parent.m_data->global_coordinates[i]) + local_coordinates[i]; + m_global_coordinates[i] = (2 * parent->m_global_coordinates[i]) + local_coordinates[i]; + + } else { + m_depth = 0; - } - else for (int i = 0; i < Dimension::value; i++) - m_data->global_coordinates[i] = 0; + m_global_coordinates[i] = 0; + } } - void free() { delete m_data; } - - Node deep_copy(Self parent = Node()) const - { - if (is_null()) - return Node(); + Node deep_copy(Self parent = Node()) const { Node out; - out.m_data = new Data(parent); - - out.m_data->points = m_data->points; - out.m_data->depth = m_data->depth; - out.m_data->global_coordinates = m_data->global_coordinates; - std::unique_ptr children; - if (!is_leaf()) - { - out.m_data->children = std::make_unique(); + + out.m_parent = m_parent; + out.m_points = m_points; + out.m_depth = m_depth; + out.m_global_coordinates = m_global_coordinates; + + if (!is_leaf()) { + out.m_children = std::make_shared(); for (int index = 0; index < Degree::value; index++) - (*out.m_data->children)[index] = (*this)[index].deep_copy(out); + (*out.m_children)[index] = (*m_children)[index].deep_copy(out); } + return out; } @@ -251,10 +230,10 @@ class Orthtree::Node CGAL_precondition (is_leaf()); - m_data->children = std::make_unique(); + m_children = std::make_shared(); for (int index = 0; index < Degree::value; index++) { - (*m_data->children)[index] = std::move(Self(*this, {Local_coordinates(index)})); + (*m_children)[index] = std::move(Self(this, {Local_coordinates(index)})); } } @@ -267,7 +246,7 @@ class Orthtree::Node */ void unsplit() { - m_data->children.reset(); + m_children.reset(); } /// @} @@ -279,10 +258,12 @@ class Orthtree::Node /// \cond SKIP_IN_MANUAL // Default creates null node - Node() : m_data(nullptr) { } + // todo: default node is no longer null, but still isn't guaranteed to be valid + Node() = default; // Comparison operator - bool operator< (const Node& other) const { return m_data < other.m_data; } + // todo: where is this used + //bool operator<(const Node& other) const { return m_data < other.m_data; } /// \endcond /// \name Type & Location @@ -291,36 +272,30 @@ class Orthtree::Node /*! \brief returns `true` if the node is null, `false` otherwise. */ - bool is_null() const { return (m_data == nullptr); } + //bool is_null() const { return (m_data == nullptr); } /*! \brief returns `true` if the node has no parent, `false` otherwise. \pre `!is_null()` */ - bool is_root() const - { - CGAL_precondition(!is_null()); - return m_data->parent.is_null(); + bool is_root() const { + return m_parent == nullptr; } /*! \brief returns `true` if the node has no children, `false` otherwise. \pre `!is_null()` */ - bool is_leaf() const - { - CGAL_precondition(!is_null()); - return (!m_data->children); + bool is_leaf() const { + return (!m_children); } /*! \brief returns this node's depth. \pre `!is_null()` */ - std::uint8_t depth() const - { - CGAL_precondition (!is_null()); - return m_data->depth; + std::uint8_t depth() const { + return m_depth; } /*! @@ -329,12 +304,9 @@ class Orthtree::Node */ Local_coordinates local_coordinates() const { - CGAL_precondition (!is_null()); - // TODO: There must be a better way of doing this! - Local_coordinates result; - for (std::size_t i = 0; i < Dimension::value; ++ i) + for (std::size_t i = 0; i < Dimension::value; ++i) result[i] = global_coordinates()[i] & 1; return result; @@ -344,10 +316,8 @@ class Orthtree::Node \brief returns this node's global coordinates. \pre `!is_null()` */ - Global_coordinates global_coordinates() const - { - CGAL_precondition (!is_null()); - return m_data->global_coordinates; + Global_coordinates global_coordinates() const { + return m_global_coordinates; } @@ -356,18 +326,16 @@ class Orthtree::Node /*! \brief returns this node's parent. - \pre `!is_null()` - */ - Self parent() const - { - CGAL_precondition (!is_null()); - return m_data->parent; + \pre `!is_root()` + */ + const Self* parent() const { + CGAL_precondition (!is_root()); + return m_parent; } /*! \brief returns the nth child of this node. - \pre `!is_null()` \pre `!is_leaf()` \pre `0 <= index && index < Degree::value` @@ -418,13 +386,29 @@ class Orthtree::Node The operator can be chained. For example, `n[5][2][3]` returns the third child of the second child of the fifth child of a node `n`. */ - Self operator[](std::size_t index) const { + Self& operator[](std::size_t index) { - CGAL_precondition (!is_null()); CGAL_precondition (!is_leaf()); CGAL_precondition (index < Degree::value); - return (*m_data->children)[index]; + return (*m_children)[index]; + } + + /*! + \brief returns the nth child of this node. + + \pre `!is_leaf()` + \pre `index < Degree::value` + + The operator can be chained. For example, `n[5][2][3]` returns the + third child of the second child of the fifth child of a node `n`. + */ + const Self& operator[](std::size_t index) const { + + CGAL_precondition (!is_leaf()); + CGAL_precondition (index < Degree::value); + + return (*m_children)[index]; } /*! @@ -478,9 +462,7 @@ class Orthtree::Node \return the adjacent node if it exists, a null node otherwise. */ - Self adjacent_node (Local_coordinates direction) const - { - CGAL_precondition(!is_null()); + const Self* adjacent_node(Local_coordinates direction) const { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 @@ -489,8 +471,7 @@ class Orthtree::Node CGAL_precondition(direction.to_ulong() < Dimension::value * 2); // The root node has no adjacent nodes! - if (is_root()) - return Self(); + if (is_root()) return nullptr; // The least significant bit indicates the sign (which side of the node) bool sign = direction[0]; @@ -506,24 +487,22 @@ class Orthtree::Node // Check if this child has the opposite sign along the direction's axis if (local_coordinates()[dimension] != sign) { - // This means the adjacent node is a direct sibling, the offset can be applied easily! - return parent()[local_coordinates().to_ulong() + offset]; + return &(*parent())[local_coordinates().to_ulong() + offset]; } - // Find the parent's neighbor in that direction if it exists - Self adjacent_node_of_parent = parent().adjacent_node(direction); + // Find the parent's neighbor in that direction, if it exists + const Self* adjacent_node_of_parent = parent()->adjacent_node(direction); // If the parent has no neighbor, then this node doesn't have one - if (adjacent_node_of_parent.is_null()) - return Node(); + if (!adjacent_node_of_parent) return nullptr; // If the parent's adjacent node has no children, then it's this node's adjacent node - if (adjacent_node_of_parent.is_leaf()) + if (adjacent_node_of_parent->is_leaf()) return adjacent_node_of_parent; // Return the nearest node of the parent by subtracting the offset instead of adding - return adjacent_node_of_parent[local_coordinates().to_ulong() - offset]; + return &(*adjacent_node_of_parent)[local_coordinates().to_ulong() - offset]; } @@ -531,7 +510,21 @@ class Orthtree::Node \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. */ - Self adjacent_node(Adjacency adjacency) const { + const Self* adjacent_node(Adjacency adjacency) const { + return adjacent_node(std::bitset(static_cast(adjacency))); + } + + /*! + * \brief equivalent to adjacent_node, except non-const + */ + Self* adjacent_node(std::bitset direction) { + return const_cast(const_cast(this)->adjacent_node(direction)); + } + + /*! + * \brief equivalent to adjacent_node, with a Direction rather than a bitset and non-const + */ + Self* adjacent_node(Adjacency adjacency) { return adjacent_node(std::bitset(static_cast(adjacency))); } @@ -544,27 +537,27 @@ class Orthtree::Node \brief checks whether the node is empty of points or not. */ bool empty() const { - return m_data->points.empty(); + return m_points.empty(); } /*! \brief returns the number of points of this node. */ std::size_t size() const { - return std::size_t(std::distance(m_data->points.begin(), m_data->points.end())); + return std::size_t(std::distance(m_points.begin(), m_points.end())); } /*! \brief returns the iterator at the start of the collection of points held by this node. */ - const_iterator begin() const { return m_data->points.begin(); } + const_iterator begin() const { return m_points.begin(); } /*! \brief returns the iterator at the end of the collection of points held by this node. */ - const_iterator end() const { return m_data->points.end(); } + const_iterator end() const { return m_points.end(); } /// @} @@ -575,18 +568,23 @@ class Orthtree::Node /*! * \brief compares the topology of this node to another node. * - * \todo + * \todo This seems out of date, the implementation I see compares for direct equality * * \param rhs node to compare with * \return whether the nodes have different topology. */ - bool operator==(const Self &rhs) const { - return m_data == rhs.m_data; + bool operator==(const Self& rhs) const { + + // todo: This is a trivial implementation, maybe it can be set to =default in c++17? + return rhs.m_parent == m_parent && + rhs.m_children == m_children && + rhs.m_points == m_points && + rhs.m_depth == m_depth && + rhs.m_global_coordinates == m_global_coordinates; } - static bool is_topology_equal (const Self& a, const Self& b) - { - CGAL_assertion (!a.is_null() && !b.is_null()); + // todo: this does what the documentation for operator== claims to do! + static bool is_topology_equal(const Self& a, const Self& b) { // If one node is a leaf, and the other isn't, they're not the same if (a.is_leaf() != b.is_leaf()) @@ -607,8 +605,7 @@ class Orthtree::Node return (a.global_coordinates() == b.global_coordinates()); } - friend std::ostream& operator<< (std::ostream& os, const Self& node) - { + friend std::ostream& operator<<(std::ostream& os, const Self& node) { return internal::print_orthtree_node(os, node); } /// \endcond diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index d8a594e4ef03..6670b480cd60 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -44,7 +44,7 @@ class Traversal_iterator : * * \todo */ - typedef std::function Traversal_function; + typedef std::function Traversal_function; /// @} @@ -58,7 +58,7 @@ class Traversal_iterator : * * \todo */ - Traversal_iterator() : m_value(), m_next() {} + Traversal_iterator() : m_value(nullptr), m_next() {} /*! * \brief @@ -68,7 +68,7 @@ class Traversal_iterator : * \param first * \param next */ - Traversal_iterator(Value first, const Traversal_function &next) : m_value(first), m_next(next) {} + Traversal_iterator(Value *first, const Traversal_function &next) : m_value(first), m_next(next) {} /// @} @@ -84,12 +84,12 @@ class Traversal_iterator : } Value &dereference() const { - return const_cast(m_value); + return *m_value; } private: - Value m_value; + Value *m_value; Traversal_function m_next; }; } diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 885af4f9b4d9..b7de300b3c9d 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -24,7 +24,7 @@ namespace CGAL { /// \cond SKIP_IN_MANUAL // Forward declaration -template +template class Orthtree; /// \endcond @@ -33,86 +33,85 @@ namespace Orthtrees { /// \cond SKIP_IN_MANUAL template -Node next_sibling(Node n) { +const Node* next_sibling(const Node* n) { // Passing null returns the first node - if (n.is_null()) - return Node(); + if (nullptr == n) + return nullptr; // If this node has no parent, it has no siblings - if (n.parent().is_null()) - return Node(); + if (n->is_root()) + return nullptr; // Find out which child this is - std::size_t index = n.local_coordinates().to_ulong(); + std::size_t index = n->local_coordinates().to_ulong(); constexpr static int degree = Node::Degree::value; // Return null if this is the last child if (int(index) == degree - 1) - return Node(); + return nullptr; // Otherwise, return the next child - return n.parent()[index + 1]; + return &((*n->parent())[index + 1]); } template -Node next_sibling_up(Node n) { +const Node* next_sibling_up(const Node* n) { - if (n.is_null()) - return Node(); + if (!n || n->is_root()) return nullptr; - Node up = n.parent(); + auto up = n->parent(); + while (nullptr != up) { - while (!up.is_null()) { - - if (!next_sibling(up).is_null()) + if (nullptr != next_sibling(up)) return next_sibling(up); - up = up.parent(); + if (up->is_root()) return nullptr; + + up = up->parent(); } - return Node(); + return nullptr; } template -Node deepest_first_child(Node n) { +const Node* deepest_first_child(const Node* n) { - if (n.is_null()) - return Node(); + if (!n) + return nullptr; // Find the deepest child on the left - Node first = n; - while (!first.is_leaf()) - first = first[0]; + auto first = n; + while (!first->is_leaf()) + first = &(*first)[0]; return first; } template -Node first_child_at_depth(Node n, std::size_t depth) { +const Node& first_child_at_depth(const Node* n, std::size_t depth) { - if (n.is_null()) - return Node(); + if (!n) + return nullptr; - std::stack todo; + std::stack todo; todo.push(n); - if (n.depth() == depth) + if (n->depth() == depth) return n; - while (!todo.empty()) - { - Node node = todo.top(); + while (!todo.empty()) { + const Node* node = todo.top(); todo.pop(); - if (node.depth() == depth) + if (node->depth() == depth) return node; - if (!node.is_leaf()) - for (int i = 0; i < Node::Degree::value; ++ i) - todo.push(node[std::size_t(Node::Degree::value - 1 - i)]); + if (!node->is_leaf()) + for (int i = 0; i < Node::Degree::value; ++i) + todo.push(&((*node)[std::size_t(Node::Degree::value - 1 - i)])); } - return Node(); + return nullptr; } /// \endcond @@ -128,25 +127,30 @@ Node first_child_at_depth(Node n, std::size_t depth) { struct Preorder_traversal { template - Node first(Node root) const { + const Node* first(const Node* root) const { return root; } template - Node next(Node n) const { + const Node* next(const Node* n) const { - if (n.is_leaf()) { + if (n->is_leaf()) { - Node next = next_sibling(n); + auto next = next_sibling(n); + + if (nullptr == next) { - if (next.is_null()) return next_sibling_up(n); + } return next; + } else { + + // Return the first child of this node + return &(*n)[0]; } - else // Return the first child of this node - return n[0]; + } }; @@ -161,18 +165,18 @@ struct Preorder_traversal { struct Postorder_traversal { template - Node first(Node root) const { + const Node* first(const Node* root) const { return deepest_first_child(root); } template - Node next(Node n) const { + const Node* next(const Node* n) const { - Node next = deepest_first_child(next_sibling(n)); + auto next = deepest_first_child(next_sibling(n)); - if (next.is_null()) - next = n.parent(); + if (!next) + next = n->parent(); return next; } @@ -189,17 +193,17 @@ struct Postorder_traversal { struct Leaves_traversal { template - Node first(Node root) const { + const Node* first(const Node* root) const { return deepest_first_child(root); } template - Node next(Node n) const { + const Node* next(const Node* n) const { - Node next = deepest_first_child(next_sibling(n)); + auto next = deepest_first_child(next_sibling(n)); - if (next.is_null()) + if (!next) next = deepest_first_child(next_sibling_up(n)); return next; @@ -226,29 +230,28 @@ struct Level_traversal { /*! constructs a `depth`-level traversal. */ - Level_traversal (std::size_t depth) : depth(depth) { } + Level_traversal(std::size_t depth) : depth(depth) {} template - Node first(Node root) const { + const Node* first(const Node* root) const { return first_child_at_depth(root, depth); } template - Node next(Node n) const { + const Node* next(const Node* n) const { + // fixme: leftover from debugging? std::cerr << depth << " "; - Node next = next_sibling(n); + const Node* next = next_sibling(n); - if (next.is_null()) - { - Node up = n; - do - { + if (!next) { + const Node* up = n; + do { up = next_sibling_up(up); - if (up.is_null()) - return Node(); + if (!up) + return nullptr; + next = first_child_at_depth(up, depth); - } - while (next.is_null()); + } while (!next); } return next; diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 084d72dc5176..1d4e55535d05 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -10,7 +10,7 @@ typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_3 Point; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree -Octree; + Octree; typedef Octree::Node Node; typedef Octree::Traits Traits; @@ -42,33 +42,36 @@ int main(void) { std::cout << octree << std::endl; // Root node should have no siblings - assert(octree.root().adjacent_node(0).is_null()); - assert(octree.root().adjacent_node(1).is_null()); - assert(octree.root().adjacent_node(2).is_null()); - assert(octree.root().adjacent_node(3).is_null()); - assert(octree.root().adjacent_node(4).is_null()); - assert(octree.root().adjacent_node(5).is_null()); + assert(octree.root().adjacent_node(0) == nullptr); + assert(octree.root().adjacent_node(1) == nullptr); + assert(octree.root().adjacent_node(2) == nullptr); + assert(octree.root().adjacent_node(3) == nullptr); + assert(octree.root().adjacent_node(4) == nullptr); + assert(octree.root().adjacent_node(5) == nullptr); // Left Top Front node should have siblings to the Right, Down, and Back auto left_top_back = octree.root()[Traits::LEFT_TOP_BACK]; - assert(octree.root()[Traits::RIGHT_TOP_BACK] == left_top_back.adjacent_node(Traits::RIGHT)); - assert(octree.root()[Traits::LEFT_BOTTOM_BACK] == left_top_back.adjacent_node(Traits::DOWN)); - assert(octree.root()[Traits::LEFT_TOP_FRONT] == left_top_back.adjacent_node(Traits::FRONT)); - assert(left_top_back.adjacent_node(Traits::LEFT).is_null()); - assert(left_top_back.adjacent_node(Traits::UP).is_null()); - assert(left_top_back.adjacent_node(Traits::BACK).is_null()); + assert(&octree.root()[Traits::RIGHT_TOP_BACK] == left_top_back.adjacent_node(Traits::RIGHT)); + assert(&octree.root()[Traits::LEFT_BOTTOM_BACK] == left_top_back.adjacent_node(Traits::DOWN)); + assert(&octree.root()[Traits::LEFT_TOP_FRONT] == left_top_back.adjacent_node(Traits::FRONT)); + assert(left_top_back.adjacent_node(Traits::LEFT) == nullptr); + assert(left_top_back.adjacent_node(Traits::UP) == nullptr); + assert(left_top_back.adjacent_node(Traits::BACK) == nullptr); std::cout << octree.root()[Traits::LEFT_BOTTOM_BACK] << std::endl; auto right_top_back_of_left_bottom_back = octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::RIGHT_TOP_BACK]; - assert(octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::LEFT_TOP_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::LEFT)); - assert(octree.root()[Traits::RIGHT_BOTTOM_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT)); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT).is_null()); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::UP).is_null()); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::DOWN).is_null()); - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::BACK).is_null()); - assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::FRONT).is_null()); + assert(&octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::LEFT_TOP_BACK] == + right_top_back_of_left_bottom_back.adjacent_node(Traits::LEFT)); + assert(&octree.root()[Traits::RIGHT_BOTTOM_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT)); + assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT) != nullptr); + assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::UP) != nullptr); + assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::DOWN) != nullptr); + assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::FRONT) != nullptr); + + // A node at the back of the tree should have no neighbor to its back + assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::BACK) == nullptr); return 0; } diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index a0a574bd9eff..686068028fcd 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -27,10 +27,10 @@ std::size_t count_jumps(Octree &octree) { auto adjacent_node = node.adjacent_node(direction); - if (adjacent_node.is_null()) + if (adjacent_node == nullptr) continue; - if ((node.depth() - adjacent_node.depth()) > 1) + if ((node.depth() - adjacent_node->depth()) > 1) jumps++; } } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 59ce353fd28a..4d83929c2e0f 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -46,14 +46,14 @@ int main(void) { auto query = Point{1, 1, 1}; // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // A point should only intersect one node assert(1 == nodes.size()); // That node should be the node leaf that contains the point - assert(octree.locate(Point(1, 1, 1)) == nodes[0]); + assert(octree.locate(Point(1, 1, 1)) == *nodes[0]); } // Intersection with a sphere @@ -63,15 +63,15 @@ int main(void) { auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(4 == nodes.size()); - assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[0]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[1]); - assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[2]); - assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[3]); + assert(octree[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]); + assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]); + assert(octree[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]); + assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]); } // Intersection with a ray @@ -81,19 +81,19 @@ int main(void) { auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0}); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(8 == nodes.size()); - assert(octree[Octree::Traits::LEFT_BOTTOM_BACK] == nodes[0]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == nodes[1]); - assert(octree[Octree::Traits::LEFT_TOP_BACK] == nodes[2]); - assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[3]); - assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == nodes[4]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[5]); - assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[6]); - assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[7]); + assert(octree[Octree::Traits::LEFT_BOTTOM_BACK] == *nodes[0]); + assert(octree[Octree::Traits::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == *nodes[1]); + assert(octree[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]); + assert(octree[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]); + assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]); + assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]); + assert(octree[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]); + assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]); } return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index 8e87eaab152d..bec700dbc46b 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -20,7 +20,7 @@ typedef Kernel::Point_3 Point; typedef Kernel::FT FT; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree -Octree; + Octree; typedef CGAL::Search_traits_3 Kd_tree_traits; typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; @@ -47,7 +47,7 @@ void naive_vs_octree(std::size_t dataset_size) { { FT distance_nearest = (std::numeric_limits::max)(); - for (auto &p : points.points()) { + for (auto& p: points.points()) { FT distance_current = CGAL::squared_distance(p, random_point); if (distance_current < distance_nearest) { @@ -72,7 +72,6 @@ void naive_vs_octree(std::size_t dataset_size) { octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); { - // TODO: Write a nearest-neighbor implementation and use it here std::vector k_neighbors; octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); octree_nearest = *k_neighbors.begin(); @@ -109,9 +108,9 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { Kd_tree kd_tree(points.points().begin(), points.points().end()); kd_tree.build(); auto kd_tree_start_time = high_resolution_clock::now(); - Kd_tree_search search(kd_tree, random_point, (unsigned int)(K)); + Kd_tree_search search(kd_tree, random_point, (unsigned int) (K)); duration kd_tree_elapsed_time = high_resolution_clock::now() - kd_tree_start_time; - for (auto p : search) + for (auto p: search) kd_tree_nearest_neighbors.push_back(p.first); std::cout << "Kd_tree --> " @@ -143,6 +142,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { int main(void) { + naive_vs_octree(21); naive_vs_octree(500); naive_vs_octree(1000); naive_vs_octree(10000); diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index ff5177821790..82b0862e3275 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -25,12 +25,11 @@ void test_1_point() { octree.refine(10, 1); // Check that the topology matches - Node single_node = CGAL::Orthtrees::Node_access::create_node(Node(), 0); + Node single_node = CGAL::Orthtrees::Node_access::create_node(static_cast(nullptr), 0); CGAL::Orthtrees::Node_access::points(single_node) = CGAL::Orthtrees::Node_access::points(octree.root()); assert(Node::is_topology_equal(single_node, octree.root())); assert(0 == octree.depth()); - CGAL::Orthtrees::Node_access::free(single_node); } void test_2_points() { @@ -45,12 +44,10 @@ void test_2_points() { octree.refine(10, 1); // The octree should have been split once - Node other = CGAL::Orthtrees::Node_access::create_node(Node(), 0); + Node other = CGAL::Orthtrees::Node_access::create_node(static_cast(nullptr), 0); CGAL::Orthtrees::Node_access::split(other); assert(Node::is_topology_equal(other, octree.root())); assert(1 == octree.depth()); - CGAL::Orthtrees::Node_access::free(other); - } void test_4_points() { @@ -66,13 +63,12 @@ void test_4_points() { octree.refine(10, 1); // The octree should have been split once on the first level, and twice on the second - Node other = CGAL::Orthtrees::Node_access::create_node(Node(), 0); + Node other = CGAL::Orthtrees::Node_access::create_node(static_cast(nullptr), 0); CGAL::Orthtrees::Node_access::split(other); CGAL::Orthtrees::Node_access::split(other[3]); CGAL::Orthtrees::Node_access::split(other[7]); assert(Node::is_topology_equal(other, octree.root())); assert(2 == octree.depth()); - CGAL::Orthtrees::Node_access::free(other); } int main(void) { From 035db4854209f612794c4115d64265f78d7cb85f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 26 Mar 2023 16:50:06 +0200 Subject: [PATCH 002/520] Nodes can't split, unsplit, or copy themselves. This ensures allocating and deallocating nodes is strictly the responsibility of the orthtree object. --- Orthtree/include/CGAL/Orthtree.h | 189 ++++++++++-------- Orthtree/include/CGAL/Orthtree/Node.h | 127 ++---------- Orthtree/test/Orthtree/test_octree_refine.cpp | 23 +-- 3 files changed, 136 insertions(+), 203 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 43b42a53782b..0e3e9a187438 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -40,8 +40,7 @@ #include #include -namespace CGAL -{ +namespace CGAL { /*! \ingroup PkgOrthtreeClasses @@ -66,8 +65,7 @@ namespace CGAL */ template > -class Orthtree -{ +class Orthtree { public: @@ -132,7 +130,7 @@ class Orthtree /*! * \brief A function that determines the next node in a traversal given the current one. */ - typedef std::function Node_traversal_method_const; + typedef std::function Node_traversal_method_const; /// \endcond @@ -206,20 +204,17 @@ class Orthtree { const Point& p = get(m_point_map, *(point_range.begin())); std::size_t i = 0; - for (const FT& x: cartesian_range(p)) - { + for (const FT& x: cartesian_range(p)) { bbox_min[i] = x; bbox_max[i] = x; ++i; } } - for (const Range_type& r: point_range) - { + for (const Range_type& r: point_range) { const Point& p = get(m_point_map, r); std::size_t i = 0; - for (const FT& x: cartesian_range(p)) - { + for (const FT& x: cartesian_range(p)) { bbox_min[i] = (std::min)(x, bbox_min[i]); bbox_max[i] = (std::max)(x, bbox_max[i]); ++i; @@ -228,15 +223,13 @@ class Orthtree Array bbox_centroid; FT max_length = FT(0); - for (std::size_t i = 0; i < Dimension::value; ++i) - { + for (std::size_t i = 0; i < Dimension::value; ++i) { bbox_centroid[i] = (bbox_min[i] + bbox_max[i]) / FT(2); max_length = (std::max)(max_length, bbox_max[i] - bbox_min[i]); } max_length *= enlarge_ratio / FT(2); - for (std::size_t i = 0; i < Dimension::value; ++i) - { + for (std::size_t i = 0; i < Dimension::value; ++i) { bbox_min[i] = bbox_centroid[i] - max_length; bbox_max[i] = bbox_centroid[i] + max_length; } @@ -259,7 +252,7 @@ class Orthtree : m_traits(other.m_traits) , m_range(other.m_range) , m_point_map(other.m_point_map) - , m_root(other.m_root.deep_copy()) + , m_root(deep_copy(other.m_root)) , m_bbox_min(other.m_bbox_min) , m_side_per_depth(other.m_side_per_depth) {} @@ -306,13 +299,13 @@ class Orthtree // If the tree has already been refined, reset it if (!m_root.is_leaf()) - m_root.unsplit(); + unsplit(m_root); // Reset the side length map, too m_side_per_depth.resize(1); // Initialize a queue of nodes that need to be refined - std::queue todo; + std::queue todo; todo.push(&m_root); // Process items in the queue until it's consumed fully @@ -373,17 +366,17 @@ class Orthtree void grade() { // Collect all the leaf nodes - std::queue leaf_nodes; - for (const Node &leaf : traverse(Orthtrees::Leaves_traversal())) { + std::queue leaf_nodes; + for (const Node& leaf: traverse(Orthtrees::Leaves_traversal())) { // TODO: I'd like to find a better (safer) way of doing this - leaf_nodes.push(const_cast(&leaf)); + leaf_nodes.push(const_cast(&leaf)); } // Iterate over the nodes while (!leaf_nodes.empty()) { // Get the next node - Node *node = leaf_nodes.front(); + Node* node = leaf_nodes.front(); leaf_nodes.pop(); // Skip this node if it isn't a leaf anymore @@ -394,7 +387,7 @@ class Orthtree for (int direction = 0; direction < 6; ++direction) { // Get the neighbor - auto *neighbor = node->adjacent_node(direction); + auto* neighbor = node->adjacent_node(direction); // If it doesn't exist, skip it if (!neighbor) @@ -436,7 +429,7 @@ class Orthtree \return a const reference to the root node of the tree. */ - const Node &root() const { return m_root; } + const Node& root() const { return m_root; } /*! \brief provides read-write access to the root node, and by @@ -446,7 +439,7 @@ class Orthtree \return a reference to the root node of the tree. */ - Node &root() { return m_root; } + Node& root() { return m_root; } /*! \brief Convenience function to access the child nodes of the root @@ -459,7 +452,7 @@ class Orthtree \param index the index of the child node. \return a reference to the node. */ - const Node &operator[](std::size_t index) const { return m_root[index]; } + const Node& operator[](std::size_t index) const { return m_root[index]; } /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). @@ -479,13 +472,13 @@ class Orthtree \return a forward input iterator over the nodes of the tree */ - template - Node_range traverse(const Traversal &traversal = Traversal()) const { + template + Node_range traverse(const Traversal& traversal = Traversal()) const { - const Node *first = traversal.first(&m_root); + const Node* first = traversal.first(&m_root); Node_traversal_method_const next - = [&](const Node* n) -> const Node * { return traversal.next(n); }; + = [&](const Node* n) -> const Node* { return traversal.next(n); }; return boost::make_iterator_range(Traversal_iterator(first, next), Traversal_iterator()); @@ -498,7 +491,7 @@ class Orthtree subset inside the node, but the bounding box of the node itself (cubic). */ - Bbox bbox(const Node &node) const { + Bbox bbox(const Node& node) const { // Determine the side length of this node FT size = m_side_per_depth[node.depth()]; @@ -532,13 +525,13 @@ class Orthtree \param point query point. \return the node which contains the point. */ - const Node& locate(const Point &point) const { + const Node& locate(const Point& point) const { // Make sure the point is enclosed by the orthtree CGAL_precondition (CGAL::do_intersect(point, bbox(m_root))); // Start at the root node - auto *node_for_point = &m_root; + auto* node_for_point = &m_root; // Descend the tree until reaching a leaf node while (!node_for_point->is_leaf()) { @@ -549,8 +542,8 @@ class Orthtree // Find the index of the correct sub-node typename Node::Local_coordinates index; std::size_t dimension = 0; - for (const auto& r : cartesian_range(center, point)) - index[dimension ++] = (get<0>(r) < get<1>(r)); + for (const auto& r: cartesian_range(center, point)) + index[dimension++] = (get < 0 > (r) < get < 1 > (r)); // Find the correct sub-node of the current node node_for_point = &(*node_for_point)[index.to_ulong()]; @@ -609,7 +602,7 @@ class Orthtree \param query the intersecting primitive. \param output output iterator. */ - template + template OutputIterator intersected_nodes(const Query& query, OutputIterator output) const { return intersected_nodes_recursive(query, root(), output); } @@ -626,7 +619,7 @@ class Orthtree Equivalent trees must have the same bounding box and the same node structure. Node structure is evaluated by comparing the root nodes using the node equality operator. */ - bool operator==(const Self &rhs) const { + bool operator==(const Self& rhs) const { // Identical trees should have the same bounding box if (rhs.m_bbox_min != m_bbox_min || rhs.m_side_per_depth[0] != m_side_per_depth[0]) @@ -643,15 +636,73 @@ class Orthtree /*! \brief compares the topology of the orthtree with that of `rhs`. */ - bool operator!=(const Self &rhs) const { + bool operator!=(const Self& rhs) const { return !operator==(rhs); } /// @} + /*! + \brief splits the node into subnodes. + + Only leaf nodes should be split. + When a node is split it is no longer a leaf node. + A number of `Degree::value` children are constructed automatically, and their values are set. + Contents of this node are _not_ propagated automatically. + It is the responsibility of the caller to redistribute the points contained by a node after splitting + */ + void split(Node& node) { + + // Make sure the node hasn't already been split + CGAL_precondition (node.is_leaf()); + + // Split the node to create children + node.m_children = std::make_shared(); + for (int index = 0; index < Degree::value; index++) { + (*node.m_children)[index] = std::move(Node(&node, {index})); + } + + // Find the point to around which the node is split + Point center = barycenter(node); + + // Add the node's points to its children + reassign_points(node, node.points().begin(), node.points().end(), center); + } + + /*! + * \brief eliminates this node's children, making it a leaf node. + * + * When a node is un-split, its children are automatically deleted. + * After un-splitting a node it will be considered a leaf node. + * Idempotent, un-splitting a leaf node has no effect. + */ + void unsplit(Node& node) { + node.m_children.reset(); + } + + // todo: this can be removed when nodes store indices instead of references! + Node deep_copy(const Node& node, Node* parent = nullptr) const { + + Node out; + + out.m_parent = parent; + out.m_points = node.m_points; + out.m_depth = node.m_depth; + out.m_global_coordinates = node.m_global_coordinates; + + if (!node.is_leaf()) { + out.m_children = std::make_shared(); + for (int index = 0; index < Degree::value; index++) + (*out.m_children)[index] = deep_copy((*node.m_children)[index], &out); + } + + return out; + } + + // TODO: Document this // TODO: Could this method name be reduced to just "center" ? - Point barycenter(const Node &node) const { + Point barycenter(const Node& node) const { // Determine the side length of this node FT size = m_side_per_depth[node.depth()]; @@ -659,8 +710,7 @@ class Orthtree // Determine the location this node should be split Array bary; std::size_t i = 0; - for (const FT& f : cartesian_range(m_bbox_min)) - { + for (const FT& f: cartesian_range(m_bbox_min)) { bary[i] = FT(node.global_coordinates()[i]) * size + size / FT(2) + f; ++i; } @@ -673,7 +723,7 @@ class Orthtree private: // functions : - void reassign_points(Node &node, Range_iterator begin, Range_iterator end, const Point ¢er, + void reassign_points(Node& node, Range_iterator begin, Range_iterator end, const Point& center, std::bitset coord = {}, std::size_t dimension = 0) { @@ -688,8 +738,7 @@ class Orthtree // Split the point collection around the center point on this dimension Range_iterator split_point = std::partition (begin, end, - [&](const Range_type& a) -> bool - { + [&](const Range_type& a) -> bool { // This should be done with cartesian iterator but it seems // complicated to do efficiently return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); @@ -707,22 +756,7 @@ class Orthtree } - void split(Node &node) { - - // Make sure the node hasn't already been split - CGAL_precondition (node.is_leaf()); - - // Split the node to create children - node.split(); - - // Find the point to around which the node is split - Point center = barycenter(node); - - // Add the node's points to its children - reassign_points(node, node.points().begin(), node.points().end(), center); - } - - bool do_intersect(const Node &node, const Sphere &sphere) const { + bool do_intersect(const Node& node, const Sphere& sphere) const { // Create a cubic bounding box from the node Bbox node_cube = bbox(node); @@ -742,8 +776,7 @@ class Orthtree FT distance; }; - struct Node_index_with_distance - { + struct Node_index_with_distance { typename Node::Local_coordinates index; FT distance; @@ -752,7 +785,7 @@ class Orthtree : index(index), distance(distance) {} }; - void nearest_k_neighbors_recursive(Sphere &search_bounds, const Node& node, + void nearest_k_neighbors_recursive(Sphere& search_bounds, const Node& node, std::vector& results, FT epsilon = 0) const { // Check whether the node has children @@ -762,14 +795,14 @@ class Orthtree // Loop through each of the points contained by the node // Note: there might be none, and that should be fine! - for (auto point_index : node.points()) { + for (auto point_index: node.points()) { // Retrieve each point from the orthtree's point map auto point = get(m_point_map, point_index); // Pair that point with its distance from the search point Point_with_distance current_point_with_distance = - {point, squared_distance(point, search_bounds.center())}; + {point, squared_distance(point, search_bounds.center())}; // Check if the new point is within the bounds if (current_point_with_distance.distance < search_bounds.squared_radius()) { @@ -785,7 +818,7 @@ class Orthtree results.push_back(current_point_with_distance); // Sort the list - std::sort(results.begin(), results.end(), [=](auto &left, auto &right) { + std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { return left.distance < right.distance; }); @@ -807,7 +840,7 @@ class Orthtree // Fill the list with child nodes for (int index = 0; index < Degree::value; ++index) { - auto &child_node = node[index]; + auto& child_node = node[index]; // Add a child to the list, with its distance children_with_distances.emplace_back(typename Node::Local_coordinates(index), @@ -815,13 +848,13 @@ class Orthtree } // Sort the children by their distance from the search point - std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto &left, auto &right) { + std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto& left, auto& right) { return left.distance < right.distance; }); // Loop over the children - for (auto child_with_distance : children_with_distances) { - auto &child_node = node[child_with_distance.index.to_ulong()]; + for (auto child_with_distance: children_with_distances) { + auto& child_node = node[child_with_distance.index.to_ulong()]; // Check whether the bounding box of the child intersects with the search bounds if (do_intersect(child_node, search_bounds)) { @@ -833,8 +866,8 @@ class Orthtree } } - template - Node_output_iterator intersected_nodes_recursive(const Query &query, const Node &node, + template + Node_output_iterator intersected_nodes_recursive(const Query& query, const Node& node, Node_output_iterator output) const { // Check if the current node intersects with the query @@ -873,8 +906,8 @@ class Orthtree */ template OutputIterator nearest_k_neighbors_in_radius - (Sphere &query_sphere, - std::size_t k, OutputIterator output) const { + (Sphere& query_sphere, + std::size_t k, OutputIterator output) const { // Create an empty list of points std::vector points_list; @@ -885,7 +918,7 @@ class Orthtree nearest_k_neighbors_recursive(query_sphere, m_root, points_list); // Add all the points found to the output - for (auto &item : points_list) + for (auto& item: points_list) *output++ = item.point; return output; @@ -896,8 +929,7 @@ class Orthtree /// \cond SKIP_IN_MANUAL void dump_to_polylines(std::ostream& os) const { for (const Node& n: traverse()) - if (n.is_leaf()) - { + if (n.is_leaf()) { Bbox box = bbox(n); dump_box_to_polylines(box, os); } @@ -949,8 +981,7 @@ class Orthtree // Create a range of nodes auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal()); // Iterate over the range - for (auto& n: nodes) - { + for (auto& n: nodes) { // Show the depth for (int i = 0; i < n.depth(); ++i) os << ". "; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index 42f439b5598f..c9957ea30d9c 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -26,27 +26,6 @@ namespace CGAL { -/// \cond SKIP_IN_MANUAL -namespace Orthtrees { - -// Non-documented, or testing purpose only -struct Node_access { - template - static Node create_node(Node* parent, LC local_coordinates) { - return Node(parent, local_coordinates); - } - - template - static typename Node::Point_range& points(Node& node) { return node.points(); } - - template - static void split(Node& node) { return node.split(); } - -}; - -} // namespace Orthtrees -/// \endcond - /*! \brief represents a single node of the tree. Alternatively referred @@ -121,35 +100,18 @@ class Orthtree::Node { typedef boost::iterator_range Point_range; /// \endcond - // make Node trivially copiable -// struct Data -// { -// Point_range points; -// Self parent; -// std::uint8_t depth; -// Global_coordinates global_coordinates; -// std::unique_ptr children; -// -// Data (Self parent) -// : parent (parent), depth (0) { } -// }; -// Data* m_data; - Point_range m_points; - Self* m_parent; // todo: use optional> instead of Self * - std::uint8_t m_depth; - Global_coordinates m_global_coordinates; - std::shared_ptr m_children; - + Self* m_parent = nullptr; // todo: use optional> instead of Self * + std::uint8_t m_depth = 0; + Global_coordinates m_global_coordinates{}; + std::shared_ptr m_children{}; - /// \cond SKIP_IN_MANUAL // Only the Orthtree class has access to the non-default // constructor, mutators, etc. friend Enclosing; - // Hidden class to access methods for testing purposes - friend Orthtrees::Node_access; +public: // todo: Was there a good reason that all of this was private? /*! * \brief Access to the content held by this node @@ -162,6 +124,10 @@ class Orthtree::Node { /// \name Construction /// @{ + /// \cond SKIP_IN_MANUAL + Node() = default; + /// \endcond + /*! \brief creates a new node, optionally as the child of a parent @@ -180,6 +146,13 @@ class Orthtree::Node { explicit Node(Self* parent, Local_coordinates local_coordinates) : m_parent(parent) { +// if (m_parent) { +// m_depth = m_parent->m_depth + 1; +// +// for (int i = 0; i < Dimension::value; i++) +// m_global_coordinates[i] = (2 * parent->m_global_coordinates[i]) + local_coordinates[i]; +// } + if (parent != nullptr) { m_depth = parent->m_depth + 1; @@ -194,78 +167,10 @@ class Orthtree::Node { } } - Node deep_copy(Self parent = Node()) const { - - Node out; - - out.m_parent = m_parent; - out.m_points = m_points; - out.m_depth = m_depth; - out.m_global_coordinates = m_global_coordinates; - - if (!is_leaf()) { - out.m_children = std::make_shared(); - for (int index = 0; index < Degree::value; index++) - (*out.m_children)[index] = (*m_children)[index].deep_copy(out); - } - - return out; - } - - /// @} - - /// \name Mutators - /// @{ - - /*! - \brief splits a node into subnodes. - - Only leaf nodes should be split. - When a node is split it is no longer a leaf node. - A number of `Degree::value` children are constructed automatically, and their values are set. - Contents of this node are _not_ propagated automatically. - It is the responsibility of the caller to redistribute the points contained by a node after splitting - */ - void split() { - - CGAL_precondition (is_leaf()); - - m_children = std::make_shared(); - for (int index = 0; index < Degree::value; index++) { - - (*m_children)[index] = std::move(Self(this, {Local_coordinates(index)})); - - } - } - - /*! - * \brief eliminates this node's children, making it a leaf node. - * - * When a node is un-split, its children are automatically deleted. - * After un-splitting a node it will be considered a leaf node. - */ - void unsplit() { - - m_children.reset(); - } - /// @} - /// \endcond - - public: - /// \cond SKIP_IN_MANUAL - // Default creates null node - // todo: default node is no longer null, but still isn't guaranteed to be valid - Node() = default; - - // Comparison operator - // todo: where is this used - //bool operator<(const Node& other) const { return m_data < other.m_data; } - /// \endcond - /// \name Type & Location /// @{ diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 82b0862e3275..ae4549588df5 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -24,11 +24,8 @@ void test_1_point() { Octree octree(points, points.point_map()); octree.refine(10, 1); - // Check that the topology matches - Node single_node = CGAL::Orthtrees::Node_access::create_node(static_cast(nullptr), 0); - CGAL::Orthtrees::Node_access::points(single_node) - = CGAL::Orthtrees::Node_access::points(octree.root()); - assert(Node::is_topology_equal(single_node, octree.root())); + // Check that the root node was never split + assert(octree.root().is_leaf()); assert(0 == octree.depth()); } @@ -44,9 +41,9 @@ void test_2_points() { octree.refine(10, 1); // The octree should have been split once - Node other = CGAL::Orthtrees::Node_access::create_node(static_cast(nullptr), 0); - CGAL::Orthtrees::Node_access::split(other); - assert(Node::is_topology_equal(other, octree.root())); + Octree other(points, points.point_map()); + other.split(other.root()); + assert(Node::is_topology_equal(other.root(), octree.root())); assert(1 == octree.depth()); } @@ -63,11 +60,11 @@ void test_4_points() { octree.refine(10, 1); // The octree should have been split once on the first level, and twice on the second - Node other = CGAL::Orthtrees::Node_access::create_node(static_cast(nullptr), 0); - CGAL::Orthtrees::Node_access::split(other); - CGAL::Orthtrees::Node_access::split(other[3]); - CGAL::Orthtrees::Node_access::split(other[7]); - assert(Node::is_topology_equal(other, octree.root())); + Octree other(points, points.point_map()); + other.split(other.root()); + other.split(other.root()[3]); + other.split(other.root()[7]); + assert(Node::is_topology_equal(other.root(), octree.root())); assert(2 == octree.depth()); } From d8b42fd3f3f6777fd28e570cb3a21ae67f504637 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 26 Mar 2023 21:32:55 +0200 Subject: [PATCH 003/520] Remove subscript operator for direct access to children of root This operator will be used for access by node ID in the future, and this functionality was less clear than tree.root[]. --- Orthtree/include/CGAL/Orthtree.h | 13 ---------- .../Orthtree/test_octree_intersecting.cpp | 24 +++++++++---------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 0e3e9a187438..edef27df68a7 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -441,19 +441,6 @@ class Orthtree { */ Node& root() { return m_root; } - /*! - \brief Convenience function to access the child nodes of the root - node by their indices. - - `my_tree[5]` is equivalent to `my_tree.root()[5]`. - - \sa `Node::operator[]()` - - \param index the index of the child node. - \return a reference to the node. - */ - const Node& operator[](std::size_t index) const { return m_root[index]; } - /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). */ diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 4d83929c2e0f..b731ec04e331 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -68,10 +68,10 @@ int main(void) { // Check the results assert(4 == nodes.size()); - assert(octree[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]); - assert(octree[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]); - assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]); + assert(octree.root()[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]); + assert(octree.root()[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]); + assert(octree.root()[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]); + assert(octree.root()[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]); } // Intersection with a ray @@ -86,14 +86,14 @@ int main(void) { // Check the results assert(8 == nodes.size()); - assert(octree[Octree::Traits::LEFT_BOTTOM_BACK] == *nodes[0]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == *nodes[1]); - assert(octree[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]); - assert(octree[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]); - assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]); - assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]); - assert(octree[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]); - assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]); + assert(octree.root()[Octree::Traits::LEFT_BOTTOM_BACK] == *nodes[0]); + assert(octree.root()[Octree::Traits::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == *nodes[1]); + assert(octree.root()[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]); + assert(octree.root()[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]); + assert(octree.root()[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]); + assert(octree.root()[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]); + assert(octree.root()[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]); + assert(octree.root()[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]); } return EXIT_SUCCESS; From 353acb57ed1bf754bac17c65e86670f5c0c517a5 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 27 Mar 2023 12:20:57 +0200 Subject: [PATCH 004/520] Nodes are allocated in an std::vector, and contain a boost::span of children This reference-based approach breaks when the vector re-allocates. This is a stepping-stone to using indices. --- Orthtree/include/CGAL/Orthtree.h | 62 ++++++++++++++------------- Orthtree/include/CGAL/Orthtree/Node.h | 47 +++++++++++++------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index edef27df68a7..4b4c042ee051 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -148,7 +149,7 @@ class Orthtree { PointRange& m_range; /* input point range */ PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */ - Node m_root; /* root node of the orthtree */ + std::vector m_nodes; /* nodes of the tree; root is always at index 0 */ Point m_bbox_min; /* input bounding box min value */ @@ -194,9 +195,11 @@ class Orthtree { Traits traits = Traits()) : m_traits(traits) , m_range(point_range) - , m_point_map(point_map) - , m_root() // todo: can this be default-constructed? - { + , m_point_map(point_map) { + + m_nodes.reserve(2048); // todo: temporary, for testing + m_nodes.emplace_back(); + Array bbox_min; Array bbox_max; @@ -240,7 +243,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = construct_point_d_from_array(bbox_min); m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - m_root.points() = {point_range.begin(), point_range.end()}; + root().points() = {point_range.begin(), point_range.end()}; } /// @} @@ -252,7 +255,7 @@ class Orthtree { : m_traits(other.m_traits) , m_range(other.m_range) , m_point_map(other.m_point_map) - , m_root(deep_copy(other.m_root)) + , m_nodes(other.m_nodes) // todo: copying will require some extra management , m_bbox_min(other.m_bbox_min) , m_side_per_depth(other.m_side_per_depth) {} @@ -261,11 +264,9 @@ class Orthtree { : m_traits(other.m_traits) , m_range(other.m_range) , m_point_map(other.m_point_map) - , m_root(other.m_root) + , m_nodes(std::move(other.m_nodes)) , m_bbox_min(other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) { - other.m_root = Node{}; - } + , m_side_per_depth(other.m_side_per_depth) {} // Non-necessary but just to be clear on the rule of 5: @@ -298,15 +299,15 @@ class Orthtree { void refine(const Split_predicate& split_predicate) { // If the tree has already been refined, reset it - if (!m_root.is_leaf()) - unsplit(m_root); + if (!root().is_leaf()) + unsplit(root()); // Reset the side length map, too m_side_per_depth.resize(1); // Initialize a queue of nodes that need to be refined std::queue todo; - todo.push(&m_root); + todo.push(&root()); // Process items in the queue until it's consumed fully while (!todo.empty()) { @@ -429,7 +430,7 @@ class Orthtree { \return a const reference to the root node of the tree. */ - const Node& root() const { return m_root; } + const Node& root() const { return m_nodes[0]; } /*! \brief provides read-write access to the root node, and by @@ -439,7 +440,7 @@ class Orthtree { \return a reference to the root node of the tree. */ - Node& root() { return m_root; } + Node& root() { return m_nodes[0]; } /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). @@ -462,7 +463,7 @@ class Orthtree { template Node_range traverse(const Traversal& traversal = Traversal()) const { - const Node* first = traversal.first(&m_root); + const Node* first = traversal.first(&root()); Node_traversal_method_const next = [&](const Node* n) -> const Node* { return traversal.next(n); }; @@ -515,10 +516,10 @@ class Orthtree { const Node& locate(const Point& point) const { // Make sure the point is enclosed by the orthtree - CGAL_precondition (CGAL::do_intersect(point, bbox(m_root))); + CGAL_precondition (CGAL::do_intersect(point, bbox(root()))); // Start at the root node - auto* node_for_point = &m_root; + auto* node_for_point = &root(); // Descend the tree until reaching a leaf node while (!node_for_point->is_leaf()) { @@ -617,7 +618,7 @@ class Orthtree { return false; // If all else is equal, recursively compare the trees themselves - return Node::is_topology_equal(rhs.m_root, m_root); + return Node::is_topology_equal(rhs.root(), root()); } /*! @@ -644,10 +645,12 @@ class Orthtree { CGAL_precondition (node.is_leaf()); // Split the node to create children - node.m_children = std::make_shared(); + using Children = typename Node::Children; + using Local_coordinates = typename Node::Local_coordinates; for (int index = 0; index < Degree::value; index++) { - (*node.m_children)[index] = std::move(Node(&node, {index})); + m_nodes.emplace_back(&node, Local_coordinates{index}); } + node.m_children = Children{&*(m_nodes.end() - Degree::value), Degree::value}; // todo: temporary, for testing // Find the point to around which the node is split Point center = barycenter(node); @@ -723,13 +726,14 @@ class Orthtree { } // Split the point collection around the center point on this dimension - Range_iterator split_point = std::partition - (begin, end, - [&](const Range_type& a) -> bool { - // This should be done with cartesian iterator but it seems - // complicated to do efficiently - return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); - }); + Range_iterator split_point = std::partition( + begin, end, + [&](const Range_type& a) -> bool { + // This should be done with cartesian iterator but it seems + // complicated to do efficiently + return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); + } + ); // Further subdivide the first side of the split std::bitset coord_left = coord; @@ -902,7 +906,7 @@ class Orthtree { points_list.reserve(k); // Invoking the recursive function adds those points to the vector (passed by reference) - nearest_k_neighbors_recursive(query_sphere, m_root, points_list); + nearest_k_neighbors_recursive(query_sphere, root(), points_list); // Add all the points found to the output for (auto& item: points_list) diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index c9957ea30d9c..e88268e41d30 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -58,7 +59,7 @@ class Orthtree::Node { /*! * \brief Array for containing the child nodes of this node. */ - typedef std::array Children; + typedef boost::span Children; /// \endcond /*! @@ -104,22 +105,14 @@ class Orthtree::Node { Self* m_parent = nullptr; // todo: use optional> instead of Self * std::uint8_t m_depth = 0; Global_coordinates m_global_coordinates{}; - std::shared_ptr m_children{}; + boost::optional m_children{}; // Only the Orthtree class has access to the non-default // constructor, mutators, etc. friend Enclosing; -public: // todo: Was there a good reason that all of this was private? - - /*! - * \brief Access to the content held by this node - * \return a reference to the collection of point indices - */ - Point_range& points() { return m_points; } - - const Point_range& points() const { return m_points; } +public: /// \name Construction /// @{ @@ -171,6 +164,29 @@ class Orthtree::Node { public: + /// \name Member Access + /// @{ + + /*! + * \brief Access to the content held by this node + * \return a reference to the collection of point indices + */ + Point_range& points() { return m_points; } + + const Point_range& points() const { return m_points; } + + Children& children() { + CGAL_precondition (!is_leaf()); + return m_children.get(); + } + + const Children& children() const { + CGAL_precondition (!is_leaf()); + return m_children.get(); + } + + /// @} + /// \name Type & Location /// @{ @@ -192,7 +208,7 @@ class Orthtree::Node { \pre `!is_null()` */ bool is_leaf() const { - return (!m_children); + return (!m_children.has_value()); } /*! @@ -225,6 +241,7 @@ class Orthtree::Node { return m_global_coordinates; } + /// @} /// \name Adjacency /// @{ @@ -296,7 +313,7 @@ class Orthtree::Node { CGAL_precondition (!is_leaf()); CGAL_precondition (index < Degree::value); - return (*m_children)[index]; + return m_children.get()[index]; } /*! @@ -313,7 +330,7 @@ class Orthtree::Node { CGAL_precondition (!is_leaf()); CGAL_precondition (index < Degree::value); - return (*m_children)[index]; + return m_children.get()[index]; } /*! @@ -482,7 +499,7 @@ class Orthtree::Node { // todo: This is a trivial implementation, maybe it can be set to =default in c++17? return rhs.m_parent == m_parent && - rhs.m_children == m_children && + //rhs.m_children == m_children && // todo: this might be wrong for deep-copies rhs.m_points == m_points && rhs.m_depth == m_depth && rhs.m_global_coordinates == m_global_coordinates; From 45244da9e1cb8926edabb8996ea5489aa9ed593f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 27 Mar 2023 18:12:20 +0200 Subject: [PATCH 005/520] Node can no longer directly access its children without the tree This is necessary for the next step, but makes the API a pain to use. Some sort of `Node_handle` type is definitely worthwhile. --- Orthtree/include/CGAL/Orthtree.h | 194 +++++++++++++-- Orthtree/include/CGAL/Orthtree/Node.h | 234 ------------------ Orthtree/include/CGAL/Orthtree/Traversals.h | 119 +++++---- Orthtree/test/Orthtree/test_node_adjacent.cpp | 49 ++-- Orthtree/test/Orthtree/test_node_index.cpp | 9 +- Orthtree/test/Orthtree/test_octree_bbox.cpp | 82 +++--- Orthtree/test/Orthtree/test_octree_grade.cpp | 6 +- .../Orthtree/test_octree_intersecting.cpp | 33 +-- Orthtree/test/Orthtree/test_octree_locate.cpp | 66 ++--- Orthtree/test/Orthtree/test_octree_refine.cpp | 8 +- .../test/Orthtree/test_octree_traverse.cpp | 24 +- 11 files changed, 403 insertions(+), 421 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 4b4c042ee051..0e2a79e816f8 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -197,7 +197,7 @@ class Orthtree { , m_range(point_range) , m_point_map(point_map) { - m_nodes.reserve(2048); // todo: temporary, for testing + m_nodes.reserve(1'000'000); // todo: temporary, for testing m_nodes.emplace_back(); Array bbox_min; @@ -331,7 +331,7 @@ class Orthtree { // Process each of its children for (int i = 0; i < Degree::value; ++i) - todo.push(&(*current)[i]); + todo.push(&children(*current)[i]); } } @@ -368,7 +368,7 @@ class Orthtree { // Collect all the leaf nodes std::queue leaf_nodes; - for (const Node& leaf: traverse(Orthtrees::Leaves_traversal())) { + for (const Node& leaf: traverse(Orthtrees::Leaves_traversal(*this))) { // TODO: I'd like to find a better (safer) way of doing this leaf_nodes.push(const_cast(&leaf)); } @@ -388,7 +388,7 @@ class Orthtree { for (int direction = 0; direction < 6; ++direction) { // Get the neighbor - auto* neighbor = node->adjacent_node(direction); + auto* neighbor = adjacent_node(*node, direction); // If it doesn't exist, skip it if (!neighbor) @@ -412,7 +412,7 @@ class Orthtree { // Add newly created children to the queue for (int i = 0; i < Degree::value; ++i) { - leaf_nodes.push(&(*neighbor)[i]); + leaf_nodes.push(&children(*neighbor)[i]); } } } @@ -461,9 +461,9 @@ class Orthtree { \return a forward input iterator over the nodes of the tree */ template - Node_range traverse(const Traversal& traversal = Traversal()) const { + Node_range traverse(const Traversal& traversal) const { - const Node* first = traversal.first(&root()); + const Node* first = traversal.first(); Node_traversal_method_const next = [&](const Node* n) -> const Node* { return traversal.next(n); }; @@ -472,6 +472,12 @@ class Orthtree { Traversal_iterator()); } + // todo: document this convenience function + template + Node_range traverse() const { + return traverse(Traversal(*this)); + } + /*! \brief constructs the bounding box of a node. @@ -534,7 +540,7 @@ class Orthtree { index[dimension++] = (get < 0 > (r) < get < 1 > (r)); // Find the correct sub-node of the current node - node_for_point = &(*node_for_point)[index.to_ulong()]; + node_for_point = &children(*node_for_point)[index.to_ulong()]; } // Return the result @@ -618,7 +624,7 @@ class Orthtree { return false; // If all else is equal, recursively compare the trees themselves - return Node::is_topology_equal(rhs.root(), root()); + return is_topology_equal(root(), *this, rhs.root(), rhs); } /*! @@ -630,6 +636,21 @@ class Orthtree { /// @} + /// \name Node Access + /// @{ + + using Children = typename Node::Children; + + Children& children(Node& node) { + CGAL_precondition (!node.is_leaf()); + return node.m_children.get(); + } + + const Children& children(const Node& node) const { + CGAL_precondition (!node.is_leaf()); + return node.m_children.get(); + } + /*! \brief splits the node into subnodes. @@ -711,6 +732,151 @@ class Orthtree { return construct_point_d_from_array(bary); } + + // todo: this does what the documentation for operator== claims to do! + static bool is_topology_equal(const Node& lhsNode, const Self& lhsTree, const Node& rhsNode, const Self& rhsTree) { + + // If one node is a leaf, and the other isn't, they're not the same + if (lhsNode.is_leaf() != rhsNode.is_leaf()) + return false; + + // If both nodes are non-leaf nodes + if (!lhsNode.is_leaf()) { + + // Check all the children + for (int i = 0; i < Degree::value; ++i) { + // If any child cell is different, they're not the same + if (!is_topology_equal(lhsTree.children(lhsNode)[i], lhsTree, rhsTree.children(rhsNode)[i], rhsTree)) + return false; + } + } + + // If both nodes are leaf nodes, they must be in the same location + return (lhsNode.global_coordinates() == rhsNode.global_coordinates()); + } + + static bool is_topology_equal(const Self& lhs, const Self& rhs) { + return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); + } + + /*! + \brief finds the directly adjacent node in a specific direction + + \pre `!is_null()` + \pre `direction.to_ulong < 2 * Dimension::value` + + Adjacent nodes are found according to several properties: + - adjacent nodes may be larger than the seek node, but never smaller + - a node has at most `2 * Dimension::value` different adjacent nodes (in 3D: left, right, up, down, front, back) + - adjacent nodes are not required to be leaf nodes + + Here's a diagram demonstrating the concept for a Quadtree: + + ``` + +---------------+---------------+ + | | | + | | | + | | | + | A | | + | | | + | | | + | | | + +-------+-------+---+---+-------+ + | | | | | | + | A | (S) +---A---+ | + | | | | | | + +---+---+-------+---+---+-------+ + | | | | | | + +---+---+ A | | | + | | | | | | + +---+---+-------+-------+-------+ + ``` + + - (S) : Seek node + - A : Adjacent node + + Note how the top adjacent node is larger than the seek node. The + right adjacent node is the same size, even though it contains + further subdivisions. + + This implementation returns the adjacent node if it's found. If + there is no adjacent node in that direction, it returns a null + node. + + \param direction which way to find the adjacent node relative to + this one. Each successive bit selects the direction for the + corresponding dimension: for an Octree in 3D, 010 means: negative + direction in X, position direction in Y, negative direction in Z. + + \return the adjacent node if it exists, a null node otherwise. + */ + const Node* adjacent_node(const Node& node, typename Node::Local_coordinates direction) const { + + // Direction: LEFT RIGHT DOWN UP BACK FRONT + // direction: 000 001 010 011 100 101 + + // Nodes only have up to 2*dim different adjacent nodes (since cubes have 6 sides) + CGAL_precondition(direction.to_ulong() < Dimension::value * 2); + + // The root node has no adjacent nodes! + if (node.is_root()) return nullptr; + + // The least significant bit indicates the sign (which side of the node) + bool sign = direction[0]; + + // The first two bits indicate the dimension/axis (x, y, z) + uint8_t dimension = uint8_t((direction >> 1).to_ulong()); + + // Create an offset so that the bit-significance lines up with the dimension (e.g. 1, 2, 4 --> 001, 010, 100) + int8_t offset = (uint8_t) 1 << dimension; + + // Finally, apply the sign to the offset + offset = (sign ? offset : -offset); + + // Check if this child has the opposite sign along the direction's axis + if (node.local_coordinates()[dimension] != sign) { + // This means the adjacent node is a direct sibling, the offset can be applied easily! + return &children(*node.parent())[node.local_coordinates().to_ulong() + offset]; + } + + // Find the parent's neighbor in that direction, if it exists + const Node* adjacent_node_of_parent = adjacent_node(*node.parent(), direction); + + // If the parent has no neighbor, then this node doesn't have one + if (adjacent_node_of_parent == nullptr) return nullptr; + + // If the parent's adjacent node has no children, then it's this node's adjacent node + if (adjacent_node_of_parent->is_leaf()) + return adjacent_node_of_parent; + + // Return the nearest node of the parent by subtracting the offset instead of adding + return &children(*adjacent_node_of_parent)[node.local_coordinates().to_ulong() - offset]; + + } + + /*! + \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. + */ + const Node* adjacent_node(const Node& node, typename Node::Adjacency adjacency) const { + return adjacent_node(node, std::bitset(static_cast(adjacency))); + } + + /*! + * \brief equivalent to adjacent_node, except non-const + */ + Node* adjacent_node(Node& node, std::bitset direction) { + return const_cast(const_cast(this)->adjacent_node(node, direction)); + } + + /*! + * \brief equivalent to adjacent_node, with a Direction rather than a bitset and non-const + */ + Node* adjacent_node(Node& node, typename Node::Adjacency adjacency) { + return adjacent_node(node, std::bitset(static_cast(adjacency))); + } + + /// @} + private: // functions : void reassign_points(Node& node, Range_iterator begin, Range_iterator end, const Point& center, @@ -720,7 +886,7 @@ class Orthtree { // Root case: reached the last dimension if (dimension == Dimension::value) { - node[coord.to_ulong()].points() = {begin, end}; + children(node)[coord.to_ulong()].points() = {begin, end}; return; } @@ -831,7 +997,7 @@ class Orthtree { // Fill the list with child nodes for (int index = 0; index < Degree::value; ++index) { - auto& child_node = node[index]; + auto& child_node = children(node)[index]; // Add a child to the list, with its distance children_with_distances.emplace_back(typename Node::Local_coordinates(index), @@ -845,7 +1011,7 @@ class Orthtree { // Loop over the children for (auto child_with_distance: children_with_distances) { - auto& child_node = node[child_with_distance.index.to_ulong()]; + auto& child_node = children(node)[child_with_distance.index.to_ulong()]; // Check whether the bounding box of the child intersects with the search bounds if (do_intersect(child_node, search_bounds)) { @@ -872,7 +1038,7 @@ class Orthtree { // Otherwise, each of the children need to be checked for (int i = 0; i < Degree::value; ++i) { - intersected_nodes_recursive(query, node[i], output); + intersected_nodes_recursive(query, children(node)[i], output); } } return output; @@ -970,7 +1136,7 @@ class Orthtree { friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { // Create a range of nodes - auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal()); + auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal(orthtree)); // Iterate over the range for (auto& n: nodes) { // Show the depth diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index e88268e41d30..98acd0bd2cb0 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -139,13 +139,6 @@ class Orthtree::Node { explicit Node(Self* parent, Local_coordinates local_coordinates) : m_parent(parent) { -// if (m_parent) { -// m_depth = m_parent->m_depth + 1; -// -// for (int i = 0; i < Dimension::value; i++) -// m_global_coordinates[i] = (2 * parent->m_global_coordinates[i]) + local_coordinates[i]; -// } - if (parent != nullptr) { m_depth = parent->m_depth + 1; @@ -175,16 +168,6 @@ class Orthtree::Node { const Point_range& points() const { return m_points; } - Children& children() { - CGAL_precondition (!is_leaf()); - return m_children.get(); - } - - const Children& children() const { - CGAL_precondition (!is_leaf()); - return m_children.get(); - } - /// @} /// \name Type & Location @@ -255,201 +238,6 @@ class Orthtree::Node { return m_parent; } - /*! - \brief returns the nth child of this node. - - \pre `!is_leaf()` - \pre `0 <= index && index < Degree::value` - - The orthtree subdivides the space in 2 on each dimension - available, so a child node can be accessed by selecting a Boolean - value for each dimension. The `index` parameter is thus - interpreted as a bitmap, where each bit matches a dimension - (starting by the least significant bit for coordinate X). - - For example, in the case of an octree (dimension 3): - - - index 0 (000 in binary) is the children on the "minimum corner" (xmin, ymin, zmin) - - index 1 (001 in binary) is on (xmax, ymin, zmin) - - index 2 (010 in binary) is on (xmin, ymax, zmin) - - index 6 (101 in binary) is on (xmax, ymin, zmax) - - Diagram of a quadtree: - - ``` - Children of the current node: - - Y axis - A - | +-------------------+-------------------+ - | | Coord: Ymax Xmin | Coord: Ymax Xmax | - | | Bitmap: 1 0 | Bitmap: 1 1 | - | | | | - | | -> index = 2 | -> index = 3 | - | | | | - | | | | - | | | | - | | | | - | +-------------------+-------------------+ - | | Coord: Ymin Xmin | Coord: Ymin Xmax | - | | Bitmap: 0 0 | Bitmap: 0 1 | - | | | | - | | -> index = 0 | -> index = 1 | - | | | | - | | | | - | | | | - | | | | - | +-------------------+-------------------+ - | - +--------------------------------------------> X axis - 0 - ``` - - The operator can be chained. For example, `n[5][2][3]` returns the - third child of the second child of the fifth child of a node `n`. - */ - Self& operator[](std::size_t index) { - - CGAL_precondition (!is_leaf()); - CGAL_precondition (index < Degree::value); - - return m_children.get()[index]; - } - - /*! - \brief returns the nth child of this node. - - \pre `!is_leaf()` - \pre `index < Degree::value` - - The operator can be chained. For example, `n[5][2][3]` returns the - third child of the second child of the fifth child of a node `n`. - */ - const Self& operator[](std::size_t index) const { - - CGAL_precondition (!is_leaf()); - CGAL_precondition (index < Degree::value); - - return m_children.get()[index]; - } - - /*! - \brief finds the directly adjacent node in a specific direction - - \pre `!is_null()` - \pre `direction.to_ulong < 2 * Dimension::value` - - Adjacent nodes are found according to several properties: - - adjacent nodes may be larger than the seek node, but never smaller - - a node has at most `2 * Dimension::value` different adjacent nodes (in 3D: left, right, up, down, front, back) - - adjacent nodes are not required to be leaf nodes - - Here's a diagram demonstrating the concept for a Quadtree: - - ``` - +---------------+---------------+ - | | | - | | | - | | | - | A | | - | | | - | | | - | | | - +-------+-------+---+---+-------+ - | | | | | | - | A | (S) +---A---+ | - | | | | | | - +---+---+-------+---+---+-------+ - | | | | | | - +---+---+ A | | | - | | | | | | - +---+---+-------+-------+-------+ - ``` - - - (S) : Seek node - - A : Adjacent node - - Note how the top adjacent node is larger than the seek node. The - right adjacent node is the same size, even though it contains - further subdivisions. - - This implementation returns the adjacent node if it's found. If - there is no adjacent node in that direction, it returns a null - node. - - \param direction which way to find the adjacent node relative to - this one. Each successive bit selects the direction for the - corresponding dimension: for an Octree in 3D, 010 means: negative - direction in X, position direction in Y, negative direction in Z. - - \return the adjacent node if it exists, a null node otherwise. - */ - const Self* adjacent_node(Local_coordinates direction) const { - - // Direction: LEFT RIGHT DOWN UP BACK FRONT - // direction: 000 001 010 011 100 101 - - // Nodes only have up to 2*dim different adjacent nodes (since cubes have 6 sides) - CGAL_precondition(direction.to_ulong() < Dimension::value * 2); - - // The root node has no adjacent nodes! - if (is_root()) return nullptr; - - // The least significant bit indicates the sign (which side of the node) - bool sign = direction[0]; - - // The first two bits indicate the dimension/axis (x, y, z) - uint8_t dimension = uint8_t((direction >> 1).to_ulong()); - - // Create an offset so that the bit-significance lines up with the dimension (e.g. 1, 2, 4 --> 001, 010, 100) - int8_t offset = (uint8_t) 1 << dimension; - - // Finally, apply the sign to the offset - offset = (sign ? offset : -offset); - - // Check if this child has the opposite sign along the direction's axis - if (local_coordinates()[dimension] != sign) { - // This means the adjacent node is a direct sibling, the offset can be applied easily! - return &(*parent())[local_coordinates().to_ulong() + offset]; - } - - // Find the parent's neighbor in that direction, if it exists - const Self* adjacent_node_of_parent = parent()->adjacent_node(direction); - - // If the parent has no neighbor, then this node doesn't have one - if (!adjacent_node_of_parent) return nullptr; - - // If the parent's adjacent node has no children, then it's this node's adjacent node - if (adjacent_node_of_parent->is_leaf()) - return adjacent_node_of_parent; - - // Return the nearest node of the parent by subtracting the offset instead of adding - return &(*adjacent_node_of_parent)[local_coordinates().to_ulong() - offset]; - - } - - /*! - \brief equivalent to `adjacent_node()`, with an adjacency direction - rather than a bitset. - */ - const Self* adjacent_node(Adjacency adjacency) const { - return adjacent_node(std::bitset(static_cast(adjacency))); - } - - /*! - * \brief equivalent to adjacent_node, except non-const - */ - Self* adjacent_node(std::bitset direction) { - return const_cast(const_cast(this)->adjacent_node(direction)); - } - - /*! - * \brief equivalent to adjacent_node, with a Direction rather than a bitset and non-const - */ - Self* adjacent_node(Adjacency adjacency) { - return adjacent_node(std::bitset(static_cast(adjacency))); - } - /// @} /// \name Point Range @@ -505,28 +293,6 @@ class Orthtree::Node { rhs.m_global_coordinates == m_global_coordinates; } - // todo: this does what the documentation for operator== claims to do! - static bool is_topology_equal(const Self& a, const Self& b) { - - // If one node is a leaf, and the other isn't, they're not the same - if (a.is_leaf() != b.is_leaf()) - return false; - - // If both nodes are non-leaf nodes - if (!a.is_leaf()) { - - // Check all the children - for (int i = 0; i < Degree::value; ++i) { - // If any child cell is different, they're not the same - if (!is_topology_equal(a[i], b[i])) - return false; - } - } - - // If both nodes are leaf nodes, they must be in the same location - return (a.global_coordinates() == b.global_coordinates()); - } - friend std::ostream& operator<<(std::ostream& os, const Self& node) { return internal::print_orthtree_node(os, node); } diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index b7de300b3c9d..6e51e3ce77cd 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -23,6 +23,7 @@ namespace CGAL { /// \cond SKIP_IN_MANUAL +// todo: is this necessary? // Forward declaration template class Orthtree; @@ -32,8 +33,10 @@ namespace Orthtrees { /// \cond SKIP_IN_MANUAL -template -const Node* next_sibling(const Node* n) { +// todo: all of these could be members of Orthtree + +template +const typename Tree::Node* next_sibling(const Tree &orthtree, const typename Tree::Node* n) { // Passing null returns the first node if (nullptr == n) @@ -46,25 +49,25 @@ const Node* next_sibling(const Node* n) { // Find out which child this is std::size_t index = n->local_coordinates().to_ulong(); - constexpr static int degree = Node::Degree::value; + constexpr static int degree = Tree::Node::Degree::value; // Return null if this is the last child if (int(index) == degree - 1) return nullptr; // Otherwise, return the next child - return &((*n->parent())[index + 1]); + return &(orthtree.children(*n->parent())[index + 1]); } -template -const Node* next_sibling_up(const Node* n) { +template +const typename Tree::Node* next_sibling_up(const Tree &orthtree, const typename Tree::Node* n) { if (!n || n->is_root()) return nullptr; auto up = n->parent(); while (nullptr != up) { - if (nullptr != next_sibling(up)) - return next_sibling(up); + if (nullptr != next_sibling(orthtree, up)) + return next_sibling(orthtree, up); if (up->is_root()) return nullptr; @@ -74,41 +77,42 @@ const Node* next_sibling_up(const Node* n) { return nullptr; } -template -const Node* deepest_first_child(const Node* n) { +template +const typename Tree::Node* deepest_first_child(const Tree &orthtree, const typename Tree::Node* n) { - if (!n) + if (n == nullptr) return nullptr; // Find the deepest child on the left auto first = n; while (!first->is_leaf()) - first = &(*first)[0]; + first = &orthtree.children(*first)[0]; return first; } -template -const Node& first_child_at_depth(const Node* n, std::size_t depth) { + +template +const typename Tree::Node* first_child_at_depth(const Tree &orthtree, const typename Tree::Node* n, std::size_t depth) { if (!n) return nullptr; - std::stack todo; + std::stack todo; todo.push(n); if (n->depth() == depth) return n; while (!todo.empty()) { - const Node* node = todo.top(); + const typename Tree::Node* node = todo.top(); todo.pop(); if (node->depth() == depth) return node; if (!node->is_leaf()) - for (int i = 0; i < Node::Degree::value; ++i) - todo.push(&((*node)[std::size_t(Node::Degree::value - 1 - i)])); + for (int i = 0; i < Tree::Node::Degree::value; ++i) + todo.push(&((*node)[std::size_t(Tree::Node::Degree::value - 1 - i)])); } return nullptr; @@ -124,23 +128,31 @@ const Node& first_child_at_depth(const Node* n, std::size_t depth) { \cgalModels `OrthtreeTraversal` */ +template struct Preorder_traversal { +private: - template - const Node* first(const Node* root) const { - return root; + using Node = typename Tree::Node; + + const Tree& m_orthtree; + +public: + + Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + const Node* first() const { + return &m_orthtree.root(); } - template const Node* next(const Node* n) const { if (n->is_leaf()) { - auto next = next_sibling(n); + auto next = next_sibling(m_orthtree, n); if (nullptr == next) { - return next_sibling_up(n); + return next_sibling_up(m_orthtree, n); } return next; @@ -148,7 +160,7 @@ struct Preorder_traversal { } else { // Return the first child of this node - return &(*n)[0]; + return &m_orthtree.children(*n)[0]; } } @@ -162,18 +174,25 @@ struct Preorder_traversal { \cgalModels `OrthtreeTraversal` */ +template struct Postorder_traversal { +private: - template - const Node* first(const Node* root) const { + using Node = typename Tree::Node; + + const Tree& m_orthtree; - return deepest_first_child(root); +public: + + Postorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + const Node* first() const { + return deepest_first_child(m_orthtree, m_orthtree.root()); } - template const Node* next(const Node* n) const { - auto next = deepest_first_child(next_sibling(n)); + auto next = deepest_first_child(m_orthtree, next_sibling(m_orthtree, n)); if (!next) next = n->parent(); @@ -190,21 +209,28 @@ struct Postorder_traversal { \cgalModels `OrthtreeTraversal` */ +template struct Leaves_traversal { +private: - template - const Node* first(const Node* root) const { + using Node = typename Tree::Node; + + const Tree& m_orthtree; - return deepest_first_child(root); +public: + + Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + const Node* first() const { + return deepest_first_child(m_orthtree, &m_orthtree.root()); } - template const Node* next(const Node* n) const { - auto next = deepest_first_child(next_sibling(n)); + auto next = deepest_first_child(m_orthtree, next_sibling(m_orthtree, n)); if (!next) - next = deepest_first_child(next_sibling_up(n)); + next = deepest_first_child(m_orthtree, next_sibling_up(m_orthtree, n)); return next; } @@ -219,38 +245,39 @@ struct Leaves_traversal { \cgalModels `OrthtreeTraversal` */ +template struct Level_traversal { - private: - const std::size_t depth; + using Node = typename Tree::Node; + + const Tree& m_orthtree; + const std::size_t m_depth; public: /*! constructs a `depth`-level traversal. */ - Level_traversal(std::size_t depth) : depth(depth) {} + Level_traversal(const Tree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} template - const Node* first(const Node* root) const { - return first_child_at_depth(root, depth); + const Node* first() const { + return first_child_at_depth(m_orthtree, m_orthtree.root(), m_depth); } template const Node* next(const Node* n) const { - // fixme: leftover from debugging? - std::cerr << depth << " "; - const Node* next = next_sibling(n); + const Node* next = next_sibling(m_orthtree, n); if (!next) { const Node* up = n; do { - up = next_sibling_up(up); + up = next_sibling_up(m_orthtree, up); if (!up) return nullptr; - next = first_child_at_depth(up, depth); + next = first_child_at_depth(m_orthtree, up, m_depth); } while (!next); } diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 1d4e55535d05..feee130ad7f8 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -42,36 +42,39 @@ int main(void) { std::cout << octree << std::endl; // Root node should have no siblings - assert(octree.root().adjacent_node(0) == nullptr); - assert(octree.root().adjacent_node(1) == nullptr); - assert(octree.root().adjacent_node(2) == nullptr); - assert(octree.root().adjacent_node(3) == nullptr); - assert(octree.root().adjacent_node(4) == nullptr); - assert(octree.root().adjacent_node(5) == nullptr); + assert(octree.adjacent_node(octree.root(), 0) == nullptr); + assert(octree.adjacent_node(octree.root(), 1) == nullptr); + assert(octree.adjacent_node(octree.root(), 2) == nullptr); + assert(octree.adjacent_node(octree.root(), 3) == nullptr); + assert(octree.adjacent_node(octree.root(), 4) == nullptr); + assert(octree.adjacent_node(octree.root(), 5) == nullptr); // Left Top Front node should have siblings to the Right, Down, and Back - auto left_top_back = octree.root()[Traits::LEFT_TOP_BACK]; + auto left_top_back = octree.children(octree.root())[Traits::LEFT_TOP_BACK]; - assert(&octree.root()[Traits::RIGHT_TOP_BACK] == left_top_back.adjacent_node(Traits::RIGHT)); - assert(&octree.root()[Traits::LEFT_BOTTOM_BACK] == left_top_back.adjacent_node(Traits::DOWN)); - assert(&octree.root()[Traits::LEFT_TOP_FRONT] == left_top_back.adjacent_node(Traits::FRONT)); - assert(left_top_back.adjacent_node(Traits::LEFT) == nullptr); - assert(left_top_back.adjacent_node(Traits::UP) == nullptr); - assert(left_top_back.adjacent_node(Traits::BACK) == nullptr); + assert(&octree.children(octree.root())[Traits::RIGHT_TOP_BACK] == octree.adjacent_node(left_top_back, Traits::RIGHT)); + assert( + &octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK] == octree.adjacent_node(left_top_back, Traits::DOWN)); + assert(&octree.children(octree.root())[Traits::LEFT_TOP_FRONT] == octree.adjacent_node(left_top_back, Traits::FRONT)); + assert(octree.adjacent_node(left_top_back, Traits::LEFT) == nullptr); + assert(octree.adjacent_node(left_top_back, Traits::UP) == nullptr); + assert(octree.adjacent_node(left_top_back, Traits::BACK) == nullptr); - std::cout << octree.root()[Traits::LEFT_BOTTOM_BACK] << std::endl; + std::cout << octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK] << std::endl; - auto right_top_back_of_left_bottom_back = octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::RIGHT_TOP_BACK]; - assert(&octree.root()[Traits::LEFT_BOTTOM_BACK][Traits::LEFT_TOP_BACK] == - right_top_back_of_left_bottom_back.adjacent_node(Traits::LEFT)); - assert(&octree.root()[Traits::RIGHT_BOTTOM_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT)); - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT) != nullptr); - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::UP) != nullptr); - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::DOWN) != nullptr); - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::FRONT) != nullptr); + auto right_top_back_of_left_bottom_back = + octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::RIGHT_TOP_BACK]; + assert(&octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::LEFT_TOP_BACK] == + octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT)); + assert(&octree.children(octree.root())[Traits::RIGHT_BOTTOM_BACK] == + octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT)); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) != nullptr); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::UP) != nullptr); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::DOWN) != nullptr); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::FRONT) != nullptr); // A node at the back of the tree should have no neighbor to its back - assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::BACK) == nullptr); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::BACK) == nullptr); return 0; } diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index 9ee08bd1083c..fdabd97417c9 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -10,7 +10,7 @@ typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_3 Point; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree -Octree; + Octree; int main(void) { @@ -38,9 +38,10 @@ int main(void) { octree.refine(10, 1); std::cout << "root: " << octree.root().local_coordinates() << std::endl; - std::cout << "first child: " << octree.root()[0].local_coordinates() << std::endl; - std::cout << "fifth child: " << octree.root()[4].local_coordinates() << std::endl; - std::cout << "fifth child of first child: " << octree.root()[0][4].local_coordinates() << std::endl; + std::cout << "first child: " << octree.children(octree.root())[0].local_coordinates() << std::endl; + std::cout << "fifth child: " << octree.children(octree.root())[4].local_coordinates() << std::endl; + std::cout << "fifth child of first child: " + << octree.children(octree.children(octree.root())[0])[4].local_coordinates() << std::endl; // TODO diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 375894cc54cc..097d7442ddcc 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -11,7 +11,7 @@ typedef Kernel::Point_3 Point; typedef Kernel::FT FT; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree -Octree; + Octree; void test_1_node() { @@ -42,14 +42,14 @@ void test_9_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); // Compare the child nodes - assert(octree.bbox(octree.root()[0]) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); - assert(octree.bbox(octree.root()[1]) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); - assert(octree.bbox(octree.root()[2]) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); - assert(octree.bbox(octree.root()[3]) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); - assert(octree.bbox(octree.root()[4]) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); - assert(octree.bbox(octree.root()[5]) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); - assert(octree.bbox(octree.root()[6]) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); - assert(octree.bbox(octree.root()[7]) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.children(octree.root())[0]) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); + assert(octree.bbox(octree.children(octree.root())[1]) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); + assert(octree.bbox(octree.children(octree.root())[2]) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); + assert(octree.bbox(octree.children(octree.root())[3]) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); + assert(octree.bbox(octree.children(octree.root())[4]) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); + assert(octree.bbox(octree.children(octree.root())[5]) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); + assert(octree.bbox(octree.children(octree.root())[6]) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); + assert(octree.bbox(octree.children(octree.root())[7]) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); } void test_25_nodes() { @@ -69,34 +69,50 @@ void test_25_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); // Compare the child nodes - assert(octree.bbox(octree.root()[0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); - assert(octree.bbox(octree.root()[1]) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); - assert(octree.bbox(octree.root()[2]) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); - assert(octree.bbox(octree.root()[3]) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); - assert(octree.bbox(octree.root()[4]) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); - assert(octree.bbox(octree.root()[5]) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); - assert(octree.bbox(octree.root()[6]) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); - assert(octree.bbox(octree.root()[7]) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.children(octree.root())[0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); + assert(octree.bbox(octree.children(octree.root())[1]) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); + assert(octree.bbox(octree.children(octree.root())[2]) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); + assert(octree.bbox(octree.children(octree.root())[3]) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); + assert(octree.bbox(octree.children(octree.root())[4]) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); + assert(octree.bbox(octree.children(octree.root())[5]) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); + assert(octree.bbox(octree.children(octree.root())[6]) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); + assert(octree.bbox(octree.children(octree.root())[7]) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); // Compare children of the first child - assert(octree.bbox(octree.root()[0][0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); - assert(octree.bbox(octree.root()[0][1]) == CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); - assert(octree.bbox(octree.root()[0][2]) == CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); - assert(octree.bbox(octree.root()[0][3]) == CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); - assert(octree.bbox(octree.root()[0][4]) == CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); - assert(octree.bbox(octree.root()[0][5]) == CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); - assert(octree.bbox(octree.root()[0][6]) == CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); - assert(octree.bbox(octree.root()[0][7]) == CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[0]) == + CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[1]) == + CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[2]) == + CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[3]) == + CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[4]) == + CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[5]) == + CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[6]) == + CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); + assert(octree.bbox(octree.children(octree.children(octree.root())[0])[7]) == + CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); // Compare children of the last child - assert(octree.bbox(octree.root()[7][0]) == CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); - assert(octree.bbox(octree.root()[7][1]) == CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); - assert(octree.bbox(octree.root()[7][2]) == CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); - assert(octree.bbox(octree.root()[7][3]) == CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); - assert(octree.bbox(octree.root()[7][4]) == CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); - assert(octree.bbox(octree.root()[7][5]) == CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); - assert(octree.bbox(octree.root()[7][6]) == CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); - assert(octree.bbox(octree.root()[7][7]) == CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[0]) == + CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[1]) == + CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[2]) == + CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[3]) == + CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[4]) == + CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[5]) == + CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[6]) == + CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); + assert(octree.bbox(octree.children(octree.children(octree.root())[7])[7]) == + CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); } int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 686068028fcd..9399d70585d7 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -15,17 +15,17 @@ typedef Kernel::Point_3 Point; typedef Kernel::FT FT; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Leaves_traversal Leaves_traversal; +typedef CGAL::Orthtrees::Leaves_traversal Leaves_traversal; std::size_t count_jumps(Octree &octree) { std::size_t jumps = 0; - for (auto &node : octree.traverse(Leaves_traversal())) { + for (auto &node : octree.traverse()) { for (int direction = 0; direction < 6; ++direction) { - auto adjacent_node = node.adjacent_node(direction); + auto adjacent_node = octree.adjacent_node(node, direction); if (adjacent_node == nullptr) continue; diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index b731ec04e331..b4bf0a21d9c6 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -46,7 +46,7 @@ int main(void) { auto query = Point{1, 1, 1}; // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // A point should only intersect one node @@ -63,15 +63,15 @@ int main(void) { auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(4 == nodes.size()); - assert(octree.root()[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]); - assert(octree.root()[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]); - assert(octree.root()[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]); - assert(octree.root()[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]); + assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]); + assert(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]); + assert(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]); + assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]); } // Intersection with a ray @@ -81,19 +81,22 @@ int main(void) { auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0}); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(8 == nodes.size()); - assert(octree.root()[Octree::Traits::LEFT_BOTTOM_BACK] == *nodes[0]); - assert(octree.root()[Octree::Traits::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == *nodes[1]); - assert(octree.root()[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]); - assert(octree.root()[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]); - assert(octree.root()[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]); - assert(octree.root()[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]); - assert(octree.root()[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]); - assert(octree.root()[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]); + assert(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_BACK] == *nodes[0]); + assert( + octree.children(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_BACK])[Octree::Traits::LEFT_TOP_FRONT] + == *nodes[1] + ); + assert(octree.children(octree.root())[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]); + assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]); + assert(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]); + assert(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]); + assert(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]); + assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]); } return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index d601e3614660..54c7d2e2281b 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -13,7 +13,7 @@ typedef Kernel::Point_3 Point; typedef Kernel::FT FT; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree -Octree; + Octree; void test_1_point() { @@ -52,24 +52,24 @@ void test_8_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.root()[0] == octree.locate({-1, -1, -1})); - assert(octree.root()[1] == octree.locate({1, -1, -1})); - assert(octree.root()[2] == octree.locate({-1, 1, -1})); - assert(octree.root()[3] == octree.locate({1, 1, -1})); - assert(octree.root()[4] == octree.locate({-1, -1, 1})); - assert(octree.root()[5] == octree.locate({1, -1, 1})); - assert(octree.root()[6] == octree.locate({-1, 1, 1})); - assert(octree.root()[7] == octree.locate({1, 1, 1})); + assert(octree.children(octree.root())[0] == octree.locate({-1, -1, -1})); + assert(octree.children(octree.root())[1] == octree.locate({1, -1, -1})); + assert(octree.children(octree.root())[2] == octree.locate({-1, 1, -1})); + assert(octree.children(octree.root())[3] == octree.locate({1, 1, -1})); + assert(octree.children(octree.root())[4] == octree.locate({-1, -1, 1})); + assert(octree.children(octree.root())[5] == octree.locate({1, -1, 1})); + assert(octree.children(octree.root())[6] == octree.locate({-1, 1, 1})); + assert(octree.children(octree.root())[7] == octree.locate({1, 1, 1})); // Points adjacent to the existing points should also end up in the same place - assert(octree.root()[0] == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.root()[1] == octree.locate({1.1, -1.1, -1.1})); - assert(octree.root()[2] == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.root()[3] == octree.locate({1.1, 1.1, -1.1})); - assert(octree.root()[4] == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.root()[5] == octree.locate({1.1, -1.1, 1.1})); - assert(octree.root()[6] == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.root()[7] == octree.locate({1.1, 1.1, 1.1})); + assert(octree.children(octree.root())[0] == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.children(octree.root())[1] == octree.locate({1.1, -1.1, -1.1})); + assert(octree.children(octree.root())[2] == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.children(octree.root())[3] == octree.locate({1.1, 1.1, -1.1})); + assert(octree.children(octree.root())[4] == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.children(octree.root())[5] == octree.locate({1.1, -1.1, 1.1})); + assert(octree.children(octree.root())[6] == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.children(octree.root())[7] == octree.locate({1.1, 1.1, 1.1})); } @@ -93,24 +93,24 @@ void test_10_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.root()[0] == octree.locate({-1, -1, -1})); - assert(octree.root()[1] == octree.locate({1, -1, -1})); - assert(octree.root()[2] == octree.locate({-1, 1, -1})); - assert(octree.root()[3][3][3] == octree.locate({1, 1, -1})); - assert(octree.root()[4][4][4] == octree.locate({-1, -1, 1})); - assert(octree.root()[5] == octree.locate({1, -1, 1})); - assert(octree.root()[6] == octree.locate({-1, 1, 1})); - assert(octree.root()[7] == octree.locate({1, 1, 1})); + assert(octree.children(octree.root())[0] == octree.locate({-1, -1, -1})); + assert(octree.children(octree.root())[1] == octree.locate({1, -1, -1})); + assert(octree.children(octree.root())[2] == octree.locate({-1, 1, -1})); + assert(octree.children(octree.children(octree.children(octree.root())[3])[3])[3] == octree.locate({1, 1, -1})); + assert(octree.children(octree.children(octree.children(octree.root())[4])[4])[4] == octree.locate({-1, -1, 1})); + assert(octree.children(octree.root())[5] == octree.locate({1, -1, 1})); + assert(octree.children(octree.root())[6] == octree.locate({-1, 1, 1})); + assert(octree.children(octree.root())[7] == octree.locate({1, 1, 1})); // Points adjacent to the existing points might end up in different places - assert(octree.root()[0] == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.root()[1] == octree.locate({1.1, -1.1, -1.1})); - assert(octree.root()[2] == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.root()[3][3][3] == octree.locate({1.1, 1.1, -1.1})); - assert(octree.root()[4][4][4] == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.root()[5] == octree.locate({1.1, -1.1, 1.1})); - assert(octree.root()[6] == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.root()[7] == octree.locate({1.1, 1.1, 1.1})); + assert(octree.children(octree.root())[0] == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.children(octree.root())[1] == octree.locate({1.1, -1.1, -1.1})); + assert(octree.children(octree.root())[2] == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.children(octree.children(octree.children(octree.root())[3])[3])[3] == octree.locate({1.1, 1.1, -1.1})); + assert(octree.children(octree.children(octree.children(octree.root())[4])[4])[4] == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.children(octree.root())[5] == octree.locate({1.1, -1.1, 1.1})); + assert(octree.children(octree.root())[6] == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.children(octree.root())[7] == octree.locate({1.1, 1.1, 1.1})); } diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index ae4549588df5..30c251e7b3b2 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -43,7 +43,7 @@ void test_2_points() { // The octree should have been split once Octree other(points, points.point_map()); other.split(other.root()); - assert(Node::is_topology_equal(other.root(), octree.root())); + assert(Octree::is_topology_equal(other, octree)); assert(1 == octree.depth()); } @@ -62,9 +62,9 @@ void test_4_points() { // The octree should have been split once on the first level, and twice on the second Octree other(points, points.point_map()); other.split(other.root()); - other.split(other.root()[3]); - other.split(other.root()[7]); - assert(Node::is_topology_equal(other.root(), octree.root())); + other.split(other.children(other.root())[3]); + other.split(other.children(other.root())[7]); + assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); } diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 57853df8dfaf..99664d21cb1d 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -12,7 +12,7 @@ typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_3 Point; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; +typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; bool test_preorder_1_node() { @@ -53,7 +53,7 @@ bool test_preorder_9_nodes() { assert(*iter == octree.root()); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.root()[i])); + assert((*iter == octree.children(octree.root())[i])); } return true; @@ -79,28 +79,28 @@ bool test_preorder_25_nodes() { auto iter = nodes.begin(); assert(*iter == octree.root()); iter++; - assert((*iter == octree.root()[0])); + assert((*iter == octree.children(octree.root())[0])); iter++; - assert((*iter == octree.root()[1])); + assert((*iter == octree.children(octree.root())[1])); iter++; - assert((*iter == octree.root()[2])); + assert((*iter == octree.children(octree.root())[2])); iter++; - assert((*iter == octree.root()[3])); + assert((*iter == octree.children(octree.root())[3])); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.root()[3][i])); + assert((*iter == octree.children(octree.children(octree.root())[3])[i])); } iter++; - assert((*iter == octree.root()[4])); + assert((*iter == octree.children(octree.root())[4])); iter++; - assert((*iter == octree.root()[5])); + assert((*iter == octree.children(octree.root())[5])); iter++; - assert((*iter == octree.root()[6])); + assert((*iter == octree.children(octree.root())[6])); iter++; - assert((*iter == octree.root()[7])); + assert((*iter == octree.children(octree.root())[7])); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.root()[7][i])); + assert((*iter == octree.children(octree.children(octree.root())[7])[i])); } return true; From 6dec072b0052726a708e3dd7131ab23bfdbbe637 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 29 Mar 2023 16:38:45 +0200 Subject: [PATCH 006/520] Node parent access is now done through the tree --- Orthtree/include/CGAL/Orthtree.h | 20 +++++++++++++++++--- Orthtree/include/CGAL/Orthtree/Node.h | 14 -------------- Orthtree/include/CGAL/Orthtree/Traversals.h | 19 +++++++++---------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 0e2a79e816f8..4b39fde2767f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -396,7 +396,7 @@ class Orthtree { // Skip if this neighbor is a direct sibling (it's guaranteed to be the same depth) // TODO: This check might be redundant, if it doesn't affect performance maybe I could remove it - if (neighbor->parent() == node->parent()) + if (&parent(*neighbor) == &parent(*node)) continue; // If it's already been split, skip it @@ -639,6 +639,20 @@ class Orthtree { /// \name Node Access /// @{ + /*! + \brief returns this node's parent. + \pre `!is_root()` + */ + const Node& parent(const Node& node) const { + CGAL_precondition (!node.is_root()); + return *node.m_parent; + } + + Node& parent(Node& node) { + CGAL_precondition (!node.is_root()); + return *node.m_parent; + } + using Children = typename Node::Children; Children& children(Node& node) { @@ -836,11 +850,11 @@ class Orthtree { // Check if this child has the opposite sign along the direction's axis if (node.local_coordinates()[dimension] != sign) { // This means the adjacent node is a direct sibling, the offset can be applied easily! - return &children(*node.parent())[node.local_coordinates().to_ulong() + offset]; + return &children(parent(node))[node.local_coordinates().to_ulong() + offset]; } // Find the parent's neighbor in that direction, if it exists - const Node* adjacent_node_of_parent = adjacent_node(*node.parent(), direction); + const Node* adjacent_node_of_parent = adjacent_node(parent(node), direction); // If the parent has no neighbor, then this node doesn't have one if (adjacent_node_of_parent == nullptr) return nullptr; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index 98acd0bd2cb0..c75ac1b9a009 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -226,20 +226,6 @@ class Orthtree::Node { /// @} - /// \name Adjacency - /// @{ - - /*! - \brief returns this node's parent. - \pre `!is_root()` - */ - const Self* parent() const { - CGAL_precondition (!is_root()); - return m_parent; - } - - /// @} - /// \name Point Range /// @{ diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 6e51e3ce77cd..5f3dcc54e74a 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -36,9 +36,9 @@ namespace Orthtrees { // todo: all of these could be members of Orthtree template -const typename Tree::Node* next_sibling(const Tree &orthtree, const typename Tree::Node* n) { +const typename Tree::Node* next_sibling(const Tree& orthtree, const typename Tree::Node* n) { - // Passing null returns the first node + // todo: maybe this should take a reference? if (nullptr == n) return nullptr; @@ -55,30 +55,29 @@ const typename Tree::Node* next_sibling(const Tree &orthtree, const typename Tre return nullptr; // Otherwise, return the next child - return &(orthtree.children(*n->parent())[index + 1]); + return &(orthtree.children(orthtree.parent(*n))[index + 1]); } template -const typename Tree::Node* next_sibling_up(const Tree &orthtree, const typename Tree::Node* n) { +const typename Tree::Node* next_sibling_up(const Tree& orthtree, const typename Tree::Node* n) { if (!n || n->is_root()) return nullptr; - auto up = n->parent(); + auto up = &orthtree.parent(*n); while (nullptr != up) { if (nullptr != next_sibling(orthtree, up)) return next_sibling(orthtree, up); - if (up->is_root()) return nullptr; - - up = up->parent(); + // todo: this could be cleaned up; it's probably not necessary to involve pointers here + up = up->is_root() ? nullptr : &orthtree.parent(*up); } return nullptr; } template -const typename Tree::Node* deepest_first_child(const Tree &orthtree, const typename Tree::Node* n) { +const typename Tree::Node* deepest_first_child(const Tree& orthtree, const typename Tree::Node* n) { if (n == nullptr) return nullptr; @@ -92,7 +91,7 @@ const typename Tree::Node* deepest_first_child(const Tree &orthtree, const typen template -const typename Tree::Node* first_child_at_depth(const Tree &orthtree, const typename Tree::Node* n, std::size_t depth) { +const typename Tree::Node* first_child_at_depth(const Tree& orthtree, const typename Tree::Node* n, std::size_t depth) { if (!n) return nullptr; From 14726a1e4145b950a6b68447cd6b97275aaa014b Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 29 Mar 2023 22:46:52 +0200 Subject: [PATCH 007/520] Add functions to get node indices & retrieve nodes by index --- Orthtree/include/CGAL/Orthtree.h | 94 ++++++++++++++++- Orthtree/include/CGAL/Orthtree/Traversals.h | 106 ++------------------ 2 files changed, 101 insertions(+), 99 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 4b39fde2767f..d908de7adf06 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -436,12 +436,22 @@ class Orthtree { \brief provides read-write access to the root node, and by extension the rest of the tree. - todo: why wasn't this provided previously? - \return a reference to the root node of the tree. */ Node& root() { return m_nodes[0]; } + std::size_t index(const Node& node) const { + return std::distance(m_nodes.data(), &node); + } + + const Node& operator[](std::size_t index) const { + return m_nodes[index]; + } + + Node& operator[](std::size_t index) { + return m_nodes[index]; + } + /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). */ @@ -665,6 +675,86 @@ class Orthtree { return node.m_children.get(); } + const Node* next_sibling(const Node* n) const { + + // todo: maybe this should take a reference? + if (nullptr == n) + return nullptr; + + // If this node has no parent, it has no siblings + if (n->is_root()) + return nullptr; + + // Find out which child this is + std::size_t index = n->local_coordinates().to_ulong(); + + constexpr static int degree = Node::Degree::value; + + // Return null if this is the last child + if (int(index) == degree - 1) + return nullptr; + + // Otherwise, return the next child + return &(children(parent(*n))[index + 1]); + } + + const Node* next_sibling_up(const Node* n) const { + + if (!n || n->is_root()) return nullptr; + + auto up = &parent(*n); + while (nullptr != up) { + + if (nullptr != next_sibling(up)) + return next_sibling(up); + + // todo: this could be cleaned up; it's probably not necessary to involve pointers here + up = up->is_root() ? nullptr : &parent(*up); + } + + return nullptr; + } + + const Node* deepest_first_child(const Node* n) const { + + if (n == nullptr) + return nullptr; + + // Find the deepest child on the left + auto first = n; + while (!first->is_leaf()) + first = &children(*first)[0]; + return first; + } + + + const Node* first_child_at_depth(const Node* n, std::size_t depth) const { + + if (!n) + return nullptr; + + std::stack todo; + todo.push(n); + + if (n->depth() == depth) + return n; + + while (!todo.empty()) { + const Node* node = todo.top(); + todo.pop(); + + if (node->depth() == depth) + return node; + + if (!node->is_leaf()) + for (int i = 0; i < Node::Degree::value; ++i) + todo.push(&((*node)[std::size_t(Node::Degree::value - 1 - i)])); + } + + return nullptr; + } + + /*! \brief splits the node into subnodes. diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 5f3dcc54e74a..8814219305b0 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -31,94 +31,6 @@ class Orthtree; namespace Orthtrees { -/// \cond SKIP_IN_MANUAL - -// todo: all of these could be members of Orthtree - -template -const typename Tree::Node* next_sibling(const Tree& orthtree, const typename Tree::Node* n) { - - // todo: maybe this should take a reference? - if (nullptr == n) - return nullptr; - - // If this node has no parent, it has no siblings - if (n->is_root()) - return nullptr; - - // Find out which child this is - std::size_t index = n->local_coordinates().to_ulong(); - - constexpr static int degree = Tree::Node::Degree::value; - // Return null if this is the last child - if (int(index) == degree - 1) - return nullptr; - - // Otherwise, return the next child - return &(orthtree.children(orthtree.parent(*n))[index + 1]); -} - -template -const typename Tree::Node* next_sibling_up(const Tree& orthtree, const typename Tree::Node* n) { - - if (!n || n->is_root()) return nullptr; - - auto up = &orthtree.parent(*n); - while (nullptr != up) { - - if (nullptr != next_sibling(orthtree, up)) - return next_sibling(orthtree, up); - - // todo: this could be cleaned up; it's probably not necessary to involve pointers here - up = up->is_root() ? nullptr : &orthtree.parent(*up); - } - - return nullptr; -} - -template -const typename Tree::Node* deepest_first_child(const Tree& orthtree, const typename Tree::Node* n) { - - if (n == nullptr) - return nullptr; - - // Find the deepest child on the left - auto first = n; - while (!first->is_leaf()) - first = &orthtree.children(*first)[0]; - return first; -} - - -template -const typename Tree::Node* first_child_at_depth(const Tree& orthtree, const typename Tree::Node* n, std::size_t depth) { - - if (!n) - return nullptr; - - std::stack todo; - todo.push(n); - - if (n->depth() == depth) - return n; - - while (!todo.empty()) { - const typename Tree::Node* node = todo.top(); - todo.pop(); - - if (node->depth() == depth) - return node; - - if (!node->is_leaf()) - for (int i = 0; i < Tree::Node::Degree::value; ++i) - todo.push(&((*node)[std::size_t(Tree::Node::Degree::value - 1 - i)])); - } - - return nullptr; -} - -/// \endcond - /*! \ingroup PkgOrthtreeTraversal \brief A class used for performing a preorder traversal. @@ -147,11 +59,11 @@ struct Preorder_traversal { if (n->is_leaf()) { - auto next = next_sibling(m_orthtree, n); + auto next = m_orthtree.next_sibling(n); if (nullptr == next) { - return next_sibling_up(m_orthtree, n); + return m_orthtree.next_sibling_up(n); } return next; @@ -221,15 +133,15 @@ struct Leaves_traversal { Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} const Node* first() const { - return deepest_first_child(m_orthtree, &m_orthtree.root()); + return m_orthtree.deepest_first_child(&m_orthtree.root()); } const Node* next(const Node* n) const { - auto next = deepest_first_child(m_orthtree, next_sibling(m_orthtree, n)); + auto next = m_orthtree.deepest_first_child(m_orthtree.next_sibling(n)); if (!next) - next = deepest_first_child(m_orthtree, next_sibling_up(m_orthtree, n)); + next = m_orthtree.deepest_first_child(m_orthtree.next_sibling_up(n)); return next; } @@ -262,21 +174,21 @@ struct Level_traversal { template const Node* first() const { - return first_child_at_depth(m_orthtree, m_orthtree.root(), m_depth); + return m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); } template const Node* next(const Node* n) const { - const Node* next = next_sibling(m_orthtree, n); + const Node* next = m_orthtree.next_sibling(n); if (!next) { const Node* up = n; do { - up = next_sibling_up(m_orthtree, up); + up = m_orthtree.next_sibling_up(up); if (!up) return nullptr; - next = first_child_at_depth(m_orthtree, up, m_depth); + next = m_orthtree.first_child_at_depth(up, m_depth); } while (!next); } From 1a1ca5cf28ddf464cb907526a5a79cfa07eff66d Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 31 Mar 2023 15:58:42 +0200 Subject: [PATCH 008/520] Node access is now done solely by index Nodes no longer hold references to their parents and children --- Orthtree/include/CGAL/Orthtree.h | 68 ++++++++----------- Orthtree/include/CGAL/Orthtree/Node.h | 21 +++--- .../CGAL/Orthtree/Traversal_iterator.h | 17 +++-- Orthtree/include/CGAL/Orthtree/Traversals.h | 2 + 4 files changed, 49 insertions(+), 59 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d908de7adf06..8ccb5155a63e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -197,7 +197,8 @@ class Orthtree { , m_range(point_range) , m_point_map(point_map) { - m_nodes.reserve(1'000'000); // todo: temporary, for testing + // fixme: this can be removed once traversal doesn't require pointer stability + m_nodes.reserve(1'000'000); m_nodes.emplace_back(); Array bbox_min; @@ -266,7 +267,11 @@ class Orthtree { , m_point_map(other.m_point_map) , m_nodes(std::move(other.m_nodes)) , m_bbox_min(other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) {} + , m_side_per_depth(other.m_side_per_depth) { + + // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. + other.m_nodes.emplace_back(); + } // Non-necessary but just to be clear on the rule of 5: @@ -471,12 +476,13 @@ class Orthtree { \return a forward input iterator over the nodes of the tree */ template - Node_range traverse(const Traversal& traversal) const { + Node_range traverse(Traversal traversal) const { const Node* first = traversal.first(); - Node_traversal_method_const next - = [&](const Node* n) -> const Node* { return traversal.next(n); }; + Node_traversal_method_const next = [=](const Node* n) -> const Node* { + return traversal.next(n); + }; return boost::make_iterator_range(Traversal_iterator(first, next), Traversal_iterator()); @@ -485,7 +491,7 @@ class Orthtree { // todo: document this convenience function template Node_range traverse() const { - return traverse(Traversal(*this)); + return traverse({*this}); } /*! @@ -655,24 +661,26 @@ class Orthtree { */ const Node& parent(const Node& node) const { CGAL_precondition (!node.is_root()); - return *node.m_parent; + return m_nodes[node.m_parent_index.get()]; } Node& parent(Node& node) { CGAL_precondition (!node.is_root()); - return *node.m_parent; + return m_nodes[node.m_parent_index.get()]; } + // todo: these types can probably be moved out of Node using Children = typename Node::Children; + using Children_const = typename Node::Children_const; - Children& children(Node& node) { + Children children(Node& node) { CGAL_precondition (!node.is_leaf()); - return node.m_children.get(); + return Children{&m_nodes[node.m_children_index.get()], Degree::value}; } - const Children& children(const Node& node) const { + Children_const children(const Node& node) const { CGAL_precondition (!node.is_leaf()); - return node.m_children.get(); + return Children_const{&m_nodes[node.m_children_index.get()], Degree::value}; } const Node* next_sibling(const Node* n) const { @@ -770,12 +778,11 @@ class Orthtree { CGAL_precondition (node.is_leaf()); // Split the node to create children - using Children = typename Node::Children; using Local_coordinates = typename Node::Local_coordinates; - for (int index = 0; index < Degree::value; index++) { - m_nodes.emplace_back(&node, Local_coordinates{index}); + for (int i = 0; i < Degree::value; i++) { + m_nodes.emplace_back(&node, index(node), Local_coordinates{i}); } - node.m_children = Children{&*(m_nodes.end() - Degree::value), Degree::value}; // todo: temporary, for testing + node.m_children_index = m_nodes.size() - Degree::value; // Find the point to around which the node is split Point center = barycenter(node); @@ -792,31 +799,10 @@ class Orthtree { * Idempotent, un-splitting a leaf node has no effect. */ void unsplit(Node& node) { - node.m_children.reset(); + node.m_children_index.reset(); } - // todo: this can be removed when nodes store indices instead of references! - Node deep_copy(const Node& node, Node* parent = nullptr) const { - - Node out; - - out.m_parent = parent; - out.m_points = node.m_points; - out.m_depth = node.m_depth; - out.m_global_coordinates = node.m_global_coordinates; - - if (!node.is_leaf()) { - out.m_children = std::make_shared(); - for (int index = 0; index < Degree::value; index++) - (*out.m_children)[index] = deep_copy((*node.m_children)[index], &out); - } - - return out; - } - - - // TODO: Document this - // TODO: Could this method name be reduced to just "center" ? + // todo: documentation Point barycenter(const Node& node) const { // Determine the side length of this node @@ -999,8 +985,8 @@ class Orthtree { Range_iterator split_point = std::partition( begin, end, [&](const Range_type& a) -> bool { - // This should be done with cartesian iterator but it seems - // complicated to do efficiently + // This should be done with cartesian iterator, + // but it seems complicated to do efficiently return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); } ); diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index c75ac1b9a009..2f563141a279 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -60,6 +60,7 @@ class Orthtree::Node { * \brief Array for containing the child nodes of this node. */ typedef boost::span Children; + typedef boost::span Children_const; /// \endcond /*! @@ -102,11 +103,12 @@ class Orthtree::Node { /// \endcond Point_range m_points; - Self* m_parent = nullptr; // todo: use optional> instead of Self * std::uint8_t m_depth = 0; Global_coordinates m_global_coordinates{}; - boost::optional m_children{}; + // todo + boost::optional m_parent_index{}; + boost::optional m_children_index{}; // Only the Orthtree class has access to the non-default // constructor, mutators, etc. @@ -136,8 +138,10 @@ class Orthtree::Node { \param parent the node containing this one \param index this node's relationship to its parent */ - explicit Node(Self* parent, Local_coordinates local_coordinates) - : m_parent(parent) { + explicit Node(Self* parent, boost::optional parent_index, Local_coordinates local_coordinates) : + m_parent_index(parent_index) { + + // todo: this can be cleaned up; it probably doesn't need to take a reference to the parent if (parent != nullptr) { m_depth = parent->m_depth + 1; @@ -183,7 +187,7 @@ class Orthtree::Node { \pre `!is_null()` */ bool is_root() const { - return m_parent == nullptr; + return !m_parent_index.has_value(); } /*! @@ -191,7 +195,7 @@ class Orthtree::Node { \pre `!is_null()` */ bool is_leaf() const { - return (!m_children.has_value()); + return !m_children_index.has_value(); } /*! @@ -270,10 +274,9 @@ class Orthtree::Node { * \return whether the nodes have different topology. */ bool operator==(const Self& rhs) const { - // todo: This is a trivial implementation, maybe it can be set to =default in c++17? - return rhs.m_parent == m_parent && - //rhs.m_children == m_children && // todo: this might be wrong for deep-copies + return rhs.m_parent_index == m_parent_index && + rhs.m_children_index == m_children_index && rhs.m_points == m_points && rhs.m_depth == m_depth && rhs.m_global_coordinates == m_global_coordinates; diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index 6670b480cd60..d69783260b75 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -30,10 +30,9 @@ namespace CGAL { * * \tparam Value */ -template -class Traversal_iterator : - public boost::iterator_facade, Value, boost::forward_traversal_tag> { - +template +class Traversal_iterator + : public boost::iterator_facade, Value, boost::forward_traversal_tag> { public: /// \name Types @@ -44,7 +43,7 @@ class Traversal_iterator : * * \todo */ - typedef std::function Traversal_function; + typedef std::function Traversal_function; /// @} @@ -68,14 +67,14 @@ class Traversal_iterator : * \param first * \param next */ - Traversal_iterator(Value *first, const Traversal_function &next) : m_value(first), m_next(next) {} + Traversal_iterator(Value* first, const Traversal_function& next) : m_value(first), m_next(next) {} /// @} private: friend class boost::iterator_core_access; - bool equal(Traversal_iterator const &other) const { + bool equal(Traversal_iterator const& other) const { return m_value == other.m_value; } @@ -83,13 +82,13 @@ class Traversal_iterator : m_value = m_next(m_value); } - Value &dereference() const { + Value& dereference() const { return *m_value; } private: - Value *m_value; + Value* m_value; Traversal_function m_next; }; } diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 8814219305b0..0d0a8fd019d2 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -57,6 +57,8 @@ struct Preorder_traversal { const Node* next(const Node* n) const { + if (n == nullptr) return nullptr; + if (n->is_leaf()) { auto next = m_orthtree.next_sibling(n); From a5a92ad7959b232ff30421ed1e395e56a069897d Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 11 Apr 2023 12:17:32 +0200 Subject: [PATCH 009/520] Traversal is now done by index (internally) --- Orthtree/include/CGAL/Orthtree.h | 18 ++++++++--- .../CGAL/Orthtree/Traversal_iterator.h | 32 ++++++++++++------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 8ccb5155a63e..774230374618 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -123,7 +123,7 @@ class Orthtree { #ifdef DOXYGEN_RUNNING typedef unspecified_type Node_range; #else - typedef boost::iterator_range> Node_range; + typedef boost::iterator_range> Node_range; #endif /// \cond SKIP_IN_MANUAL @@ -449,6 +449,12 @@ class Orthtree { return std::distance(m_nodes.data(), &node); } + boost::optional index(const Node* node) const { + if (node == nullptr) return {}; + return index(*node); + } + + const Node& operator[](std::size_t index) const { return m_nodes[index]; } @@ -480,12 +486,14 @@ class Orthtree { const Node* first = traversal.first(); - Node_traversal_method_const next = [=](const Node* n) -> const Node* { - return traversal.next(n); + auto next = [=](const Self& tree, std::size_t index) -> boost::optional { + auto n = traversal.next(&tree[index]); + if (n == nullptr) return {}; + return tree.index(*n); }; - return boost::make_iterator_range(Traversal_iterator(first, next), - Traversal_iterator()); + return boost::make_iterator_range(Traversal_iterator(*this, first, next), + Traversal_iterator()); } // todo: document this convenience function diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index d69783260b75..ba0e0f78f0a4 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -15,6 +15,7 @@ #include #include +#include #include /// \cond SKIP_IN_MANUAL @@ -30,9 +31,9 @@ namespace CGAL { * * \tparam Value */ -template +template class Traversal_iterator - : public boost::iterator_facade, Value, boost::forward_traversal_tag> { + : public boost::iterator_facade, const typename Tree::Node, boost::forward_traversal_tag> { public: /// \name Types @@ -43,7 +44,7 @@ class Traversal_iterator * * \todo */ - typedef std::function Traversal_function; + typedef std::function(const Tree&, std::size_t)> Traversal_function; /// @} @@ -53,43 +54,50 @@ class Traversal_iterator /// @{ /*! - * \brief + * \brief Default constructor, creates an end sentinel * * \todo */ - Traversal_iterator() : m_value(nullptr), m_next() {} + Traversal_iterator() : m_next() {} /*! * \brief * * \todo * + * \param tree * \param first * \param next */ - Traversal_iterator(Value* first, const Traversal_function& next) : m_value(first), m_next(next) {} + Traversal_iterator(const Tree& tree, const typename Tree::Node* first, const Traversal_function& next) : + m_tree(&tree), m_index(tree.index(first)), m_next(next) {} /// @} private: + friend class boost::iterator_core_access; - bool equal(Traversal_iterator const& other) const { - return m_value == other.m_value; + bool equal(Traversal_iterator const& other) const { + return m_index == other.m_index; } void increment() { - m_value = m_next(m_value); + // invoking increment on the sentinel is undefined behavior + m_index = m_next(*m_tree, m_index.get()); } - Value& dereference() const { - return *m_value; + const typename Tree::Node& dereference() const { + return (*m_tree)[m_index.get()]; } private: - Value* m_value; Traversal_function m_next; + + boost::optional m_index = {}; + const Tree* m_tree = nullptr; + }; } From 9103affe72e30f0ab662ee7a3d960877e44183b3 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 11 Apr 2023 17:52:52 +0200 Subject: [PATCH 010/520] Splitting is done by index (internally) Pre-allocating nodes is no longer necessary, since refine() isn't broken by pointer invalidation --- Orthtree/include/CGAL/Orthtree.h | 33 ++++++++++++++++----------- Orthtree/include/CGAL/Orthtree/Node.h | 27 +++++----------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 774230374618..fa03d0b7e689 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -198,7 +198,7 @@ class Orthtree { , m_point_map(point_map) { // fixme: this can be removed once traversal doesn't require pointer stability - m_nodes.reserve(1'000'000); + //m_nodes.reserve(1'000'000); m_nodes.emplace_back(); Array bbox_min; @@ -311,8 +311,8 @@ class Orthtree { m_side_per_depth.resize(1); // Initialize a queue of nodes that need to be refined - std::queue todo; - todo.push(&root()); + std::queue todo; + todo.push(0); // Process items in the queue until it's consumed fully while (!todo.empty()) { @@ -322,21 +322,21 @@ class Orthtree { todo.pop(); // Check if this node needs to be processed - if (split_predicate(*current)) { + if (split_predicate(m_nodes[current])) { // Check if we've reached a new max depth - if (current->depth() == depth()) { + if (m_nodes[current].depth() == depth()) { // Update the side length map m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); } // Split the node, redistributing its points to its children - split((*current)); + split(current); // Process each of its children for (int i = 0; i < Degree::value; ++i) - todo.push(&children(*current)[i]); + todo.push(m_nodes[current].m_children_index.get() + i); } } @@ -780,23 +780,26 @@ class Orthtree { Contents of this node are _not_ propagated automatically. It is the responsibility of the caller to redistribute the points contained by a node after splitting */ - void split(Node& node) { + void split(Node& node) { split(index(node)); } + + void split(std::size_t n) { // Make sure the node hasn't already been split - CGAL_precondition (node.is_leaf()); + CGAL_precondition (m_nodes[n].is_leaf()); // Split the node to create children using Local_coordinates = typename Node::Local_coordinates; for (int i = 0; i < Degree::value; i++) { - m_nodes.emplace_back(&node, index(node), Local_coordinates{i}); + m_nodes.emplace_back(n, m_nodes[n].global_coordinates(), m_nodes[n].depth() + 1, Local_coordinates{i}); } - node.m_children_index = m_nodes.size() - Degree::value; + // todo: this assumes that the new nodes always are allocated at the end + m_nodes[n].m_children_index = m_nodes.size() - Degree::value; // Find the point to around which the node is split - Point center = barycenter(node); + Point center = barycenter(m_nodes[n]); // Add the node's points to its children - reassign_points(node, node.points().begin(), node.points().end(), center); + reassign_points(m_nodes[n], m_nodes[n].points().begin(), m_nodes[n].points().end(), center); } /*! @@ -810,6 +813,10 @@ class Orthtree { node.m_children_index.reset(); } + void unsplit(std::size_t n) { + unsplit(m_nodes[n]); + } + // todo: documentation Point barycenter(const Node& node) const { diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index 2f563141a279..a2245fe7cbae 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -120,7 +120,7 @@ class Orthtree::Node { /// @{ /// \cond SKIP_IN_MANUAL - Node() = default; + Node() = default; // constructs a root node /// \endcond /*! @@ -138,23 +138,13 @@ class Orthtree::Node { \param parent the node containing this one \param index this node's relationship to its parent */ - explicit Node(Self* parent, boost::optional parent_index, Local_coordinates local_coordinates) : - m_parent_index(parent_index) { + explicit Node(std::size_t parent_index, Global_coordinates parent_coordinates, + std::size_t depth, Local_coordinates local_coordinates) : + m_parent_index(parent_index), m_depth(depth) { - // todo: this can be cleaned up; it probably doesn't need to take a reference to the parent + for (int i = 0; i < Dimension::value; i++) + m_global_coordinates[i] = (2 * parent_coordinates[i]) + local_coordinates[i]; - if (parent != nullptr) { - m_depth = parent->m_depth + 1; - - for (int i = 0; i < Dimension::value; i++) - m_global_coordinates[i] = (2 * parent->m_global_coordinates[i]) + local_coordinates[i]; - - } else { - m_depth = 0; - - for (int i = 0; i < Dimension::value; i++) - m_global_coordinates[i] = 0; - } } /// @} @@ -177,11 +167,6 @@ class Orthtree::Node { /// \name Type & Location /// @{ - /*! - \brief returns `true` if the node is null, `false` otherwise. - */ - //bool is_null() const { return (m_data == nullptr); } - /*! \brief returns `true` if the node has no parent, `false` otherwise. \pre `!is_null()` From 4e3fc7edf6b32b9fe939365237f23ce63939fe01 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 12 Apr 2023 11:17:57 +0200 Subject: [PATCH 011/520] Add shims for using functions with indices --- Orthtree/include/CGAL/Orthtree.h | 125 ++++++++++++++++++-------- Orthtree/include/CGAL/Orthtree/Node.h | 8 +- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index fa03d0b7e689..d1bb8f28be3b 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -87,10 +87,8 @@ class Orthtree { /// \cond SKIP_IN_MANUAL typedef typename Traits::Array Array; ///< Array type. - typedef typename Traits::Construct_point_d_from_array - Construct_point_d_from_array; - typedef typename Traits::Construct_bbox_d - Construct_bbox_d; + typedef typename Traits::Construct_point_d_from_array Construct_point_d_from_array; + typedef typename Traits::Construct_bbox_d Construct_bbox_d; /// \endcond /// @} @@ -107,6 +105,11 @@ class Orthtree { */ typedef Dimension_tag<(2 << (Dimension::value - 1))> Degree; + /*! + * \brief Index of a given node in the tree; the root always has index 0. + */ + typedef std::size_t Node_index; + /*! * \brief The Sub-tree / Orthant type. */ @@ -197,8 +200,6 @@ class Orthtree { , m_range(point_range) , m_point_map(point_map) { - // fixme: this can be removed once traversal doesn't require pointer stability - //m_nodes.reserve(1'000'000); m_nodes.emplace_back(); Array bbox_min; @@ -311,7 +312,7 @@ class Orthtree { m_side_per_depth.resize(1); // Initialize a queue of nodes that need to be refined - std::queue todo; + std::queue todo; todo.push(0); // Process items in the queue until it's consumed fully @@ -372,28 +373,27 @@ class Orthtree { void grade() { // Collect all the leaf nodes - std::queue leaf_nodes; + std::queue leaf_nodes; for (const Node& leaf: traverse(Orthtrees::Leaves_traversal(*this))) { - // TODO: I'd like to find a better (safer) way of doing this - leaf_nodes.push(const_cast(&leaf)); + leaf_nodes.push(index(leaf)); } // Iterate over the nodes while (!leaf_nodes.empty()) { // Get the next node - Node* node = leaf_nodes.front(); + Node_index node = leaf_nodes.front(); leaf_nodes.pop(); // Skip this node if it isn't a leaf anymore - if (!node->is_leaf()) + if (!m_nodes[node].is_leaf()) continue; // Iterate over each of the neighbors for (int direction = 0; direction < 6; ++direction) { // Get the neighbor - auto* neighbor = adjacent_node(*node, direction); + auto neighbor = index(adjacent_node(node, direction)); // If it doesn't exist, skip it if (!neighbor) @@ -401,23 +401,23 @@ class Orthtree { // Skip if this neighbor is a direct sibling (it's guaranteed to be the same depth) // TODO: This check might be redundant, if it doesn't affect performance maybe I could remove it - if (&parent(*neighbor) == &parent(*node)) + if (parent(neighbor.get()) == parent(node)) continue; // If it's already been split, skip it - if (!neighbor->is_leaf()) + if (!m_nodes[neighbor.get()].is_leaf()) continue; // Check if the neighbor breaks our grading rule // TODO: could the rule be parametrized? - if ((node->depth() - neighbor->depth()) > 1) { + if ((m_nodes[node].depth() - m_nodes[neighbor.get()].depth()) > 1) { // Split the neighbor - split(*neighbor); + split(neighbor.get()); // Add newly created children to the queue for (int i = 0; i < Degree::value; ++i) { - leaf_nodes.push(&children(*neighbor)[i]); + leaf_nodes.push(index(children(neighbor.get())[i])); } } } @@ -445,21 +445,21 @@ class Orthtree { */ Node& root() { return m_nodes[0]; } - std::size_t index(const Node& node) const { + Node_index index(const Node& node) const { return std::distance(m_nodes.data(), &node); } - boost::optional index(const Node* node) const { + boost::optional index(const Node* node) const { if (node == nullptr) return {}; return index(*node); } - const Node& operator[](std::size_t index) const { + const Node& operator[](Node_index index) const { return m_nodes[index]; } - Node& operator[](std::size_t index) { + Node& operator[](Node_index index) { return m_nodes[index]; } @@ -486,7 +486,7 @@ class Orthtree { const Node* first = traversal.first(); - auto next = [=](const Self& tree, std::size_t index) -> boost::optional { + auto next = [=](const Self& tree, Node_index index) -> boost::optional { auto n = traversal.next(&tree[index]); if (n == nullptr) return {}; return tree.index(*n); @@ -528,6 +528,10 @@ class Orthtree { return construct_bbox(min_corner, max_corner); } + Bbox bbox(Node_index n) const { + return bbox(m_nodes[n]); + } + /// @} /// \name Queries @@ -603,8 +607,7 @@ class Orthtree { template OutputIterator nearest_neighbors(const Sphere& query, OutputIterator output) const { Sphere query_sphere = query; - return nearest_k_neighbors_in_radius(query_sphere, - (std::numeric_limits::max)(), output); + return nearest_k_neighbors_in_radius(query_sphere, (std::numeric_limits::max)(), output); } /*! @@ -677,6 +680,11 @@ class Orthtree { return m_nodes[node.m_parent_index.get()]; } + Node_index parent(Node_index node) const { + CGAL_precondition (!m_nodes[node].is_root()); + return m_nodes[node].m_parent_index.get(); + } + // todo: these types can probably be moved out of Node using Children = typename Node::Children; using Children_const = typename Node::Children_const; @@ -686,11 +694,19 @@ class Orthtree { return Children{&m_nodes[node.m_children_index.get()], Degree::value}; } + Children children(Node_index node) { + return children(m_nodes[node]); + } + Children_const children(const Node& node) const { CGAL_precondition (!node.is_leaf()); return Children_const{&m_nodes[node.m_children_index.get()], Degree::value}; } + Children_const children(Node_index node) const { + return children(m_nodes[node]); + } + const Node* next_sibling(const Node* n) const { // todo: maybe this should take a reference? @@ -702,16 +718,20 @@ class Orthtree { return nullptr; // Find out which child this is - std::size_t index = n->local_coordinates().to_ulong(); + std::size_t local_coordinates = n->local_coordinates().to_ulong(); constexpr static int degree = Node::Degree::value; // Return null if this is the last child - if (int(index) == degree - 1) + if (int(local_coordinates) == degree - 1) return nullptr; // Otherwise, return the next child - return &(children(parent(*n))[index + 1]); + return &(children(parent(*n))[local_coordinates + 1]); + } + + const Node* next_sibling(Node_index n) const { + return next_sibling(&m_nodes[n]); } const Node* next_sibling_up(const Node* n) const { @@ -731,6 +751,10 @@ class Orthtree { return nullptr; } + const Node* next_sibling_up(Node_index n) const { + return next_sibling_up(&m_nodes[n]); + } + const Node* deepest_first_child(const Node* n) const { if (n == nullptr) @@ -743,12 +767,16 @@ class Orthtree { return first; } + const Node* deepest_first_child(Node_index n) const { + return deepest_first_child(&m_nodes[n]); + } const Node* first_child_at_depth(const Node* n, std::size_t depth) const { if (!n) return nullptr; + // todo: use Node_index instead of pointer std::stack todo; todo.push(n); @@ -770,6 +798,10 @@ class Orthtree { return nullptr; } + const Node* first_child_at_depth(Node_index n, std::size_t depth) const { + return first_child_at_depth(&m_nodes[n], depth); + } + /*! \brief splits the node into subnodes. @@ -782,7 +814,7 @@ class Orthtree { */ void split(Node& node) { split(index(node)); } - void split(std::size_t n) { + void split(Node_index n) { // Make sure the node hasn't already been split CGAL_precondition (m_nodes[n].is_leaf()); @@ -813,7 +845,7 @@ class Orthtree { node.m_children_index.reset(); } - void unsplit(std::size_t n) { + void unsplit(Node_index n) { unsplit(m_nodes[n]); } @@ -837,6 +869,9 @@ class Orthtree { return construct_point_d_from_array(bary); } + Point barycenter(Node_index n) const { + return barycenter(m_nodes[n]); + } // todo: this does what the documentation for operator== claims to do! static bool is_topology_equal(const Node& lhsNode, const Self& lhsTree, const Node& rhsNode, const Self& rhsTree) { @@ -959,6 +994,10 @@ class Orthtree { } + const Node* adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { + return adjacent_node(m_nodes[n], direction); + } + /*! \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. */ @@ -966,6 +1005,10 @@ class Orthtree { return adjacent_node(node, std::bitset(static_cast(adjacency))); } + const Node* adjacent_node(Node_index n, typename Node::Adjacency adjacency) const { + return adjacent_node(m_nodes[n], adjacency); + } + /*! * \brief equivalent to adjacent_node, except non-const */ @@ -1018,15 +1061,25 @@ class Orthtree { } + void reassign_points(Node_index n, Range_iterator begin, Range_iterator end, const Point& center, + std::bitset coord = {}, + std::size_t dimension = 0) { + reassign_points(m_nodes[n], begin, end, center, coord, dimension); + } + bool do_intersect(const Node& node, const Sphere& sphere) const { // Create a cubic bounding box from the node Bbox node_cube = bbox(node); - // Check for overlap between the node's box and the sphere as a box, to quickly catch some cases - // FIXME: Activating this causes slower times -// if (!do_overlap(node_cube, sphere.bbox())) -// return false; + // Check for intersection between the node and the sphere + return CGAL::do_intersect(node_cube, sphere); + } + + bool do_intersect(Node_index n, const Sphere& sphere) const { + + // Create a cubic bounding box from the node + Bbox node_cube = bbox(n); // Check for intersection between the node and the sphere return CGAL::do_intersect(node_cube, sphere); @@ -1167,9 +1220,7 @@ class Orthtree { \param output the output iterator to add the found points to (in order of increasing distance) */ template - OutputIterator nearest_k_neighbors_in_radius - (Sphere& query_sphere, - std::size_t k, OutputIterator output) const { + OutputIterator nearest_k_neighbors_in_radius(Sphere& query_sphere, std::size_t k, OutputIterator output) const { // Create an empty list of points std::vector points_list; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index a2245fe7cbae..c6da91748594 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -48,6 +48,7 @@ class Orthtree::Node { typedef Orthtree Enclosing; ///< Orthtree type (enclosing class). typedef typename Enclosing::Dimension Dimension; ///< Dimension type. typedef typename Enclosing::Degree Degree; ///< Degree type. + typedef typename Enclosing::Node_index Node_index; ///< Index type. /*! \brief Self typedef for convenience. @@ -106,9 +107,8 @@ class Orthtree::Node { std::uint8_t m_depth = 0; Global_coordinates m_global_coordinates{}; - // todo - boost::optional m_parent_index{}; - boost::optional m_children_index{}; + boost::optional m_parent_index{}; + boost::optional m_children_index{}; // Only the Orthtree class has access to the non-default // constructor, mutators, etc. @@ -138,7 +138,7 @@ class Orthtree::Node { \param parent the node containing this one \param index this node's relationship to its parent */ - explicit Node(std::size_t parent_index, Global_coordinates parent_coordinates, + explicit Node(Node_index parent_index, Global_coordinates parent_coordinates, std::size_t depth, Local_coordinates local_coordinates) : m_parent_index(parent_index), m_depth(depth) { From 8c12fd3bc7cb69a360efd1b4588f473f0d820fb5 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 12 Apr 2023 12:10:53 +0200 Subject: [PATCH 012/520] Implement desired behavior for repeated refinement --- Orthtree/include/CGAL/Orthtree.h | 13 +++++-------- Orthtree/test/Orthtree/test_octree_refine.cpp | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d1bb8f28be3b..83871150d776 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -304,13 +304,6 @@ class Orthtree { */ void refine(const Split_predicate& split_predicate) { - // If the tree has already been refined, reset it - if (!root().is_leaf()) - unsplit(root()); - - // Reset the side length map, too - m_side_per_depth.resize(1); - // Initialize a queue of nodes that need to be refined std::queue todo; todo.push(0); @@ -335,10 +328,14 @@ class Orthtree { // Split the node, redistributing its points to its children split(current); + } + + // Check if the node has children which need to be processed + if (!m_nodes[current].is_leaf()) { + // Process each of its children for (int i = 0; i < Degree::value; ++i) todo.push(m_nodes[current].m_children_index.get() + i); - } } } diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 30c251e7b3b2..71f95913050c 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -14,6 +14,18 @@ typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; typedef Octree::Node Node; +class Split_nth_child_of_root { + std::size_t m_n; +public: + + Split_nth_child_of_root(std::size_t n) : m_n(n) {} + + template + bool operator()(const Node& node) const { + return (node.depth() == 1 && node.local_coordinates().to_ulong() == m_n); + } +}; + void test_1_point() { // Define the dataset @@ -66,6 +78,12 @@ void test_4_points() { other.split(other.children(other.root())[7]); assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); + + // Applying another splitting criterion shouldn't reset the tree. + octree.refine(Split_nth_child_of_root(2)); + other.split(other.children(other.root())[2]); + assert(Octree::is_topology_equal(other, octree)); + } int main(void) { From 07968655f6785f5f7b9dd88f0524d181fbefe083 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 18 Apr 2023 14:45:00 +0200 Subject: [PATCH 013/520] Add support for traversal by index --- Orthtree/include/CGAL/Orthtree.h | 45 ++++++++++- .../CGAL/Orthtree/Traversal_iterator.h | 81 ++++++++++++++++++- Orthtree/include/CGAL/Orthtree/Traversals.h | 33 ++++++++ 3 files changed, 153 insertions(+), 6 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 83871150d776..27ae8c87c2dc 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -125,8 +125,10 @@ class Orthtree { */ #ifdef DOXYGEN_RUNNING typedef unspecified_type Node_range; + typedef unspecified_type Node_index_range; #else typedef boost::iterator_range> Node_range; + typedef boost::iterator_range> Node_index_range; #endif /// \cond SKIP_IN_MANUAL @@ -371,8 +373,8 @@ class Orthtree { // Collect all the leaf nodes std::queue leaf_nodes; - for (const Node& leaf: traverse(Orthtrees::Leaves_traversal(*this))) { - leaf_nodes.push(index(leaf)); + for (Node_index leaf: traverse_indices(Orthtrees::Leaves_traversal(*this))) { + leaf_nodes.push(leaf); } // Iterate over the nodes @@ -481,7 +483,7 @@ class Orthtree { template Node_range traverse(Traversal traversal) const { - const Node* first = traversal.first(); + auto first = traversal.first_index(); auto next = [=](const Self& tree, Node_index index) -> boost::optional { auto n = traversal.next(&tree[index]); @@ -493,12 +495,30 @@ class Orthtree { Traversal_iterator()); } + template + Node_index_range traverse_indices(Traversal traversal) const { + + Node_index first = traversal.first_index(); + + auto next = [=](const Self& tree, Node_index index) -> boost::optional { + return traversal.next_index(index); + }; + + return boost::make_iterator_range(Index_traversal_iterator(*this, first, next), + Index_traversal_iterator()); + } + // todo: document this convenience function template Node_range traverse() const { return traverse({*this}); } + template + Node_index_range traverse_indices() const { + return traverse_indices({*this}); + } + /*! \brief constructs the bounding box of a node. @@ -526,7 +546,24 @@ class Orthtree { } Bbox bbox(Node_index n) const { - return bbox(m_nodes[n]); + auto node = m_nodes[n]; + + // Determine the side length of this node + FT size = m_side_per_depth[node.depth()]; + + // Determine the location this node should be split + Array min_corner; + Array max_corner; + for (int i = 0; i < Dimension::value; i++) { + + min_corner[i] = m_bbox_min[i] + (node.global_coordinates()[i] * size); + max_corner[i] = min_corner[i] + size; + } + + // Create the bbox + Construct_bbox_d construct_bbox + = m_traits.construct_bbox_d_object(); + return construct_bbox(min_corner, max_corner); } /// @} diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index ba0e0f78f0a4..e1426b26cf72 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -46,6 +46,8 @@ class Traversal_iterator */ typedef std::function(const Tree&, std::size_t)> Traversal_function; + typedef typename Tree::Node_index Node_index; + /// @} public: @@ -69,8 +71,8 @@ class Traversal_iterator * \param first * \param next */ - Traversal_iterator(const Tree& tree, const typename Tree::Node* first, const Traversal_function& next) : - m_tree(&tree), m_index(tree.index(first)), m_next(next) {} + Traversal_iterator(const Tree& tree, Node_index first, const Traversal_function& next) : + m_tree(&tree), m_index(first), m_next(next) {} /// @} @@ -99,6 +101,81 @@ class Traversal_iterator const Tree* m_tree = nullptr; }; + +template +class Index_traversal_iterator : public boost::iterator_facade< + Index_traversal_iterator, + const typename Tree::Node_index, + boost::forward_traversal_tag +> { +public: + + /// \name Types + /// @{ + + /*! + * \brief + * + * \todo + */ + typedef std::function(const Tree&, std::size_t)> Traversal_function; + + typedef typename Tree::Node_index Node_index; + + /// @} + +public: + + /// \name Creation + /// @{ + + /*! + * \brief Default constructor, creates an end sentinel + * + * \todo + */ + Index_traversal_iterator() : m_next() {} + + /*! + * \brief + * + * \todo + * + * \param tree + * \param first + * \param next + */ + Index_traversal_iterator(const Tree& tree, Node_index first, const Traversal_function& next) : + m_tree(&tree), m_index(first), m_next(next) {} + + /// @} + +private: + + friend class boost::iterator_core_access; + + bool equal(Index_traversal_iterator const& other) const { + return m_index == other.m_index; + } + + void increment() { + // invoking increment on the sentinel is undefined behavior + m_index = m_next(*m_tree, m_index.get()); + } + + Node_index dereference() const { + return m_index.get(); + } + +private: + + Traversal_function m_next; + + boost::optional m_index = {}; + const Tree* m_tree = nullptr; + +}; + } /// \endcond diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 0d0a8fd019d2..ea54f1ac5b38 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -55,6 +55,10 @@ struct Preorder_traversal { return &m_orthtree.root(); } + typename Tree::Node_index first_index() const { + return m_orthtree.index(first()).get(); + } + const Node* next(const Node* n) const { if (n == nullptr) return nullptr; @@ -77,6 +81,11 @@ struct Preorder_traversal { } } + + boost::optional next_index(typename Tree::Node_index n) const { + return m_orthtree.index(next(&m_orthtree[n])); + } + }; /*! @@ -103,6 +112,10 @@ struct Postorder_traversal { return deepest_first_child(m_orthtree, m_orthtree.root()); } + typename Tree::Node_index first_index() const { + return m_orthtree.index(first()).get(); + } + const Node* next(const Node* n) const { auto next = deepest_first_child(m_orthtree, next_sibling(m_orthtree, n)); @@ -112,6 +125,10 @@ struct Postorder_traversal { return next; } + + boost::optional next_index(typename Tree::Node_index n) const { + return m_orthtree.index(next(&m_orthtree[n])); + } }; /*! @@ -138,6 +155,10 @@ struct Leaves_traversal { return m_orthtree.deepest_first_child(&m_orthtree.root()); } + typename Tree::Node_index first_index() const { + return m_orthtree.index(first()).get(); + } + const Node* next(const Node* n) const { auto next = m_orthtree.deepest_first_child(m_orthtree.next_sibling(n)); @@ -147,6 +168,10 @@ struct Leaves_traversal { return next; } + + boost::optional next_index(typename Tree::Node_index n) const { + return m_orthtree.index(next(&m_orthtree[n])); + } }; /*! @@ -179,6 +204,10 @@ struct Level_traversal { return m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); } + typename Tree::Node_index first_index() const { + return m_orthtree.index(first()).get(); + } + template const Node* next(const Node* n) const { const Node* next = m_orthtree.next_sibling(n); @@ -196,6 +225,10 @@ struct Level_traversal { return next; } + + boost::optional next_index(typename Tree::Node_index n) const { + return m_orthtree.index(next(&m_orthtree[n])); + } }; } // Orthtree From 62fa9db7a6a878391c775c6b21f9e07ce1e90e9e Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 19 Apr 2023 10:42:48 +0200 Subject: [PATCH 014/520] Preorder traversal is now implemented in terms of indices --- Orthtree/include/CGAL/Orthtree.h | 55 +++++++++++---------- Orthtree/include/CGAL/Orthtree/Traversals.h | 32 ++++++------ 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 27ae8c87c2dc..b3c6e65082c4 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -486,9 +486,7 @@ class Orthtree { auto first = traversal.first_index(); auto next = [=](const Self& tree, Node_index index) -> boost::optional { - auto n = traversal.next(&tree[index]); - if (n == nullptr) return {}; - return tree.index(*n); + return traversal.next_index(index); }; return boost::make_iterator_range(Traversal_iterator(*this, first, next), @@ -527,22 +525,7 @@ class Orthtree { (cubic). */ Bbox bbox(const Node& node) const { - // Determine the side length of this node - FT size = m_side_per_depth[node.depth()]; - - // Determine the location this node should be split - Array min_corner; - Array max_corner; - for (int i = 0; i < Dimension::value; i++) { - - min_corner[i] = m_bbox_min[i] + (node.global_coordinates()[i] * size); - max_corner[i] = min_corner[i] + size; - } - - // Create the bbox - Construct_bbox_d construct_bbox - = m_traits.construct_bbox_d_object(); - return construct_bbox(min_corner, max_corner); + return bbox(index(node)); } Bbox bbox(Node_index n) const { @@ -700,6 +683,14 @@ class Orthtree { /// \name Node Access /// @{ + bool is_leaf(Node_index n) const { + return m_nodes[n].is_leaf(); + } + + bool is_root(Node_index n) const { + return n == 0; + } + /*! \brief returns this node's parent. \pre `!is_root()` @@ -741,7 +732,7 @@ class Orthtree { return children(m_nodes[node]); } - const Node* next_sibling(const Node* n) const { + const Node* _next_sibling(const Node* n) const { // todo: maybe this should take a reference? if (nullptr == n) @@ -764,8 +755,8 @@ class Orthtree { return &(children(parent(*n))[local_coordinates + 1]); } - const Node* next_sibling(Node_index n) const { - return next_sibling(&m_nodes[n]); + const boost::optional next_sibling(Node_index n) const { + return index(_next_sibling(&m_nodes[n])); } const Node* next_sibling_up(const Node* n) const { @@ -785,8 +776,20 @@ class Orthtree { return nullptr; } - const Node* next_sibling_up(Node_index n) const { - return next_sibling_up(&m_nodes[n]); + const boost::optional next_sibling_up(Node_index n) const { + + // the root node has no next sibling up + if (n == 0) return {}; + + auto up = boost::optional{parent(n)}; + while (up) { + + if (next_sibling(up.get())) return {next_sibling(up.get())}; + + up = is_root(up.get()) ? boost::optional{} : boost::optional{parent(up.get())}; + } + + return {}; } const Node* deepest_first_child(const Node* n) const { @@ -801,8 +804,8 @@ class Orthtree { return first; } - const Node* deepest_first_child(Node_index n) const { - return deepest_first_child(&m_nodes[n]); + Node_index deepest_first_child(Node_index n) const { + return index(deepest_first_child(&m_nodes[n])).get(); } const Node* first_child_at_depth(const Node* n, std::size_t depth) const { diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index ea54f1ac5b38..9dd87d241d0e 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -60,30 +60,27 @@ struct Preorder_traversal { } const Node* next(const Node* n) const { + if (n == nullptr || !next_index(m_orthtree.index(*n))) return nullptr; + return &m_orthtree[next_index(m_orthtree.index(*n)).get()]; + } - if (n == nullptr) return nullptr; + boost::optional next_index(typename Tree::Node_index n) const { - if (n->is_leaf()) { + if (m_orthtree.is_leaf(n)) { auto next = m_orthtree.next_sibling(n); - if (nullptr == next) { - - return m_orthtree.next_sibling_up(n); - } + if (!next) + next = m_orthtree.next_sibling_up(n); return next; } else { - // Return the first child of this node - return &m_orthtree.children(*n)[0]; - } + // todo: this shouldn't be necessary, I'd prefer to directly get m_orthtree[n].m_children_index + return m_orthtree.index(m_orthtree.children(n)[0]); - } - - boost::optional next_index(typename Tree::Node_index n) const { - return m_orthtree.index(next(&m_orthtree[n])); + } } }; @@ -170,7 +167,14 @@ struct Leaves_traversal { } boost::optional next_index(typename Tree::Node_index n) const { - return m_orthtree.index(next(&m_orthtree[n])); + + if (m_orthtree.next_sibling(n)) + return m_orthtree.deepest_first_child(m_orthtree.next_sibling(n).get()); + + if (m_orthtree.next_sibling_up(n)) + return m_orthtree.deepest_first_child(m_orthtree.next_sibling_up(n).get()); + + return {}; } }; From b4f04645f147a42fa725d26d1ea9a2523dd4741a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 19 Apr 2023 18:44:30 +0200 Subject: [PATCH 015/520] Level traversal is done by index, has a new unit test --- Orthtree/include/CGAL/Orthtree.h | 30 +++++++++++++++---- Orthtree/include/CGAL/Orthtree/Traversals.h | 22 ++++++++++++-- .../test/Orthtree/test_octree_traverse.cpp | 26 ++++++++++++++++ 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index b3c6e65082c4..49ab9b570ef4 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -507,9 +507,9 @@ class Orthtree { } // todo: document this convenience function - template - Node_range traverse() const { - return traverse({*this}); + template + Node_range traverse(Args&& ...args) const { + return traverse({*this, std::forward(args)...}); } template @@ -691,6 +691,10 @@ class Orthtree { return n == 0; } + std::size_t depth(Node_index n) const { + return m_nodes[n].depth(); + } + /*! \brief returns this node's parent. \pre `!is_root()` @@ -835,8 +839,24 @@ class Orthtree { return nullptr; } - const Node* first_child_at_depth(Node_index n, std::size_t depth) const { - return first_child_at_depth(&m_nodes[n], depth); + boost::optional first_child_at_depth(Node_index n, std::size_t d) const { + + std::queue todo; + todo.push(n); + + while (!todo.empty()) { + Node_index node = todo.front(); + todo.pop(); + + if (depth(node) == d) + return node; + + if (!is_leaf(node)) + for (int i = 0; i < Node::Degree::value; ++i) + todo.push(m_nodes[node].m_children_index.get() + i); + } + + return {}; } diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 9dd87d241d0e..7cc9dfb5349e 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -209,7 +209,8 @@ struct Level_traversal { } typename Tree::Node_index first_index() const { - return m_orthtree.index(first()).get(); + // assumes the tree has at least one child at m_depth + return m_orthtree.first_child_at_depth(m_orthtree.index(m_orthtree.root()), m_depth).get(); } template @@ -231,7 +232,24 @@ struct Level_traversal { } boost::optional next_index(typename Tree::Node_index n) const { - return m_orthtree.index(next(&m_orthtree[n])); + + auto next = m_orthtree.next_sibling(n); + + if (!next) { + + auto up = n; + do { + + if (!m_orthtree.next_sibling_up(up)) + return {}; + + up = m_orthtree.next_sibling_up(up).get(); + next = m_orthtree.first_child_at_depth(up, m_depth); + + } while (!next); + } + + return next; } }; diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 99664d21cb1d..4ec8e1f3e415 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -13,6 +13,7 @@ typedef Kernel::Point_3 Point; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; +typedef CGAL::Orthtrees::Level_traversal Level_traversal; bool test_preorder_1_node() { @@ -59,6 +60,30 @@ bool test_preorder_9_nodes() { return true; } +bool test_level_9_nodes() { + + // Define the dataset + Point_set points; + points.insert({-1, -1, -1}); + points.insert({1, -1, -1}); + + // Create the octree + Octree octree(points, points.point_map()); + octree.refine(10, 1); + + // Create the range + auto nodes = octree.traverse(1); + + // Check each item in the range + auto iter = nodes.begin(); + for (int i = 0; i < 8; ++i) { + assert((*iter == octree.children(octree.root())[i])); + iter++; + } + + return true; +} + bool test_preorder_25_nodes() { // Define the dataset @@ -110,6 +135,7 @@ int main(void) { test_preorder_1_node(); test_preorder_9_nodes(); + test_level_9_nodes(); test_preorder_25_nodes(); return 0; From b9ed5a4221cd854c96fbcacb403191088b30074f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 19 Apr 2023 19:11:59 +0200 Subject: [PATCH 016/520] Relative node access is now done only by index --- Orthtree/include/CGAL/Orthtree.h | 109 +++--------------- Orthtree/include/CGAL/Orthtree/Traversals.h | 2 +- Orthtree/test/Orthtree/test_octree_refine.cpp | 10 +- 3 files changed, 24 insertions(+), 97 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 49ab9b570ef4..8baca1abc534 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -736,48 +736,20 @@ class Orthtree { return children(m_nodes[node]); } - const Node* _next_sibling(const Node* n) const { - - // todo: maybe this should take a reference? - if (nullptr == n) - return nullptr; - - // If this node has no parent, it has no siblings - if (n->is_root()) - return nullptr; - - // Find out which child this is - std::size_t local_coordinates = n->local_coordinates().to_ulong(); - - constexpr static int degree = Node::Degree::value; - - // Return null if this is the last child - if (int(local_coordinates) == degree - 1) - return nullptr; - - // Otherwise, return the next child - return &(children(parent(*n))[local_coordinates + 1]); - } - const boost::optional next_sibling(Node_index n) const { - return index(_next_sibling(&m_nodes[n])); - } - - const Node* next_sibling_up(const Node* n) const { - if (!n || n->is_root()) return nullptr; + // Root node has no siblings + if (is_root(n)) return {}; - auto up = &parent(*n); - while (nullptr != up) { - - if (nullptr != next_sibling(up)) - return next_sibling(up); + // Find out which child this is + std::size_t local_coordinates = m_nodes[n].local_coordinates().to_ulong(); // todo: add local_coordinates(n) helper - // todo: this could be cleaned up; it's probably not necessary to involve pointers here - up = up->is_root() ? nullptr : &parent(*up); - } + // The last child has no more siblings + if (int(local_coordinates) == Node::Degree::value - 1) + return {}; - return nullptr; + // The next sibling is the child of the parent with the following local coordinates + return index(children(parent(n))[local_coordinates + 1]); } const boost::optional next_sibling_up(Node_index n) const { @@ -796,47 +768,13 @@ class Orthtree { return {}; } - const Node* deepest_first_child(const Node* n) const { - - if (n == nullptr) - return nullptr; - - // Find the deepest child on the left - auto first = n; - while (!first->is_leaf()) - first = &children(*first)[0]; - return first; - } - Node_index deepest_first_child(Node_index n) const { - return index(deepest_first_child(&m_nodes[n])).get(); - } - - const Node* first_child_at_depth(const Node* n, std::size_t depth) const { - if (!n) - return nullptr; - - // todo: use Node_index instead of pointer - std::stack todo; - todo.push(n); - - if (n->depth() == depth) - return n; - - while (!todo.empty()) { - const Node* node = todo.top(); - todo.pop(); - - if (node->depth() == depth) - return node; - - if (!node->is_leaf()) - for (int i = 0; i < Node::Degree::value; ++i) - todo.push(&((*node)[std::size_t(Node::Degree::value - 1 - i)])); - } + auto first = n; + while (!is_leaf(first)) + first = index(children(first)[0]); - return nullptr; + return first; } boost::optional first_child_at_depth(Node_index n, std::size_t d) const { @@ -859,7 +797,6 @@ class Orthtree { return {}; } - /*! \brief splits the node into subnodes. @@ -869,8 +806,6 @@ class Orthtree { Contents of this node are _not_ propagated automatically. It is the responsibility of the caller to redistribute the points contained by a node after splitting */ - void split(Node& node) { split(index(node)); } - void split(Node_index n) { // Make sure the node hasn't already been split @@ -898,25 +833,21 @@ class Orthtree { * After un-splitting a node it will be considered a leaf node. * Idempotent, un-splitting a leaf node has no effect. */ - void unsplit(Node& node) { - node.m_children_index.reset(); - } - void unsplit(Node_index n) { - unsplit(m_nodes[n]); + m_nodes[n].m_children_index.reset(); + // todo: the child nodes should be de-allocated! } - // todo: documentation - Point barycenter(const Node& node) const { + Point barycenter(Node_index n) const { // Determine the side length of this node - FT size = m_side_per_depth[node.depth()]; + FT size = m_side_per_depth[depth(n)]; // Determine the location this node should be split Array bary; std::size_t i = 0; for (const FT& f: cartesian_range(m_bbox_min)) { - bary[i] = FT(node.global_coordinates()[i]) * size + size / FT(2) + f; + bary[i] = FT(m_nodes[n].global_coordinates()[i]) * size + size / FT(2) + f; ++i; } @@ -926,10 +857,6 @@ class Orthtree { return construct_point_d_from_array(bary); } - Point barycenter(Node_index n) const { - return barycenter(m_nodes[n]); - } - // todo: this does what the documentation for operator== claims to do! static bool is_topology_equal(const Node& lhsNode, const Self& lhsTree, const Node& rhsNode, const Self& rhsTree) { diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 7cc9dfb5349e..433404988684 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -153,7 +153,7 @@ struct Leaves_traversal { } typename Tree::Node_index first_index() const { - return m_orthtree.index(first()).get(); + return m_orthtree.deepest_first_child(0); } const Node* next(const Node* n) const { diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 71f95913050c..e960d6bd6166 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -54,7 +54,7 @@ void test_2_points() { // The octree should have been split once Octree other(points, points.point_map()); - other.split(other.root()); + other.split(other.index(other.root())); assert(Octree::is_topology_equal(other, octree)); assert(1 == octree.depth()); } @@ -73,15 +73,15 @@ void test_4_points() { // The octree should have been split once on the first level, and twice on the second Octree other(points, points.point_map()); - other.split(other.root()); - other.split(other.children(other.root())[3]); - other.split(other.children(other.root())[7]); + other.split(other.index(other.root())); + other.split(other.index(other.children(other.root())[3])); + other.split(other.index(other.children(other.root())[7])); assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); // Applying another splitting criterion shouldn't reset the tree. octree.refine(Split_nth_child_of_root(2)); - other.split(other.children(other.root())[2]); + other.split(other.index(other.children(other.root())[2])); assert(Octree::is_topology_equal(other, octree)); } From c0a8bbf2c99027932695c2ecbd846067c97a7eef Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 20 Apr 2023 11:57:35 +0200 Subject: [PATCH 017/520] Topology equality is done by index --- Orthtree/include/CGAL/Orthtree.h | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 8baca1abc534..3a5adc3db86d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -576,7 +576,7 @@ class Orthtree { while (!node_for_point->is_leaf()) { // Find the point to split around - Point center = barycenter(*node_for_point); + Point center = barycenter(index(*node_for_point)); // Find the index of the correct sub-node typename Node::Local_coordinates index; @@ -668,7 +668,7 @@ class Orthtree { return false; // If all else is equal, recursively compare the trees themselves - return is_topology_equal(root(), *this, rhs.root(), rhs); + return is_topology_equal(*this, rhs); } /*! @@ -816,11 +816,11 @@ class Orthtree { for (int i = 0; i < Degree::value; i++) { m_nodes.emplace_back(n, m_nodes[n].global_coordinates(), m_nodes[n].depth() + 1, Local_coordinates{i}); } - // todo: this assumes that the new nodes always are allocated at the end + // todo: this assumes that the new nodes are always allocated at the end m_nodes[n].m_children_index = m_nodes.size() - Degree::value; - // Find the point to around which the node is split - Point center = barycenter(m_nodes[n]); + // Find the point around which the node is split + Point center = barycenter(n); // Add the node's points to its children reassign_points(m_nodes[n], m_nodes[n].points().begin(), m_nodes[n].points().end(), center); @@ -857,30 +857,29 @@ class Orthtree { return construct_point_d_from_array(bary); } - // todo: this does what the documentation for operator== claims to do! - static bool is_topology_equal(const Node& lhsNode, const Self& lhsTree, const Node& rhsNode, const Self& rhsTree) { + static bool is_topology_equal(Node_index lhsNode, const Self& lhsTree, Node_index rhsNode, const Self& rhsTree) { // If one node is a leaf, and the other isn't, they're not the same - if (lhsNode.is_leaf() != rhsNode.is_leaf()) + if (lhsTree.is_leaf(lhsNode) != rhsTree.is_leaf(rhsNode)) return false; - // If both nodes are non-leaf nodes - if (!lhsNode.is_leaf()) { + // If both nodes are non-leaf + if (!lhsTree.is_leaf(lhsNode)) { // Check all the children for (int i = 0; i < Degree::value; ++i) { // If any child cell is different, they're not the same - if (!is_topology_equal(lhsTree.children(lhsNode)[i], lhsTree, rhsTree.children(rhsNode)[i], rhsTree)) + if (!is_topology_equal(lhsTree[lhsNode].m_children_index.get() + i, lhsTree, + rhsTree[rhsNode].m_children_index.get() + i, rhsTree)) return false; } } - // If both nodes are leaf nodes, they must be in the same location - return (lhsNode.global_coordinates() == rhsNode.global_coordinates()); + return (lhsTree[lhsNode].global_coordinates() == rhsTree[rhsNode].global_coordinates()); } static bool is_topology_equal(const Self& lhs, const Self& rhs) { - return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); + return is_topology_equal(lhs.index(lhs.root()), lhs, rhs.index(rhs.root()), rhs); } /*! @@ -1138,12 +1137,14 @@ class Orthtree { children_with_distances.reserve(Degree::value); // Fill the list with child nodes - for (int index = 0; index < Degree::value; ++index) { - auto& child_node = children(node)[index]; + for (int i = 0; i < Degree::value; ++i) { + auto& child_node = children(node)[i]; // Add a child to the list, with its distance - children_with_distances.emplace_back(typename Node::Local_coordinates(index), - CGAL::squared_distance(search_bounds.center(), barycenter(child_node))); + children_with_distances.emplace_back( + typename Node::Local_coordinates(i), + CGAL::squared_distance(search_bounds.center(), barycenter(index(child_node))) + ); } // Sort the children by their distance from the search point From b267403b951acdc1e80f2bc9fc0ff615d540773b Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 24 Apr 2023 13:38:05 +0200 Subject: [PATCH 018/520] Recursive intersection now uses indices --- Orthtree/include/CGAL/Orthtree.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 3a5adc3db86d..41cf29c5f30a 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -642,7 +642,7 @@ class Orthtree { */ template OutputIterator intersected_nodes(const Query& query, OutputIterator output) const { - return intersected_nodes_recursive(query, root(), output); + return intersected_nodes_recursive(query, index(root()), output); } /// @} @@ -1167,21 +1167,22 @@ class Orthtree { } template - Node_output_iterator intersected_nodes_recursive(const Query& query, const Node& node, + Node_output_iterator intersected_nodes_recursive(const Query& query, Node_index node, Node_output_iterator output) const { // Check if the current node intersects with the query if (CGAL::do_intersect(query, bbox(node))) { // if this node is a leaf, then it's considered an intersecting node - if (node.is_leaf()) { - *output++ = &node; + if (is_leaf(node)) { + // todo: output iterator should hold indices instead of pointers + *output++ = &m_nodes[node]; return output; } // Otherwise, each of the children need to be checked for (int i = 0; i < Degree::value; ++i) { - intersected_nodes_recursive(query, children(node)[i], output); + intersected_nodes_recursive(query, index(children(node)[i]), output); } } return output; From 782561598eae91cc33db7f9fed83d7131c84597e Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 24 Apr 2023 13:49:13 +0200 Subject: [PATCH 019/520] Intersection writes indices to the output iterator --- Orthtree/include/CGAL/Orthtree.h | 2 +- .../Orthtree/test_octree_intersecting.cpp | 35 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 41cf29c5f30a..3a52e5699893 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -1176,7 +1176,7 @@ class Orthtree { // if this node is a leaf, then it's considered an intersecting node if (is_leaf(node)) { // todo: output iterator should hold indices instead of pointers - *output++ = &m_nodes[node]; + *output++ = node; return output; } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index b4bf0a21d9c6..6c55421cc1bb 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -46,14 +46,14 @@ int main(void) { auto query = Point{1, 1, 1}; // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // A point should only intersect one node assert(1 == nodes.size()); // That node should be the node leaf that contains the point - assert(octree.locate(Point(1, 1, 1)) == *nodes[0]); + assert(octree.index(octree.locate(Point(1, 1, 1))) == nodes[0]); } // Intersection with a sphere @@ -63,15 +63,15 @@ int main(void) { auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(4 == nodes.size()); - assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]); - assert(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]); - assert(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]); - assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK]) == nodes[0]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT]) == nodes[1]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT]) == nodes[2]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT]) == nodes[3]); } // Intersection with a ray @@ -81,22 +81,23 @@ int main(void) { auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0}); // Get a list of nodes intersected - std::vector nodes{}; + std::vector nodes{}; octree.intersected_nodes(query, std::back_inserter(nodes)); // Check the results assert(8 == nodes.size()); - assert(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_BACK] == *nodes[0]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_BACK]) == nodes[0]); assert( - octree.children(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_BACK])[Octree::Traits::LEFT_TOP_FRONT] - == *nodes[1] + octree.index(octree.children( + octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_BACK])[Octree::Traits::LEFT_TOP_FRONT]) + == nodes[1] ); - assert(octree.children(octree.root())[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]); - assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]); - assert(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]); - assert(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]); - assert(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]); - assert(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_TOP_BACK]) == nodes[2]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK]) == nodes[3]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_FRONT]) == nodes[4]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT]) == nodes[5]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT]) == nodes[6]); + assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT]) == nodes[7]); } return EXIT_SUCCESS; From 825abd172719420bc253e4a2f3393723fcd9c845 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 24 Apr 2023 14:01:01 +0200 Subject: [PATCH 020/520] Locate now returns a node index --- Orthtree/include/CGAL/Orthtree.h | 18 ++--- .../Orthtree/test_octree_intersecting.cpp | 2 +- Orthtree/test/Orthtree/test_octree_locate.cpp | 70 ++++++++++--------- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 3a52e5699893..0804949b0f1b 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -564,32 +564,32 @@ class Orthtree { \param point query point. \return the node which contains the point. */ - const Node& locate(const Point& point) const { + const Node_index locate(const Point& point) const { // Make sure the point is enclosed by the orthtree CGAL_precondition (CGAL::do_intersect(point, bbox(root()))); // Start at the root node - auto* node_for_point = &root(); + Node_index node_for_point = index(root()); // Descend the tree until reaching a leaf node - while (!node_for_point->is_leaf()) { + while (!is_leaf(node_for_point)) { // Find the point to split around - Point center = barycenter(index(*node_for_point)); + Point center = barycenter(node_for_point); // Find the index of the correct sub-node - typename Node::Local_coordinates index; + typename Node::Local_coordinates local_coordinates; std::size_t dimension = 0; for (const auto& r: cartesian_range(center, point)) - index[dimension++] = (get < 0 > (r) < get < 1 > (r)); + local_coordinates[dimension++] = (get < 0 > (r) < get < 1 > (r)); // Find the correct sub-node of the current node - node_for_point = &children(*node_for_point)[index.to_ulong()]; + node_for_point = index(children(node_for_point)[local_coordinates.to_ulong()]); } // Return the result - return *node_for_point; + return node_for_point; } /*! @@ -870,7 +870,7 @@ class Orthtree { for (int i = 0; i < Degree::value; ++i) { // If any child cell is different, they're not the same if (!is_topology_equal(lhsTree[lhsNode].m_children_index.get() + i, lhsTree, - rhsTree[rhsNode].m_children_index.get() + i, rhsTree)) + rhsTree[rhsNode].m_children_index.get() + i, rhsTree)) return false; } } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 6c55421cc1bb..30df924756cf 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -53,7 +53,7 @@ int main(void) { assert(1 == nodes.size()); // That node should be the node leaf that contains the point - assert(octree.index(octree.locate(Point(1, 1, 1))) == nodes[0]); + assert(octree.locate(Point(1, 1, 1)) == nodes[0]); } // Intersection with a sphere diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 54c7d2e2281b..15e992729d56 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -26,7 +26,7 @@ void test_1_point() { octree.refine(10, 1); // Because there's only the root node, any point should be placed in it - assert(octree.root() == octree.locate(Point(-1, -1, -1))); + assert(octree.index(octree.root()) == octree.locate(Point(-1, -1, -1))); // These points would be placed outside the root node // assert(octree.root() == octree.locate({0, 0, 0})); @@ -52,24 +52,24 @@ void test_8_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.children(octree.root())[0] == octree.locate({-1, -1, -1})); - assert(octree.children(octree.root())[1] == octree.locate({1, -1, -1})); - assert(octree.children(octree.root())[2] == octree.locate({-1, 1, -1})); - assert(octree.children(octree.root())[3] == octree.locate({1, 1, -1})); - assert(octree.children(octree.root())[4] == octree.locate({-1, -1, 1})); - assert(octree.children(octree.root())[5] == octree.locate({1, -1, 1})); - assert(octree.children(octree.root())[6] == octree.locate({-1, 1, 1})); - assert(octree.children(octree.root())[7] == octree.locate({1, 1, 1})); + assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1, -1, -1})); + assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1, -1, -1})); + assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1, 1, -1})); + assert(octree.index(octree.children(octree.root())[3]) == octree.locate({1, 1, -1})); + assert(octree.index(octree.children(octree.root())[4]) == octree.locate({-1, -1, 1})); + assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1, -1, 1})); + assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1, 1, 1})); + assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1, 1, 1})); // Points adjacent to the existing points should also end up in the same place - assert(octree.children(octree.root())[0] == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.children(octree.root())[1] == octree.locate({1.1, -1.1, -1.1})); - assert(octree.children(octree.root())[2] == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.children(octree.root())[3] == octree.locate({1.1, 1.1, -1.1})); - assert(octree.children(octree.root())[4] == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.children(octree.root())[5] == octree.locate({1.1, -1.1, 1.1})); - assert(octree.children(octree.root())[6] == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.children(octree.root())[7] == octree.locate({1.1, 1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.index(octree.children(octree.root())[3]) == octree.locate({1.1, 1.1, -1.1})); + assert(octree.index(octree.children(octree.root())[4]) == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1.1, 1.1, 1.1})); } @@ -93,24 +93,28 @@ void test_10_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.children(octree.root())[0] == octree.locate({-1, -1, -1})); - assert(octree.children(octree.root())[1] == octree.locate({1, -1, -1})); - assert(octree.children(octree.root())[2] == octree.locate({-1, 1, -1})); - assert(octree.children(octree.children(octree.children(octree.root())[3])[3])[3] == octree.locate({1, 1, -1})); - assert(octree.children(octree.children(octree.children(octree.root())[4])[4])[4] == octree.locate({-1, -1, 1})); - assert(octree.children(octree.root())[5] == octree.locate({1, -1, 1})); - assert(octree.children(octree.root())[6] == octree.locate({-1, 1, 1})); - assert(octree.children(octree.root())[7] == octree.locate({1, 1, 1})); + assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1, -1, -1})); + assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1, -1, -1})); + assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1, 1, -1})); + assert(octree.index(octree.children(octree.children(octree.children(octree.root())[3])[3])[3]) == + octree.locate({1, 1, -1})); + assert(octree.index(octree.children(octree.children(octree.children(octree.root())[4])[4])[4]) == + octree.locate({-1, -1, 1})); + assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1, -1, 1})); + assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1, 1, 1})); + assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1, 1, 1})); // Points adjacent to the existing points might end up in different places - assert(octree.children(octree.root())[0] == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.children(octree.root())[1] == octree.locate({1.1, -1.1, -1.1})); - assert(octree.children(octree.root())[2] == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.children(octree.children(octree.children(octree.root())[3])[3])[3] == octree.locate({1.1, 1.1, -1.1})); - assert(octree.children(octree.children(octree.children(octree.root())[4])[4])[4] == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.children(octree.root())[5] == octree.locate({1.1, -1.1, 1.1})); - assert(octree.children(octree.root())[6] == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.children(octree.root())[7] == octree.locate({1.1, 1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.index(octree.children(octree.children(octree.children(octree.root())[3])[3])[3]) == + octree.locate({1.1, 1.1, -1.1})); + assert(octree.index(octree.children(octree.children(octree.children(octree.root())[4])[4])[4]) == + octree.locate({-1.1, -1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1.1, 1.1, 1.1})); } From d761f6ebc092730fc0edad8c6067181383886e8b Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 10:57:56 +0200 Subject: [PATCH 021/520] adjacent_node() now returns an optional index --- Orthtree/include/CGAL/Orthtree.h | 10 ++-- Orthtree/test/Orthtree/test_node_adjacent.cpp | 54 +++++++++++-------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 0804949b0f1b..463954de12ae 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -392,7 +392,7 @@ class Orthtree { for (int direction = 0; direction < 6; ++direction) { // Get the neighbor - auto neighbor = index(adjacent_node(node, direction)); + auto neighbor = adjacent_node(node, direction); // If it doesn't exist, skip it if (!neighbor) @@ -977,8 +977,8 @@ class Orthtree { } - const Node* adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { - return adjacent_node(m_nodes[n], direction); + boost::optional adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { + return {index(adjacent_node(m_nodes[n], direction))}; } /*! @@ -988,10 +988,6 @@ class Orthtree { return adjacent_node(node, std::bitset(static_cast(adjacency))); } - const Node* adjacent_node(Node_index n, typename Node::Adjacency adjacency) const { - return adjacent_node(m_nodes[n], adjacency); - } - /*! * \brief equivalent to adjacent_node, except non-const */ diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index feee130ad7f8..0c9cab8821d2 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -42,39 +42,47 @@ int main(void) { std::cout << octree << std::endl; // Root node should have no siblings - assert(octree.adjacent_node(octree.root(), 0) == nullptr); - assert(octree.adjacent_node(octree.root(), 1) == nullptr); - assert(octree.adjacent_node(octree.root(), 2) == nullptr); - assert(octree.adjacent_node(octree.root(), 3) == nullptr); - assert(octree.adjacent_node(octree.root(), 4) == nullptr); - assert(octree.adjacent_node(octree.root(), 5) == nullptr); + assert(!octree.adjacent_node(octree.index(octree.root()), 0)); + assert(!octree.adjacent_node(octree.index(octree.root()), 1)); + assert(!octree.adjacent_node(octree.index(octree.root()), 2)); + assert(!octree.adjacent_node(octree.index(octree.root()), 3)); + assert(!octree.adjacent_node(octree.index(octree.root()), 4)); + assert(!octree.adjacent_node(octree.index(octree.root()), 5)); // Left Top Front node should have siblings to the Right, Down, and Back - auto left_top_back = octree.children(octree.root())[Traits::LEFT_TOP_BACK]; + auto left_top_back = octree.index(octree.children(octree.root())[Traits::LEFT_TOP_BACK]); - assert(&octree.children(octree.root())[Traits::RIGHT_TOP_BACK] == octree.adjacent_node(left_top_back, Traits::RIGHT)); + assert(octree.index(&octree.children(octree.root())[Traits::RIGHT_TOP_BACK]) == + octree.adjacent_node(left_top_back, Traits::RIGHT)); assert( - &octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK] == octree.adjacent_node(left_top_back, Traits::DOWN)); - assert(&octree.children(octree.root())[Traits::LEFT_TOP_FRONT] == octree.adjacent_node(left_top_back, Traits::FRONT)); - assert(octree.adjacent_node(left_top_back, Traits::LEFT) == nullptr); - assert(octree.adjacent_node(left_top_back, Traits::UP) == nullptr); - assert(octree.adjacent_node(left_top_back, Traits::BACK) == nullptr); + octree.index(&octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK]) == + octree.adjacent_node(left_top_back, Traits::DOWN)); + assert(octree.index(&octree.children(octree.root())[Traits::LEFT_TOP_FRONT]) == + octree.adjacent_node(left_top_back, Traits::FRONT)); + assert(!octree.adjacent_node(left_top_back, Traits::LEFT)); + assert(!octree.adjacent_node(left_top_back, Traits::UP)); + assert(!octree.adjacent_node(left_top_back, Traits::BACK)); std::cout << octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK] << std::endl; auto right_top_back_of_left_bottom_back = - octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::RIGHT_TOP_BACK]; - assert(&octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::LEFT_TOP_BACK] == - octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT)); - assert(&octree.children(octree.root())[Traits::RIGHT_BOTTOM_BACK] == - octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT)); - assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) != nullptr); - assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::UP) != nullptr); - assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::DOWN) != nullptr); - assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::FRONT) != nullptr); + octree.index(octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::RIGHT_TOP_BACK]); + + assert( + octree.index(&octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::LEFT_TOP_BACK]) == + octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT) + ); + assert( + octree.index(&octree.children(octree.root())[Traits::RIGHT_BOTTOM_BACK]) == + octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) + ); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT).has_value()); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::UP).has_value()); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::DOWN).has_value()); + assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::FRONT).has_value()); // A node at the back of the tree should have no neighbor to its back - assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::BACK) == nullptr); + assert(!octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::BACK)); return 0; } From 871203219322dd81532aaf2f774c68c03a3f1c76 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 11:11:53 +0200 Subject: [PATCH 022/520] adjacent_node() implemented in terms of indices --- Orthtree/include/CGAL/Orthtree.h | 41 ++++++-------------- Orthtree/test/Orthtree/test_octree_grade.cpp | 6 +-- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 463954de12ae..9dde6f991e96 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -931,9 +931,9 @@ class Orthtree { corresponding dimension: for an Octree in 3D, 010 means: negative direction in X, position direction in Y, negative direction in Z. - \return the adjacent node if it exists, a null node otherwise. + \return the index of the adjacent node if it exists, nothing otherwise. */ - const Node* adjacent_node(const Node& node, typename Node::Local_coordinates direction) const { + boost::optional adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 @@ -942,7 +942,7 @@ class Orthtree { CGAL_precondition(direction.to_ulong() < Dimension::value * 2); // The root node has no adjacent nodes! - if (node.is_root()) return nullptr; + if (is_root(n)) return {}; // The least significant bit indicates the sign (which side of the node) bool sign = direction[0]; @@ -957,49 +957,30 @@ class Orthtree { offset = (sign ? offset : -offset); // Check if this child has the opposite sign along the direction's axis - if (node.local_coordinates()[dimension] != sign) { + if (m_nodes[n].local_coordinates()[dimension] != sign) { // This means the adjacent node is a direct sibling, the offset can be applied easily! - return &children(parent(node))[node.local_coordinates().to_ulong() + offset]; + return {index(children(parent(n))[m_nodes[n].local_coordinates().to_ulong() + offset])}; } // Find the parent's neighbor in that direction, if it exists - const Node* adjacent_node_of_parent = adjacent_node(parent(node), direction); + auto adjacent_node_of_parent = adjacent_node(parent(n), direction); // If the parent has no neighbor, then this node doesn't have one - if (adjacent_node_of_parent == nullptr) return nullptr; + if (!adjacent_node_of_parent) return {}; // If the parent's adjacent node has no children, then it's this node's adjacent node - if (adjacent_node_of_parent->is_leaf()) + if (is_leaf(adjacent_node_of_parent.get())) return adjacent_node_of_parent; // Return the nearest node of the parent by subtracting the offset instead of adding - return &children(*adjacent_node_of_parent)[node.local_coordinates().to_ulong() - offset]; - - } - - boost::optional adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { - return {index(adjacent_node(m_nodes[n], direction))}; + return {index(children(adjacent_node_of_parent.get())[m_nodes[n].local_coordinates().to_ulong() - offset])}; } /*! \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. */ - const Node* adjacent_node(const Node& node, typename Node::Adjacency adjacency) const { - return adjacent_node(node, std::bitset(static_cast(adjacency))); - } - - /*! - * \brief equivalent to adjacent_node, except non-const - */ - Node* adjacent_node(Node& node, std::bitset direction) { - return const_cast(const_cast(this)->adjacent_node(node, direction)); - } - - /*! - * \brief equivalent to adjacent_node, with a Direction rather than a bitset and non-const - */ - Node* adjacent_node(Node& node, typename Node::Adjacency adjacency) { - return adjacent_node(node, std::bitset(static_cast(adjacency))); + boost::optional adjacent_node(Node_index n, typename Node::Adjacency adjacency) const { + return adjacent_node(n, std::bitset(static_cast(adjacency))); } /// @} diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 9399d70585d7..d748fb2ee773 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -21,16 +21,16 @@ std::size_t count_jumps(Octree &octree) { std::size_t jumps = 0; - for (auto &node : octree.traverse()) { + for (auto node : octree.traverse_indices()) { for (int direction = 0; direction < 6; ++direction) { auto adjacent_node = octree.adjacent_node(node, direction); - if (adjacent_node == nullptr) + if (!adjacent_node) continue; - if ((node.depth() - adjacent_node->depth()) > 1) + if ((octree.depth(node) - octree.depth(adjacent_node.get())) > 1) jumps++; } } From 43b7543d3bcb2d473ae53538f99d28973c74fa7c Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 11:24:56 +0200 Subject: [PATCH 023/520] nearest_k_neighbors() implemented in terms of indices --- Orthtree/include/CGAL/Orthtree.h | 36 +++++++------------- Orthtree/test/Orthtree/test_octree_grade.cpp | 4 +-- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9dde6f991e96..e2877950355f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -1027,15 +1027,6 @@ class Orthtree { reassign_points(m_nodes[n], begin, end, center, coord, dimension); } - bool do_intersect(const Node& node, const Sphere& sphere) const { - - // Create a cubic bounding box from the node - Bbox node_cube = bbox(node); - - // Check for intersection between the node and the sphere - return CGAL::do_intersect(node_cube, sphere); - } - bool do_intersect(Node_index n, const Sphere& sphere) const { // Create a cubic bounding box from the node @@ -1052,25 +1043,25 @@ class Orthtree { }; struct Node_index_with_distance { - typename Node::Local_coordinates index; + Node_index index; FT distance; - Node_index_with_distance(const typename Node::Local_coordinates& index, - const FT& distance) - : index(index), distance(distance) {} + Node_index_with_distance(const Node_index& index, const FT& distance) : + index(index), distance(distance) {} + }; - void nearest_k_neighbors_recursive(Sphere& search_bounds, const Node& node, + void nearest_k_neighbors_recursive(Sphere& search_bounds, Node_index node, std::vector& results, FT epsilon = 0) const { // Check whether the node has children - if (node.is_leaf()) { + if (is_leaf(node)) { // Base case: the node has no children // Loop through each of the points contained by the node // Note: there might be none, and that should be fine! - for (auto point_index: node.points()) { + for (auto point_index: m_nodes[node].points()) { // Retrieve each point from the orthtree's point map auto point = get(m_point_map, point_index); @@ -1115,12 +1106,12 @@ class Orthtree { // Fill the list with child nodes for (int i = 0; i < Degree::value; ++i) { - auto& child_node = children(node)[i]; + auto child_node = index(children(node)[i]); // Add a child to the list, with its distance children_with_distances.emplace_back( - typename Node::Local_coordinates(i), - CGAL::squared_distance(search_bounds.center(), barycenter(index(child_node))) + child_node, + CGAL::squared_distance(search_bounds.center(), barycenter(child_node)) ); } @@ -1131,13 +1122,12 @@ class Orthtree { // Loop over the children for (auto child_with_distance: children_with_distances) { - auto& child_node = children(node)[child_with_distance.index.to_ulong()]; // Check whether the bounding box of the child intersects with the search bounds - if (do_intersect(child_node, search_bounds)) { + if (do_intersect(child_with_distance.index, search_bounds)) { // Recursively invoke this function - nearest_k_neighbors_recursive(search_bounds, child_node, results); + nearest_k_neighbors_recursive(search_bounds, child_with_distance.index, results); } } } @@ -1191,7 +1181,7 @@ class Orthtree { points_list.reserve(k); // Invoking the recursive function adds those points to the vector (passed by reference) - nearest_k_neighbors_recursive(query_sphere, root(), points_list); + nearest_k_neighbors_recursive(query_sphere, index(root()), points_list); // Add all the points found to the output for (auto& item: points_list) diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index d748fb2ee773..da3b438e3fd0 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -17,11 +17,11 @@ typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; typedef CGAL::Orthtrees::Leaves_traversal Leaves_traversal; -std::size_t count_jumps(Octree &octree) { +std::size_t count_jumps(Octree& octree) { std::size_t jumps = 0; - for (auto node : octree.traverse_indices()) { + for (auto node: octree.traverse_indices()) { for (int direction = 0; direction < 6; ++direction) { From 0707300f9955c7095ddcf659512683f29afd6bdd Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 11:32:11 +0200 Subject: [PATCH 024/520] reassign_points() implemented in terms of indices --- Orthtree/include/CGAL/Orthtree.h | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e2877950355f..236eb3ec93c7 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -823,7 +823,7 @@ class Orthtree { Point center = barycenter(n); // Add the node's points to its children - reassign_points(m_nodes[n], m_nodes[n].points().begin(), m_nodes[n].points().end(), center); + reassign_points(n, m_nodes[n].points().begin(), m_nodes[n].points().end(), center); } /*! @@ -987,15 +987,13 @@ class Orthtree { private: // functions : - void reassign_points(Node& node, Range_iterator begin, Range_iterator end, const Point& center, + void reassign_points(Node_index n, Range_iterator begin, Range_iterator end, const Point& center, std::bitset coord = {}, std::size_t dimension = 0) { // Root case: reached the last dimension if (dimension == Dimension::value) { - - children(node)[coord.to_ulong()].points() = {begin, end}; - + children(n)[coord.to_ulong()].points() = {begin, end}; return; } @@ -1012,19 +1010,12 @@ class Orthtree { // Further subdivide the first side of the split std::bitset coord_left = coord; coord_left[dimension] = false; - reassign_points(node, begin, split_point, center, coord_left, dimension + 1); + reassign_points(n, begin, split_point, center, coord_left, dimension + 1); // Further subdivide the second side of the split std::bitset coord_right = coord; coord_right[dimension] = true; - reassign_points(node, split_point, end, center, coord_right, dimension + 1); - - } - - void reassign_points(Node_index n, Range_iterator begin, Range_iterator end, const Point& center, - std::bitset coord = {}, - std::size_t dimension = 0) { - reassign_points(m_nodes[n], begin, end, center, coord, dimension); + reassign_points(n, split_point, end, center, coord_right, dimension + 1); } bool do_intersect(Node_index n, const Sphere& sphere) const { From 33358ae838c500c3a675f0ad27611097fa9059be Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 12:15:46 +0200 Subject: [PATCH 025/520] Only access node properties through helpers This will make later refactoring to use property maps much simpler --- Orthtree/include/CGAL/Orthtree.h | 90 ++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 236eb3ec93c7..da3ff52fefd8 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -247,7 +247,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = construct_point_d_from_array(bbox_min); m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - root().points() = {point_range.begin(), point_range.end()}; + points(index(root())) = {point_range.begin(), point_range.end()}; } /// @} @@ -296,6 +296,8 @@ class Orthtree { returns a Boolean value (where `true` implies that a `Node` needs to be split, `false` that the `Node` should be a leaf). + todo: split predicate should work with node indices! + This function may be called several times with different predicates: in that case, nodes already split are left unaltered, while nodes that were not split and for which `split_predicate` @@ -321,7 +323,7 @@ class Orthtree { if (split_predicate(m_nodes[current])) { // Check if we've reached a new max depth - if (m_nodes[current].depth() == depth()) { + if (depth(current) == depth()) { // Update the side length map m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); @@ -333,11 +335,11 @@ class Orthtree { } // Check if the node has children which need to be processed - if (!m_nodes[current].is_leaf()) { + if (!is_leaf(current)) { // Process each of its children for (int i = 0; i < Degree::value; ++i) - todo.push(m_nodes[current].m_children_index.get() + i); + todo.push(child(current, i)); } } } @@ -385,7 +387,7 @@ class Orthtree { leaf_nodes.pop(); // Skip this node if it isn't a leaf anymore - if (!m_nodes[node].is_leaf()) + if (!is_leaf(node)) continue; // Iterate over each of the neighbors @@ -404,19 +406,19 @@ class Orthtree { continue; // If it's already been split, skip it - if (!m_nodes[neighbor.get()].is_leaf()) + if (!is_leaf(neighbor.get())) continue; // Check if the neighbor breaks our grading rule // TODO: could the rule be parametrized? - if ((m_nodes[node].depth() - m_nodes[neighbor.get()].depth()) > 1) { + if ((depth(node) - depth(neighbor.get())) > 1) { // Split the neighbor split(neighbor.get()); // Add newly created children to the queue for (int i = 0; i < Degree::value; ++i) { - leaf_nodes.push(index(children(neighbor.get())[i])); + leaf_nodes.push(child(neighbor.get(), i)); } } } @@ -529,17 +531,16 @@ class Orthtree { } Bbox bbox(Node_index n) const { - auto node = m_nodes[n]; // Determine the side length of this node - FT size = m_side_per_depth[node.depth()]; + FT size = m_side_per_depth[depth(n)]; // Determine the location this node should be split Array min_corner; Array max_corner; for (int i = 0; i < Dimension::value; i++) { - min_corner[i] = m_bbox_min[i] + (node.global_coordinates()[i] * size); + min_corner[i] = m_bbox_min[i] + (global_coordinates(n)[i] * size); max_corner[i] = min_corner[i] + size; } @@ -579,13 +580,13 @@ class Orthtree { Point center = barycenter(node_for_point); // Find the index of the correct sub-node - typename Node::Local_coordinates local_coordinates; + typename Node::Local_coordinates local_coords; std::size_t dimension = 0; for (const auto& r: cartesian_range(center, point)) - local_coordinates[dimension++] = (get < 0 > (r) < get < 1 > (r)); + local_coords[dimension++] = (get < 0 > (r) < get < 1 > (r)); // Find the correct sub-node of the current node - node_for_point = index(children(node_for_point)[local_coordinates.to_ulong()]); + node_for_point = child(node_for_point, local_coords.to_ulong()); } // Return the result @@ -695,6 +696,22 @@ class Orthtree { return m_nodes[n].depth(); } + typename Node::Point_range& points(Node_index n) { + return m_nodes[n].points(); + } + + const typename Node::Point_range& points(Node_index n) const { + return m_nodes[n].points(); + } + + typename Node::Global_coordinates global_coordinates(Node_index n) const { + return m_nodes[n].global_coordinates(); + } + + typename Node::Local_coordinates local_coordinates(Node_index n) const { + return m_nodes[n].local_coordinates(); + } + /*! \brief returns this node's parent. \pre `!is_root()` @@ -710,10 +727,15 @@ class Orthtree { } Node_index parent(Node_index node) const { - CGAL_precondition (!m_nodes[node].is_root()); + CGAL_precondition (!is_root(node)); return m_nodes[node].m_parent_index.get(); } + Node_index child(Node_index node, std::size_t i) const { + CGAL_precondition (!is_leaf(node)); + return m_nodes[node].m_children_index.get() + i; + } + // todo: these types can probably be moved out of Node using Children = typename Node::Children; using Children_const = typename Node::Children_const; @@ -742,14 +764,14 @@ class Orthtree { if (is_root(n)) return {}; // Find out which child this is - std::size_t local_coordinates = m_nodes[n].local_coordinates().to_ulong(); // todo: add local_coordinates(n) helper + std::size_t local_coords = local_coordinates(n).to_ulong(); // todo: add local_coordinates(n) helper // The last child has no more siblings - if (int(local_coordinates) == Node::Degree::value - 1) + if (int(local_coords) == Node::Degree::value - 1) return {}; // The next sibling is the child of the parent with the following local coordinates - return index(children(parent(n))[local_coordinates + 1]); + return child(parent(n), local_coords + 1); } const boost::optional next_sibling_up(Node_index n) const { @@ -772,7 +794,7 @@ class Orthtree { auto first = n; while (!is_leaf(first)) - first = index(children(first)[0]); + first = child(first, 0); return first; } @@ -791,7 +813,7 @@ class Orthtree { if (!is_leaf(node)) for (int i = 0; i < Node::Degree::value; ++i) - todo.push(m_nodes[node].m_children_index.get() + i); + todo.push(child(node, i)); } return {}; @@ -809,12 +831,12 @@ class Orthtree { void split(Node_index n) { // Make sure the node hasn't already been split - CGAL_precondition (m_nodes[n].is_leaf()); + CGAL_precondition (is_leaf(n)); // Split the node to create children using Local_coordinates = typename Node::Local_coordinates; for (int i = 0; i < Degree::value; i++) { - m_nodes.emplace_back(n, m_nodes[n].global_coordinates(), m_nodes[n].depth() + 1, Local_coordinates{i}); + m_nodes.emplace_back(n, global_coordinates(n), depth(n) + 1, Local_coordinates{i}); } // todo: this assumes that the new nodes are always allocated at the end m_nodes[n].m_children_index = m_nodes.size() - Degree::value; @@ -823,7 +845,7 @@ class Orthtree { Point center = barycenter(n); // Add the node's points to its children - reassign_points(n, m_nodes[n].points().begin(), m_nodes[n].points().end(), center); + reassign_points(n, points(n).begin(), points(n).end(), center); } /*! @@ -847,7 +869,7 @@ class Orthtree { Array bary; std::size_t i = 0; for (const FT& f: cartesian_range(m_bbox_min)) { - bary[i] = FT(m_nodes[n].global_coordinates()[i]) * size + size / FT(2) + f; + bary[i] = FT(global_coordinates(n)[i]) * size + size / FT(2) + f; ++i; } @@ -869,13 +891,13 @@ class Orthtree { // Check all the children for (int i = 0; i < Degree::value; ++i) { // If any child cell is different, they're not the same - if (!is_topology_equal(lhsTree[lhsNode].m_children_index.get() + i, lhsTree, - rhsTree[rhsNode].m_children_index.get() + i, rhsTree)) + if (!is_topology_equal(lhsTree.child(lhsNode, i), lhsTree, + rhsTree.child(rhsNode, i), rhsTree)) return false; } } - return (lhsTree[lhsNode].global_coordinates() == rhsTree[rhsNode].global_coordinates()); + return (lhsTree.global_coordinates(lhsNode) == rhsTree.global_coordinates(rhsNode)); } static bool is_topology_equal(const Self& lhs, const Self& rhs) { @@ -957,9 +979,9 @@ class Orthtree { offset = (sign ? offset : -offset); // Check if this child has the opposite sign along the direction's axis - if (m_nodes[n].local_coordinates()[dimension] != sign) { + if (local_coordinates(n)[dimension] != sign) { // This means the adjacent node is a direct sibling, the offset can be applied easily! - return {index(children(parent(n))[m_nodes[n].local_coordinates().to_ulong() + offset])}; + return {child(parent(n), local_coordinates(n).to_ulong() + offset)}; } // Find the parent's neighbor in that direction, if it exists @@ -973,7 +995,7 @@ class Orthtree { return adjacent_node_of_parent; // Return the nearest node of the parent by subtracting the offset instead of adding - return {index(children(adjacent_node_of_parent.get())[m_nodes[n].local_coordinates().to_ulong() - offset])}; + return {child(adjacent_node_of_parent.get(), local_coordinates(n).to_ulong() - offset)}; } /*! @@ -993,7 +1015,7 @@ class Orthtree { // Root case: reached the last dimension if (dimension == Dimension::value) { - children(n)[coord.to_ulong()].points() = {begin, end}; + points(child(n, coord.to_ulong())) = {begin, end}; return; } @@ -1052,7 +1074,7 @@ class Orthtree { // Loop through each of the points contained by the node // Note: there might be none, and that should be fine! - for (auto point_index: m_nodes[node].points()) { + for (auto point_index: points(node)) { // Retrieve each point from the orthtree's point map auto point = get(m_point_map, point_index); @@ -1097,7 +1119,7 @@ class Orthtree { // Fill the list with child nodes for (int i = 0; i < Degree::value; ++i) { - auto child_node = index(children(node)[i]); + auto child_node = child(node, i); // Add a child to the list, with its distance children_with_distances.emplace_back( @@ -1140,7 +1162,7 @@ class Orthtree { // Otherwise, each of the children need to be checked for (int i = 0; i < Degree::value; ++i) { - intersected_nodes_recursive(query, index(children(node)[i]), output); + intersected_nodes_recursive(query, child(node, i), output); } } return output; From e7f236678e5420236def44e756ce653c9a652291 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 13:48:21 +0200 Subject: [PATCH 026/520] Replace children() helper with individual child() access --- Orthtree/include/CGAL/Orthtree.h | 24 +------ Orthtree/include/CGAL/Orthtree/Traversals.h | 2 +- Orthtree/test/Orthtree/test_node_adjacent.cpp | 21 +++--- Orthtree/test/Orthtree/test_node_index.cpp | 8 +-- Orthtree/test/Orthtree/test_octree_bbox.cpp | 64 +++++++++---------- .../Orthtree/test_octree_intersecting.cpp | 27 ++++---- Orthtree/test/Orthtree/test_octree_locate.cpp | 64 +++++++++---------- Orthtree/test/Orthtree/test_octree_refine.cpp | 6 +- .../test/Orthtree/test_octree_traverse.cpp | 38 +++++------ 9 files changed, 118 insertions(+), 136 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index da3ff52fefd8..4ee5f7876504 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -514,9 +514,9 @@ class Orthtree { return traverse({*this, std::forward(args)...}); } - template - Node_index_range traverse_indices() const { - return traverse_indices({*this}); + template + Node_index_range traverse_indices(Args&& ...args) const { + return traverse_indices({*this, std::forward(args)...}); } /*! @@ -740,24 +740,6 @@ class Orthtree { using Children = typename Node::Children; using Children_const = typename Node::Children_const; - Children children(Node& node) { - CGAL_precondition (!node.is_leaf()); - return Children{&m_nodes[node.m_children_index.get()], Degree::value}; - } - - Children children(Node_index node) { - return children(m_nodes[node]); - } - - Children_const children(const Node& node) const { - CGAL_precondition (!node.is_leaf()); - return Children_const{&m_nodes[node.m_children_index.get()], Degree::value}; - } - - Children_const children(Node_index node) const { - return children(m_nodes[node]); - } - const boost::optional next_sibling(Node_index n) const { // Root node has no siblings diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 433404988684..ab224840442b 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -78,7 +78,7 @@ struct Preorder_traversal { } else { // todo: this shouldn't be necessary, I'd prefer to directly get m_orthtree[n].m_children_index - return m_orthtree.index(m_orthtree.children(n)[0]); + return m_orthtree.child(n, 0); } } diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 0c9cab8821d2..cb248b0425af 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -50,30 +50,29 @@ int main(void) { assert(!octree.adjacent_node(octree.index(octree.root()), 5)); // Left Top Front node should have siblings to the Right, Down, and Back - auto left_top_back = octree.index(octree.children(octree.root())[Traits::LEFT_TOP_BACK]); + auto left_top_back = octree.child(octree.index(octree.root()), Traits::LEFT_TOP_BACK); - assert(octree.index(&octree.children(octree.root())[Traits::RIGHT_TOP_BACK]) == - octree.adjacent_node(left_top_back, Traits::RIGHT)); - assert( - octree.index(&octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK]) == - octree.adjacent_node(left_top_back, Traits::DOWN)); - assert(octree.index(&octree.children(octree.root())[Traits::LEFT_TOP_FRONT]) == + assert(octree.child(octree.index(octree.root()), Traits::RIGHT_TOP_BACK) == + octree.adjacent_node(left_top_back, Traits::RIGHT).get()); + assert(octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK) == + octree.adjacent_node(left_top_back, Traits::DOWN).get()); + assert(octree.child(octree.index(octree.root()), Traits::LEFT_TOP_FRONT) == octree.adjacent_node(left_top_back, Traits::FRONT)); assert(!octree.adjacent_node(left_top_back, Traits::LEFT)); assert(!octree.adjacent_node(left_top_back, Traits::UP)); assert(!octree.adjacent_node(left_top_back, Traits::BACK)); - std::cout << octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK] << std::endl; + std::cout << octree[octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK)] << std::endl; auto right_top_back_of_left_bottom_back = - octree.index(octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::RIGHT_TOP_BACK]); + octree.child(octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK), Traits::RIGHT_TOP_BACK); assert( - octree.index(&octree.children(octree.children(octree.root())[Traits::LEFT_BOTTOM_BACK])[Traits::LEFT_TOP_BACK]) == + octree.child(octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK), Traits::LEFT_TOP_BACK) == octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT) ); assert( - octree.index(&octree.children(octree.root())[Traits::RIGHT_BOTTOM_BACK]) == + octree.child(octree.index(octree.root()), Traits::RIGHT_BOTTOM_BACK) == octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) ); assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT).has_value()); diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index fdabd97417c9..db4c8ca21709 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -37,11 +37,11 @@ int main(void) { Octree octree(points, points.point_map()); octree.refine(10, 1); - std::cout << "root: " << octree.root().local_coordinates() << std::endl; - std::cout << "first child: " << octree.children(octree.root())[0].local_coordinates() << std::endl; - std::cout << "fifth child: " << octree.children(octree.root())[4].local_coordinates() << std::endl; + std::cout << "root: " << octree.local_coordinates(octree.index(octree.root())) << std::endl; + std::cout << "first child: " << octree.local_coordinates(octree.child(octree.index(octree.root()), 0)) << std::endl; + std::cout << "fifth child: " << octree.local_coordinates(octree.child(octree.index(octree.root()), 4)) << std::endl; std::cout << "fifth child of first child: " - << octree.children(octree.children(octree.root())[0])[4].local_coordinates() << std::endl; + << octree.local_coordinates(octree.child(octree.child(octree.index(octree.root()), 0), 4)) << std::endl; // TODO diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 097d7442ddcc..4f3a4f3dabb3 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -42,14 +42,14 @@ void test_9_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); // Compare the child nodes - assert(octree.bbox(octree.children(octree.root())[0]) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); - assert(octree.bbox(octree.children(octree.root())[1]) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); - assert(octree.bbox(octree.children(octree.root())[2]) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); - assert(octree.bbox(octree.children(octree.root())[3]) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); - assert(octree.bbox(octree.children(octree.root())[4]) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); - assert(octree.bbox(octree.children(octree.root())[5]) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); - assert(octree.bbox(octree.children(octree.root())[6]) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); - assert(octree.bbox(octree.children(octree.root())[7]) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 0)) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 1)) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 2)) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 3)) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 4)) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 5)) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 6)) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 7)) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); } void test_25_nodes() { @@ -69,49 +69,49 @@ void test_25_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); // Compare the child nodes - assert(octree.bbox(octree.children(octree.root())[0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); - assert(octree.bbox(octree.children(octree.root())[1]) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); - assert(octree.bbox(octree.children(octree.root())[2]) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); - assert(octree.bbox(octree.children(octree.root())[3]) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); - assert(octree.bbox(octree.children(octree.root())[4]) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); - assert(octree.bbox(octree.children(octree.root())[5]) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); - assert(octree.bbox(octree.children(octree.root())[6]) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); - assert(octree.bbox(octree.children(octree.root())[7]) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 1)) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 2)) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 3)) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 4)) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 5)) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 6)) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); + assert(octree.bbox(octree.child(octree.index(octree.root()), 7)) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); // Compare children of the first child - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[0]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[1]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 1)) == CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[2]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 2)) == CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[3]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 3)) == CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[4]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 4)) == CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[5]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 5)) == CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[6]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 6)) == CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); - assert(octree.bbox(octree.children(octree.children(octree.root())[0])[7]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 7)) == CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); // Compare children of the last child - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[0]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 0)) == CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[1]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 1)) == CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[2]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 2)) == CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[3]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 3)) == CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[4]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 4)) == CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[5]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 5)) == CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[6]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 6)) == CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); - assert(octree.bbox(octree.children(octree.children(octree.root())[7])[7]) == + assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 7)) == CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 30df924756cf..17df0fd1fec9 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -68,10 +68,10 @@ int main(void) { // Check the results assert(4 == nodes.size()); - assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK]) == nodes[0]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT]) == nodes[1]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT]) == nodes[2]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT]) == nodes[3]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_BACK) == nodes[0]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[1]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_TOP_FRONT) == nodes[2]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); } // Intersection with a ray @@ -86,18 +86,19 @@ int main(void) { // Check the results assert(8 == nodes.size()); - assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_BACK]) == nodes[0]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); assert( - octree.index(octree.children( - octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_BACK])[Octree::Traits::LEFT_TOP_FRONT]) + octree.child(octree.child(octree.index(octree.root()), + Octree::Traits::RIGHT_BOTTOM_BACK), + Octree::Traits::LEFT_TOP_FRONT) == nodes[1] ); - assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_TOP_BACK]) == nodes[2]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_BACK]) == nodes[3]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_BOTTOM_FRONT]) == nodes[4]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_BOTTOM_FRONT]) == nodes[5]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::LEFT_TOP_FRONT]) == nodes[6]); - assert(octree.index(octree.children(octree.root())[Octree::Traits::RIGHT_TOP_FRONT]) == nodes[7]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_TOP_BACK) == nodes[2]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); + assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); } return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 15e992729d56..a43593deeeb6 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -52,24 +52,24 @@ void test_8_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1, -1, -1})); - assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1, -1, -1})); - assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1, 1, -1})); - assert(octree.index(octree.children(octree.root())[3]) == octree.locate({1, 1, -1})); - assert(octree.index(octree.children(octree.root())[4]) == octree.locate({-1, -1, 1})); - assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1, -1, 1})); - assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1, 1, 1})); - assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1, 1, 1})); + assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1, -1, -1})); + assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1, -1, -1})); + assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1, 1, -1})); + assert(octree.child(octree.index(octree.root()), 3) == octree.locate({1, 1, -1})); + assert(octree.child(octree.index(octree.root()), 4) == octree.locate({-1, -1, 1})); + assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1, -1, 1})); + assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1, 1, 1})); + assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points should also end up in the same place - assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1.1, -1.1, -1.1})); - assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.index(octree.children(octree.root())[3]) == octree.locate({1.1, 1.1, -1.1})); - assert(octree.index(octree.children(octree.root())[4]) == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1.1, -1.1, 1.1})); - assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1.1, 1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.child(octree.index(octree.root()), 3) == octree.locate({1.1, 1.1, -1.1})); + assert(octree.child(octree.index(octree.root()), 4) == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1.1, 1.1, 1.1})); } @@ -93,28 +93,28 @@ void test_10_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1, -1, -1})); - assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1, -1, -1})); - assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1, 1, -1})); - assert(octree.index(octree.children(octree.children(octree.children(octree.root())[3])[3])[3]) == + assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1, -1, -1})); + assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1, -1, -1})); + assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1, 1, -1})); + assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 3), 3), 3) == octree.locate({1, 1, -1})); - assert(octree.index(octree.children(octree.children(octree.children(octree.root())[4])[4])[4]) == + assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 4), 4), 4) == octree.locate({-1, -1, 1})); - assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1, -1, 1})); - assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1, 1, 1})); - assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1, 1, 1})); + assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1, -1, 1})); + assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1, 1, 1})); + assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points might end up in different places - assert(octree.index(octree.children(octree.root())[0]) == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.index(octree.children(octree.root())[1]) == octree.locate({1.1, -1.1, -1.1})); - assert(octree.index(octree.children(octree.root())[2]) == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.index(octree.children(octree.children(octree.children(octree.root())[3])[3])[3]) == + assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 3), 3), 3) == octree.locate({1.1, 1.1, -1.1})); - assert(octree.index(octree.children(octree.children(octree.children(octree.root())[4])[4])[4]) == + assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 4), 4), 4) == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.index(octree.children(octree.root())[5]) == octree.locate({1.1, -1.1, 1.1})); - assert(octree.index(octree.children(octree.root())[6]) == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.index(octree.children(octree.root())[7]) == octree.locate({1.1, 1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1.1, 1.1, 1.1})); } diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index e960d6bd6166..63ca3a31fa7a 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -74,14 +74,14 @@ void test_4_points() { // The octree should have been split once on the first level, and twice on the second Octree other(points, points.point_map()); other.split(other.index(other.root())); - other.split(other.index(other.children(other.root())[3])); - other.split(other.index(other.children(other.root())[7])); + other.split(other.child(other.index(other.root()), 3)); + other.split(other.child(other.index(other.root()), 7)); assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); // Applying another splitting criterion shouldn't reset the tree. octree.refine(Split_nth_child_of_root(2)); - other.split(other.index(other.children(other.root())[2])); + other.split(other.child(other.index(other.root()), 2)); assert(Octree::is_topology_equal(other, octree)); } diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 4ec8e1f3e415..82abb26b12ac 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -26,11 +26,11 @@ bool test_preorder_1_node() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse(); + auto nodes = octree.traverse_indices(); // Check each item in the range auto iter = nodes.begin(); - assert(*iter == octree.root()); + assert(*iter == octree.index(octree.root())); return true; } @@ -47,14 +47,14 @@ bool test_preorder_9_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse(); + auto nodes = octree.traverse_indices(); // Check each item in the range auto iter = nodes.begin(); - assert(*iter == octree.root()); + assert(*iter == octree.index(octree.root())); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.children(octree.root())[i])); + assert(*iter == octree.child(octree.index(octree.root()), i)); } return true; @@ -72,12 +72,12 @@ bool test_level_9_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse(1); + auto nodes = octree.traverse_indices(1); // Check each item in the range auto iter = nodes.begin(); for (int i = 0; i < 8; ++i) { - assert((*iter == octree.children(octree.root())[i])); + assert(*iter == octree.child(octree.index(octree.root()), i)); iter++; } @@ -98,34 +98,34 @@ bool test_preorder_25_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse(); + auto nodes = octree.traverse_indices(); // Check each item in the range auto iter = nodes.begin(); - assert(*iter == octree.root()); + assert(*iter == octree.index(octree.root())); iter++; - assert((*iter == octree.children(octree.root())[0])); + assert(*iter == octree.child(octree.index(octree.root()), 0)); iter++; - assert((*iter == octree.children(octree.root())[1])); + assert(*iter == octree.child(octree.index(octree.root()), 1)); iter++; - assert((*iter == octree.children(octree.root())[2])); + assert((*iter == octree.child(octree.index(octree.root()), 2))); iter++; - assert((*iter == octree.children(octree.root())[3])); + assert(*iter == octree.child(octree.index(octree.root()), 3)); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.children(octree.children(octree.root())[3])[i])); + assert(*iter == octree.child(octree.child(octree.index(octree.root()), 3), i)); } iter++; - assert((*iter == octree.children(octree.root())[4])); + assert((*iter == octree.child(octree.index(octree.root()), 4))); iter++; - assert((*iter == octree.children(octree.root())[5])); + assert((*iter == octree.child(octree.index(octree.root()), 5))); iter++; - assert((*iter == octree.children(octree.root())[6])); + assert((*iter == octree.child(octree.index(octree.root()), 6))); iter++; - assert((*iter == octree.children(octree.root())[7])); + assert((*iter == octree.child(octree.index(octree.root()), 7))); for (int i = 0; i < 8; ++i) { iter++; - assert((*iter == octree.children(octree.children(octree.root())[7])[i])); + assert(*iter == octree.child(octree.child(octree.index(octree.root()), 7), i)); } return true; From 53b3278d3f486f55c59a88aea9f1f46793e5730a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 15:21:02 +0200 Subject: [PATCH 027/520] root() now returns an index, like parent() and child() --- Orthtree/include/CGAL/Orthtree.h | 35 ++-------- Orthtree/include/CGAL/Orthtree/Traversals.h | 6 +- Orthtree/test/Orthtree/test_node_adjacent.cpp | 28 ++++---- Orthtree/test/Orthtree/test_node_index.cpp | 8 +-- Orthtree/test/Orthtree/test_octree_bbox.cpp | 64 +++++++++--------- .../test_octree_copy_move_constructors.cpp | 19 +++--- .../Orthtree/test_octree_intersecting.cpp | 24 +++---- Orthtree/test/Orthtree/test_octree_locate.cpp | 66 +++++++++---------- Orthtree/test/Orthtree/test_octree_refine.cpp | 12 ++-- .../test/Orthtree/test_octree_traverse.cpp | 30 ++++----- 10 files changed, 136 insertions(+), 156 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 4ee5f7876504..dce5637e089d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -247,7 +247,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = construct_point_d_from_array(bbox_min); m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - points(index(root())) = {point_range.begin(), point_range.end()}; + points(root()) = {point_range.begin(), point_range.end()}; } /// @} @@ -436,15 +436,8 @@ class Orthtree { \return a const reference to the root node of the tree. */ - const Node& root() const { return m_nodes[0]; } - - /*! - \brief provides read-write access to the root node, and by - extension the rest of the tree. - - \return a reference to the root node of the tree. - */ - Node& root() { return m_nodes[0]; } + // todo: return index instead of ref + Node_index root() const { return 0; } Node_index index(const Node& node) const { return std::distance(m_nodes.data(), &node); @@ -571,7 +564,7 @@ class Orthtree { CGAL_precondition (CGAL::do_intersect(point, bbox(root()))); // Start at the root node - Node_index node_for_point = index(root()); + Node_index node_for_point = root(); // Descend the tree until reaching a leaf node while (!is_leaf(node_for_point)) { @@ -643,7 +636,7 @@ class Orthtree { */ template OutputIterator intersected_nodes(const Query& query, OutputIterator output) const { - return intersected_nodes_recursive(query, index(root()), output); + return intersected_nodes_recursive(query, root(), output); } /// @} @@ -716,16 +709,6 @@ class Orthtree { \brief returns this node's parent. \pre `!is_root()` */ - const Node& parent(const Node& node) const { - CGAL_precondition (!node.is_root()); - return m_nodes[node.m_parent_index.get()]; - } - - Node& parent(Node& node) { - CGAL_precondition (!node.is_root()); - return m_nodes[node.m_parent_index.get()]; - } - Node_index parent(Node_index node) const { CGAL_precondition (!is_root(node)); return m_nodes[node].m_parent_index.get(); @@ -736,10 +719,6 @@ class Orthtree { return m_nodes[node].m_children_index.get() + i; } - // todo: these types can probably be moved out of Node - using Children = typename Node::Children; - using Children_const = typename Node::Children_const; - const boost::optional next_sibling(Node_index n) const { // Root node has no siblings @@ -883,7 +862,7 @@ class Orthtree { } static bool is_topology_equal(const Self& lhs, const Self& rhs) { - return is_topology_equal(lhs.index(lhs.root()), lhs, rhs.index(rhs.root()), rhs); + return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); } /*! @@ -1176,7 +1155,7 @@ class Orthtree { points_list.reserve(k); // Invoking the recursive function adds those points to the vector (passed by reference) - nearest_k_neighbors_recursive(query_sphere, index(root()), points_list); + nearest_k_neighbors_recursive(query_sphere, root(), points_list); // Add all the points found to the output for (auto& item: points_list) diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index ab224840442b..3c5cf2dc43f9 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -52,11 +52,11 @@ struct Preorder_traversal { Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} const Node* first() const { - return &m_orthtree.root(); + return &m_orthtree[m_orthtree.root()]; } typename Tree::Node_index first_index() const { - return m_orthtree.index(first()).get(); + return m_orthtree.root(); } const Node* next(const Node* n) const { @@ -210,7 +210,7 @@ struct Level_traversal { typename Tree::Node_index first_index() const { // assumes the tree has at least one child at m_depth - return m_orthtree.first_child_at_depth(m_orthtree.index(m_orthtree.root()), m_depth).get(); + return m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth).get(); } template diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index cb248b0425af..a150f4581217 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -42,37 +42,37 @@ int main(void) { std::cout << octree << std::endl; // Root node should have no siblings - assert(!octree.adjacent_node(octree.index(octree.root()), 0)); - assert(!octree.adjacent_node(octree.index(octree.root()), 1)); - assert(!octree.adjacent_node(octree.index(octree.root()), 2)); - assert(!octree.adjacent_node(octree.index(octree.root()), 3)); - assert(!octree.adjacent_node(octree.index(octree.root()), 4)); - assert(!octree.adjacent_node(octree.index(octree.root()), 5)); + assert(!octree.adjacent_node(octree.root(), 0)); + assert(!octree.adjacent_node(octree.root(), 1)); + assert(!octree.adjacent_node(octree.root(), 2)); + assert(!octree.adjacent_node(octree.root(), 3)); + assert(!octree.adjacent_node(octree.root(), 4)); + assert(!octree.adjacent_node(octree.root(), 5)); // Left Top Front node should have siblings to the Right, Down, and Back - auto left_top_back = octree.child(octree.index(octree.root()), Traits::LEFT_TOP_BACK); + auto left_top_back = octree.child(octree.root(), Traits::LEFT_TOP_BACK); - assert(octree.child(octree.index(octree.root()), Traits::RIGHT_TOP_BACK) == + assert(octree.child(octree.root(), Traits::RIGHT_TOP_BACK) == octree.adjacent_node(left_top_back, Traits::RIGHT).get()); - assert(octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK) == + assert(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK) == octree.adjacent_node(left_top_back, Traits::DOWN).get()); - assert(octree.child(octree.index(octree.root()), Traits::LEFT_TOP_FRONT) == + assert(octree.child(octree.root(), Traits::LEFT_TOP_FRONT) == octree.adjacent_node(left_top_back, Traits::FRONT)); assert(!octree.adjacent_node(left_top_back, Traits::LEFT)); assert(!octree.adjacent_node(left_top_back, Traits::UP)); assert(!octree.adjacent_node(left_top_back, Traits::BACK)); - std::cout << octree[octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK)] << std::endl; + std::cout << octree[octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK)] << std::endl; auto right_top_back_of_left_bottom_back = - octree.child(octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK), Traits::RIGHT_TOP_BACK); + octree.child(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK), Traits::RIGHT_TOP_BACK); assert( - octree.child(octree.child(octree.index(octree.root()), Traits::LEFT_BOTTOM_BACK), Traits::LEFT_TOP_BACK) == + octree.child(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK), Traits::LEFT_TOP_BACK) == octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT) ); assert( - octree.child(octree.index(octree.root()), Traits::RIGHT_BOTTOM_BACK) == + octree.child(octree.root(), Traits::RIGHT_BOTTOM_BACK) == octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) ); assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT).has_value()); diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index db4c8ca21709..34e6f24f5c62 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -37,11 +37,11 @@ int main(void) { Octree octree(points, points.point_map()); octree.refine(10, 1); - std::cout << "root: " << octree.local_coordinates(octree.index(octree.root())) << std::endl; - std::cout << "first child: " << octree.local_coordinates(octree.child(octree.index(octree.root()), 0)) << std::endl; - std::cout << "fifth child: " << octree.local_coordinates(octree.child(octree.index(octree.root()), 4)) << std::endl; + std::cout << "root: " << octree.local_coordinates(octree.root()) << std::endl; + std::cout << "first child: " << octree.local_coordinates(octree.child(octree.root(), 0)) << std::endl; + std::cout << "fifth child: " << octree.local_coordinates(octree.child(octree.root(), 4)) << std::endl; std::cout << "fifth child of first child: " - << octree.local_coordinates(octree.child(octree.child(octree.index(octree.root()), 0), 4)) << std::endl; + << octree.local_coordinates(octree.child(octree.child(octree.root(), 0), 4)) << std::endl; // TODO diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 4f3a4f3dabb3..8c324de951a5 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -42,14 +42,14 @@ void test_9_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); // Compare the child nodes - assert(octree.bbox(octree.child(octree.index(octree.root()), 0)) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 1)) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 2)) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 3)) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 4)) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 5)) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 6)) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 7)) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.child(octree.root(), 0)) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); + assert(octree.bbox(octree.child(octree.root(), 1)) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); + assert(octree.bbox(octree.child(octree.root(), 2)) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); + assert(octree.bbox(octree.child(octree.root(), 3)) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); + assert(octree.bbox(octree.child(octree.root(), 4)) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); + assert(octree.bbox(octree.child(octree.root(), 5)) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); + assert(octree.bbox(octree.child(octree.root(), 6)) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); + assert(octree.bbox(octree.child(octree.root(), 7)) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); } void test_25_nodes() { @@ -69,49 +69,49 @@ void test_25_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); // Compare the child nodes - assert(octree.bbox(octree.child(octree.index(octree.root()), 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 1)) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 2)) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 3)) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 4)) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 5)) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 6)) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); - assert(octree.bbox(octree.child(octree.index(octree.root()), 7)) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.child(octree.root(), 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); + assert(octree.bbox(octree.child(octree.root(), 1)) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); + assert(octree.bbox(octree.child(octree.root(), 2)) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); + assert(octree.bbox(octree.child(octree.root(), 3)) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); + assert(octree.bbox(octree.child(octree.root(), 4)) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); + assert(octree.bbox(octree.child(octree.root(), 5)) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); + assert(octree.bbox(octree.child(octree.root(), 6)) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); + assert(octree.bbox(octree.child(octree.root(), 7)) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); // Compare children of the first child - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 0)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 1)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 1)) == CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 2)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 2)) == CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 3)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 3)) == CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 4)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 4)) == CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 5)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 5)) == CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 6)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 6)) == CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 0), 7)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 7)) == CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); // Compare children of the last child - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 0)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 0)) == CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 1)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 1)) == CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 2)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 2)) == CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 3)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 3)) == CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 4)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 4)) == CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 5)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 5)) == CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 6)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 6)) == CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); - assert(octree.bbox(octree.child(octree.child(octree.index(octree.root()), 7), 7)) == + assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 7)) == CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); } diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index ae6272fe149e..2b4dd730e47d 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -25,26 +25,27 @@ int main(void) points.insert(*(generator++)); Octree base (points, points.point_map()); - assert (base.root().is_leaf()); // base is not refined yet + assert (base.is_leaf(base.root())); // base is not refined yet Octree copy1 (base); - assert (copy1.root().is_leaf()); // copy1 is thus not refined either + assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either assert (base == copy1); // base should be equal to copy1 base.refine(); - assert (!base.root().is_leaf()); // base is now refined - assert (copy1.root().is_leaf()); // copy1 should be unaffected and still unrefined + assert (!base.is_leaf(base.root())); // base is now refined + assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined assert (base != copy1); // base should be different from copy1 Octree copy2 (base); - assert (!copy2.root().is_leaf()); // copy2 should be refined + assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined assert (base == copy2); // base should be equal to copy2 Octree move (std::move(base)); - assert (!move.root().is_leaf()); // move should be refined - assert (base.root().is_leaf()); // base should be back to init state (unrefined) - assert (copy1.root().is_leaf()); // copy1 still unaffected and still unrefined - assert (!copy2.root().is_leaf()); // copy2 unaffected by move and still refined + assert (!move.is_leaf(move.root())); // move should be refined + // fixme: my linter isn't happy about use-after-move + assert (base.is_leaf(base.root())); // base should be back to init state (unrefined) + assert (copy1.is_leaf(copy1.root())); // copy1 still unaffected and still unrefined + assert (!copy2.is_leaf(copy2.root())); // copy2 unaffected by move and still refined assert (move == copy2); // move should be equal to copy2 return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 17df0fd1fec9..3470ee68b387 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -68,10 +68,10 @@ int main(void) { // Check the results assert(4 == nodes.size()); - assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_BACK) == nodes[0]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[1]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_TOP_FRONT) == nodes[2]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); + assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_BACK) == nodes[0]); + assert(octree.child(octree.root(), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[1]); + assert(octree.child(octree.root(), Octree::Traits::LEFT_TOP_FRONT) == nodes[2]); + assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); } // Intersection with a ray @@ -86,19 +86,19 @@ int main(void) { // Check the results assert(8 == nodes.size()); - assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); + assert(octree.child(octree.root(), Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); assert( - octree.child(octree.child(octree.index(octree.root()), + octree.child(octree.child(octree.root(), Octree::Traits::RIGHT_BOTTOM_BACK), Octree::Traits::LEFT_TOP_FRONT) == nodes[1] ); - assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_TOP_BACK) == nodes[2]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); - assert(octree.child(octree.index(octree.root()), Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); + assert(octree.child(octree.root(), Octree::Traits::LEFT_TOP_BACK) == nodes[2]); + assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); + assert(octree.child(octree.root(), Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); + assert(octree.child(octree.root(), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); + assert(octree.child(octree.root(), Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); + assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); } return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index a43593deeeb6..e3214a8ab116 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -26,7 +26,7 @@ void test_1_point() { octree.refine(10, 1); // Because there's only the root node, any point should be placed in it - assert(octree.index(octree.root()) == octree.locate(Point(-1, -1, -1))); + assert(octree.root() == octree.locate(Point(-1, -1, -1))); // These points would be placed outside the root node // assert(octree.root() == octree.locate({0, 0, 0})); @@ -52,24 +52,24 @@ void test_8_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1, -1, -1})); - assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1, -1, -1})); - assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1, 1, -1})); - assert(octree.child(octree.index(octree.root()), 3) == octree.locate({1, 1, -1})); - assert(octree.child(octree.index(octree.root()), 4) == octree.locate({-1, -1, 1})); - assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1, -1, 1})); - assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1, 1, 1})); - assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1, 1, 1})); + assert(octree.child(octree.root(), 0) == octree.locate({-1, -1, -1})); + assert(octree.child(octree.root(), 1) == octree.locate({1, -1, -1})); + assert(octree.child(octree.root(), 2) == octree.locate({-1, 1, -1})); + assert(octree.child(octree.root(), 3) == octree.locate({1, 1, -1})); + assert(octree.child(octree.root(), 4) == octree.locate({-1, -1, 1})); + assert(octree.child(octree.root(), 5) == octree.locate({1, -1, 1})); + assert(octree.child(octree.root(), 6) == octree.locate({-1, 1, 1})); + assert(octree.child(octree.root(), 7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points should also end up in the same place - assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1.1, -1.1, -1.1})); - assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.child(octree.index(octree.root()), 3) == octree.locate({1.1, 1.1, -1.1})); - assert(octree.child(octree.index(octree.root()), 4) == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1.1, -1.1, 1.1})); - assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1.1, 1.1, 1.1})); + assert(octree.child(octree.root(), 0) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.child(octree.root(), 1) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.child(octree.root(), 2) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.child(octree.root(), 3) == octree.locate({1.1, 1.1, -1.1})); + assert(octree.child(octree.root(), 4) == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.child(octree.root(), 5) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.child(octree.root(), 6) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.child(octree.root(), 7) == octree.locate({1.1, 1.1, 1.1})); } @@ -93,28 +93,28 @@ void test_10_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1, -1, -1})); - assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1, -1, -1})); - assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1, 1, -1})); - assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 3), 3), 3) == + assert(octree.child(octree.root(), 0) == octree.locate({-1, -1, -1})); + assert(octree.child(octree.root(), 1) == octree.locate({1, -1, -1})); + assert(octree.child(octree.root(), 2) == octree.locate({-1, 1, -1})); + assert(octree.child(octree.child(octree.child(octree.root(), 3), 3), 3) == octree.locate({1, 1, -1})); - assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 4), 4), 4) == + assert(octree.child(octree.child(octree.child(octree.root(), 4), 4), 4) == octree.locate({-1, -1, 1})); - assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1, -1, 1})); - assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1, 1, 1})); - assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1, 1, 1})); + assert(octree.child(octree.root(), 5) == octree.locate({1, -1, 1})); + assert(octree.child(octree.root(), 6) == octree.locate({-1, 1, 1})); + assert(octree.child(octree.root(), 7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points might end up in different places - assert(octree.child(octree.index(octree.root()), 0) == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.child(octree.index(octree.root()), 1) == octree.locate({1.1, -1.1, -1.1})); - assert(octree.child(octree.index(octree.root()), 2) == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 3), 3), 3) == + assert(octree.child(octree.root(), 0) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.child(octree.root(), 1) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.child(octree.root(), 2) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.child(octree.child(octree.child(octree.root(), 3), 3), 3) == octree.locate({1.1, 1.1, -1.1})); - assert(octree.child(octree.child(octree.child(octree.index(octree.root()), 4), 4), 4) == + assert(octree.child(octree.child(octree.child(octree.root(), 4), 4), 4) == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.child(octree.index(octree.root()), 5) == octree.locate({1.1, -1.1, 1.1})); - assert(octree.child(octree.index(octree.root()), 6) == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.child(octree.index(octree.root()), 7) == octree.locate({1.1, 1.1, 1.1})); + assert(octree.child(octree.root(), 5) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.child(octree.root(), 6) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.child(octree.root(), 7) == octree.locate({1.1, 1.1, 1.1})); } diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 63ca3a31fa7a..543c99a5a6e8 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -37,7 +37,7 @@ void test_1_point() { octree.refine(10, 1); // Check that the root node was never split - assert(octree.root().is_leaf()); + assert(octree.is_leaf(octree.root())); assert(0 == octree.depth()); } @@ -54,7 +54,7 @@ void test_2_points() { // The octree should have been split once Octree other(points, points.point_map()); - other.split(other.index(other.root())); + other.split(other.root()); assert(Octree::is_topology_equal(other, octree)); assert(1 == octree.depth()); } @@ -73,15 +73,15 @@ void test_4_points() { // The octree should have been split once on the first level, and twice on the second Octree other(points, points.point_map()); - other.split(other.index(other.root())); - other.split(other.child(other.index(other.root()), 3)); - other.split(other.child(other.index(other.root()), 7)); + other.split(other.root()); + other.split(other.child(other.root(), 3)); + other.split(other.child(other.root(), 7)); assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); // Applying another splitting criterion shouldn't reset the tree. octree.refine(Split_nth_child_of_root(2)); - other.split(other.child(other.index(other.root()), 2)); + other.split(other.child(other.root(), 2)); assert(Octree::is_topology_equal(other, octree)); } diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 82abb26b12ac..f26625db46a7 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -30,7 +30,7 @@ bool test_preorder_1_node() { // Check each item in the range auto iter = nodes.begin(); - assert(*iter == octree.index(octree.root())); + assert(*iter == octree.root()); return true; } @@ -51,10 +51,10 @@ bool test_preorder_9_nodes() { // Check each item in the range auto iter = nodes.begin(); - assert(*iter == octree.index(octree.root())); + assert(*iter == octree.root()); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.child(octree.index(octree.root()), i)); + assert(*iter == octree.child(octree.root(), i)); } return true; @@ -77,7 +77,7 @@ bool test_level_9_nodes() { // Check each item in the range auto iter = nodes.begin(); for (int i = 0; i < 8; ++i) { - assert(*iter == octree.child(octree.index(octree.root()), i)); + assert(*iter == octree.child(octree.root(), i)); iter++; } @@ -102,30 +102,30 @@ bool test_preorder_25_nodes() { // Check each item in the range auto iter = nodes.begin(); - assert(*iter == octree.index(octree.root())); + assert(*iter == octree.root()); iter++; - assert(*iter == octree.child(octree.index(octree.root()), 0)); + assert(*iter == octree.child(octree.root(), 0)); iter++; - assert(*iter == octree.child(octree.index(octree.root()), 1)); + assert(*iter == octree.child(octree.root(), 1)); iter++; - assert((*iter == octree.child(octree.index(octree.root()), 2))); + assert((*iter == octree.child(octree.root(), 2))); iter++; - assert(*iter == octree.child(octree.index(octree.root()), 3)); + assert(*iter == octree.child(octree.root(), 3)); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.child(octree.child(octree.index(octree.root()), 3), i)); + assert(*iter == octree.child(octree.child(octree.root(), 3), i)); } iter++; - assert((*iter == octree.child(octree.index(octree.root()), 4))); + assert((*iter == octree.child(octree.root(), 4))); iter++; - assert((*iter == octree.child(octree.index(octree.root()), 5))); + assert((*iter == octree.child(octree.root(), 5))); iter++; - assert((*iter == octree.child(octree.index(octree.root()), 6))); + assert((*iter == octree.child(octree.root(), 6))); iter++; - assert((*iter == octree.child(octree.index(octree.root()), 7))); + assert((*iter == octree.child(octree.root(), 7))); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.child(octree.child(octree.index(octree.root()), 7), i)); + assert(*iter == octree.child(octree.child(octree.root(), 7), i)); } return true; From f2467dea77632c29fa526e1d8b2ee15a8314802e Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 16:00:24 +0200 Subject: [PATCH 028/520] Remove unused Node methods --- Orthtree/include/CGAL/Orthtree/Node.h | 39 +-------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index c6da91748594..bce33e2191f9 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -55,15 +55,6 @@ class Orthtree::Node { */ typedef typename Orthtree::Node Self; - - /// \cond SKIP_IN_MANUAL - /*! - * \brief Array for containing the child nodes of this node. - */ - typedef boost::span Children; - typedef boost::span Children_const; - /// \endcond - /*! \brief Set of bits representing this node's relationship to its parent. @@ -218,13 +209,6 @@ class Orthtree::Node { /// \name Point Range /// @{ - /*! - \brief checks whether the node is empty of points or not. - */ - bool empty() const { - return m_points.empty(); - } - /*! \brief returns the number of points of this node. */ @@ -232,18 +216,6 @@ class Orthtree::Node { return std::size_t(std::distance(m_points.begin(), m_points.end())); } - /*! - \brief returns the iterator at the start of the collection of - points held by this node. - */ - const_iterator begin() const { return m_points.begin(); } - - /*! - \brief returns the iterator at the end of the collection of - points held by this node. - */ - const_iterator end() const { return m_points.end(); } - /// @} /// \cond SKIP_IN_MANUAL @@ -253,19 +225,10 @@ class Orthtree::Node { /*! * \brief compares the topology of this node to another node. * - * \todo This seems out of date, the implementation I see compares for direct equality - * * \param rhs node to compare with * \return whether the nodes have different topology. */ - bool operator==(const Self& rhs) const { - // todo: This is a trivial implementation, maybe it can be set to =default in c++17? - return rhs.m_parent_index == m_parent_index && - rhs.m_children_index == m_children_index && - rhs.m_points == m_points && - rhs.m_depth == m_depth && - rhs.m_global_coordinates == m_global_coordinates; - } + bool operator==(const Self& rhs) const = default; friend std::ostream& operator<<(std::ostream& os, const Self& node) { return internal::print_orthtree_node(os, node); From 9a52cf70269bf99bf15501798cdbaa7ad06a49d0 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 25 Apr 2023 16:07:00 +0200 Subject: [PATCH 029/520] Add a "Maybe" type hiding the boost::optional implementation detail --- Orthtree/include/CGAL/Orthtree.h | 25 +++++++++++++++---------- Orthtree/include/CGAL/Orthtree/Node.h | 5 +++-- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index dce5637e089d..e67caf2fa067 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -110,6 +110,11 @@ class Orthtree { */ typedef std::size_t Node_index; + /*! + * \brief Optional index of a node in the tree. + */ + typedef boost::optional Maybe_node_index; + /*! * \brief The Sub-tree / Orthant type. */ @@ -443,7 +448,7 @@ class Orthtree { return std::distance(m_nodes.data(), &node); } - boost::optional index(const Node* node) const { + Maybe_node_index index(const Node* node) const { if (node == nullptr) return {}; return index(*node); } @@ -480,7 +485,7 @@ class Orthtree { auto first = traversal.first_index(); - auto next = [=](const Self& tree, Node_index index) -> boost::optional { + auto next = [=](const Self& tree, Node_index index) -> Maybe_node_index { return traversal.next_index(index); }; @@ -493,7 +498,7 @@ class Orthtree { Node_index first = traversal.first_index(); - auto next = [=](const Self& tree, Node_index index) -> boost::optional { + auto next = [=](const Self& tree, Node_index index) -> Maybe_node_index { return traversal.next_index(index); }; @@ -719,7 +724,7 @@ class Orthtree { return m_nodes[node].m_children_index.get() + i; } - const boost::optional next_sibling(Node_index n) const { + const Maybe_node_index next_sibling(Node_index n) const { // Root node has no siblings if (is_root(n)) return {}; @@ -735,17 +740,17 @@ class Orthtree { return child(parent(n), local_coords + 1); } - const boost::optional next_sibling_up(Node_index n) const { + const Maybe_node_index next_sibling_up(Node_index n) const { // the root node has no next sibling up if (n == 0) return {}; - auto up = boost::optional{parent(n)}; + auto up = Maybe_node_index{parent(n)}; while (up) { if (next_sibling(up.get())) return {next_sibling(up.get())}; - up = is_root(up.get()) ? boost::optional{} : boost::optional{parent(up.get())}; + up = is_root(up.get()) ? Maybe_node_index{} : Maybe_node_index{parent(up.get())}; } return {}; @@ -760,7 +765,7 @@ class Orthtree { return first; } - boost::optional first_child_at_depth(Node_index n, std::size_t d) const { + Maybe_node_index first_child_at_depth(Node_index n, std::size_t d) const { std::queue todo; todo.push(n); @@ -916,7 +921,7 @@ class Orthtree { \return the index of the adjacent node if it exists, nothing otherwise. */ - boost::optional adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { + Maybe_node_index adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 @@ -962,7 +967,7 @@ class Orthtree { /*! \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. */ - boost::optional adjacent_node(Node_index n, typename Node::Adjacency adjacency) const { + Maybe_node_index adjacent_node(Node_index n, typename Node::Adjacency adjacency) const { return adjacent_node(n, std::bitset(static_cast(adjacency))); } diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index bce33e2191f9..ca6bce35e5a5 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -49,6 +49,7 @@ class Orthtree::Node { typedef typename Enclosing::Dimension Dimension; ///< Dimension type. typedef typename Enclosing::Degree Degree; ///< Degree type. typedef typename Enclosing::Node_index Node_index; ///< Index type. + typedef typename Enclosing::Maybe_node_index Maybe_node_index; ///< Index type. /*! \brief Self typedef for convenience. @@ -98,8 +99,8 @@ class Orthtree::Node { std::uint8_t m_depth = 0; Global_coordinates m_global_coordinates{}; - boost::optional m_parent_index{}; - boost::optional m_children_index{}; + Maybe_node_index m_parent_index{}; + Maybe_node_index m_children_index{}; // Only the Orthtree class has access to the non-default // constructor, mutators, etc. From dfcae8dbf73f2768ae45164d512fed5c2a86ae57 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Apr 2023 17:48:12 +0200 Subject: [PATCH 030/520] Add a sketch of how Property_map could be implemented --- STL_Extension/include/CGAL/Properties.h | 107 ++++++++++++++++++ .../test/STL_Extension/CMakeLists.txt | 1 + .../test/STL_Extension/test_Properties.cpp | 24 ++++ 3 files changed, 132 insertions(+) create mode 100644 STL_Extension/include/CGAL/Properties.h create mode 100644 STL_Extension/test/STL_Extension/test_Properties.cpp diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h new file mode 100644 index 000000000000..9cc246b9d1df --- /dev/null +++ b/STL_Extension/include/CGAL/Properties.h @@ -0,0 +1,107 @@ +#ifndef PROPERTIES_H +#define PROPERTIES_H + +#include + +#include + +#include + +namespace CGAL::Properties { + +template +using String_literal_type = std::integer_sequence; + +template +constexpr String_literal_type operator ""_param() { return {}; } + +class Property_array_base { +public: + + virtual ~Property_array_base() = default; + + // todo: Declare virtual functions here for things which need to be done within the Property container + +}; + +/*! + * \brief Indexed storage for arbitrary types + * + * @tparam T + */ +template +class Property_array : public Property_array_base { + + std::vector m_data; + const std::vector& m_active_indices; + +public: + + Property_array(const std::vector& active_indices, const T& default_value) : + m_active_indices(active_indices), m_data() {} + + bool operator==(const Property_array& other) const { + return &other == this; + } + + bool operator!=(const Property_array& other) const { return !operator==(other); } + +}; + +class Property_container { + + + std::map> m_property_arrays; + std::vector m_active_indices; + +public: + + /* + template + Property_array& add(const std::string& name, const T default_value = T()) { + return dynamic_cast&>(*(*m_property_arrays.emplace(name, std::make_shared>( + m_active_indices, + default_value) + ).first).second); + } + + template + Property_array& get(const std::string& name) { + CGAL_precondition(m_property_arrays.count(name) != 0); + return dynamic_cast&>(*m_property_arrays[name]); + } + */ + + template + Property_array& get() { + + static Property_array array{m_active_indices, T{}}; + return array; + + } + + template + std::pair>, std::unique_lock> threadsafe_get() { + static std::mutex m{}; + return {std::reference_wrapper>{get()}, std::unique_lock{m}}; + } + + +private: + + static constexpr std::size_t next_type_index() { + static std::atomic value{0}; + return value++; + } + + template + static constexpr std::size_t type_index() { + static std::size_t value{next_type_index()}; + return value; + } + +}; + +} + +#endif //ORTHTREE_TESTS_PROPERTIES_H diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index d25906091092..50ed354bc69b 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -44,6 +44,7 @@ create_single_source_cgal_program("test_N_tuple.cpp") create_single_source_cgal_program("test_namespaces.cpp") create_single_source_cgal_program("test_Nested_iterator.cpp") create_single_source_cgal_program("test_Object.cpp") +create_single_source_cgal_program("test_Properties.cpp") create_single_source_cgal_program("test_stl_extension.cpp") create_single_source_cgal_program("test_type_traits.cpp") create_single_source_cgal_program("test_Uncertain.cpp") diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp new file mode 100644 index 000000000000..da72e38de526 --- /dev/null +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -0,0 +1,24 @@ + +#include + +using namespace CGAL::Properties; + +int main() { + + Property_container properties; + + auto &a = properties.get(); + auto &b = properties.get(); + auto &c = properties.get(); + + assert((std::is_same::value)); + + assert(a != b); + + auto &a2 = properties.get(); + assert(a == a2); + + properties.threadsafe_get(); + + return 0; +} \ No newline at end of file From e3201869d1af4d578a697c505d6681bd31cdc896 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 9 May 2023 13:07:49 +0200 Subject: [PATCH 031/520] Add support & tests for the obviously necessary features from Surface_mesh's property system --- STL_Extension/include/CGAL/Properties.h | 141 +++++++++++++----- .../test/STL_Extension/CMakeLists.txt | 2 + .../test/STL_Extension/test_Properties.cpp | 101 +++++++++++-- 3 files changed, 201 insertions(+), 43 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index 9cc246b9d1df..e9d4c8198369 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -9,18 +9,18 @@ namespace CGAL::Properties { -template -using String_literal_type = std::integer_sequence; - -template -constexpr String_literal_type operator ""_param() { return {}; } - class Property_array_base { public: virtual ~Property_array_base() = default; - // todo: Declare virtual functions here for things which need to be done within the Property container + // todo: Declare virtual functions here, for things which need to be done within the Property container + + virtual void reserve(std::size_t n) = 0; + + virtual void swap(std::size_t a, std::size_t b) = 0; + + virtual void reset(std::size_t i) = 0; }; @@ -34,11 +34,47 @@ class Property_array : public Property_array_base { std::vector m_data; const std::vector& m_active_indices; + T m_default_value; public: Property_array(const std::vector& active_indices, const T& default_value) : - m_active_indices(active_indices), m_data() {} + m_data(), m_active_indices(active_indices), m_default_value(default_value) { + + m_data.reserve(active_indices.capacity()); + m_data.resize(active_indices.size(), m_default_value); + } + + virtual void reserve(std::size_t n) override { + CGAL_precondition(m_active_indices.size() == n); + m_data.resize(n, m_default_value); + }; + + virtual void swap(std::size_t a, std::size_t b) override { + CGAL_precondition(a < m_data.size() && b < m_data.size()); + std::iter_swap(m_data.begin() + a, m_data.begin() + b); + }; + + virtual void reset(std::size_t i) override { + CGAL_precondition(i < m_data.size()); + m_data[i] = m_default_value; + }; + + std::size_t capacity() const { return m_data.size(); } + +public: + + const T& operator[](std::size_t i) const { + CGAL_precondition(i < m_data.size()); + return m_data[i]; + } + + T& operator[](std::size_t i) { + CGAL_precondition(i < m_data.size()); + return m_data[i]; + } + +public: bool operator==(const Property_array& other) const { return &other == this; @@ -50,56 +86,93 @@ class Property_array : public Property_array_base { class Property_container { - std::map> m_property_arrays; std::vector m_active_indices; public: - /* template - Property_array& add(const std::string& name, const T default_value = T()) { - return dynamic_cast&>(*(*m_property_arrays.emplace(name, std::make_shared>( - m_active_indices, - default_value) - ).first).second); + std::pair>, bool> + add(const std::string& name, const T default_value = T()) { + auto [it, created] = m_property_arrays.emplace( + name, + std::make_shared>( + m_active_indices, + default_value + ) + ); + auto [key, array] = *it; + auto& typed_array = dynamic_cast&>(*array); + return {{typed_array}, !created}; + } + + template + const Property_array& get(const std::string& name) const { + CGAL_precondition(m_property_arrays.count(name) != 0); + return dynamic_cast&>(*m_property_arrays.at(name)); } template Property_array& get(const std::string& name) { CGAL_precondition(m_property_arrays.count(name) != 0); - return dynamic_cast&>(*m_property_arrays[name]); + return dynamic_cast&>(*m_property_arrays.at(name)); } - */ - template - Property_array& get() { + /*! + * Removes a property array from the container + * + * @param name + * @return True if a container with this name existed, false otherwise + */ + bool remove(const std::string& name) { return m_property_arrays.erase(name) == 1; } - static Property_array array{m_active_indices, T{}}; - return array; + std::size_t n_properties() { return m_property_arrays.size(); } - } +public: - template - std::pair>, std::unique_lock> threadsafe_get() { - static std::mutex m{}; - return {std::reference_wrapper>{get()}, std::unique_lock{m}}; + void reserve(std::size_t n) { + m_active_indices.resize(n); + for (auto [name, array]: m_property_arrays) + array->reserve(n); } + std::size_t emplace() { + // todo: should emplacing an element also reset it to default values? + + // If there are empty slots, return the index of one of them and mark it as full + auto first_unused = std::find_if(m_active_indices.begin(), m_active_indices.end(), [](bool used) { return !used; }); + if (first_unused != m_active_indices.end()) { + *first_unused = true; + return std::distance(m_active_indices.begin(), first_unused); + } + + // Otherwise, expand the storage and return the last element + reserve(size() + 1); + m_active_indices.back() = true; + return size() - 1; + + } -private: + void swap(std::size_t a, std::size_t b) { + for (auto [name, array]: m_property_arrays) + array->swap(a, b); + } - static constexpr std::size_t next_type_index() { - static std::atomic value{0}; - return value++; + void reset(std::size_t i) { + for (auto [name, array]: m_property_arrays) + array->reset(i); } - template - static constexpr std::size_t type_index() { - static std::size_t value{next_type_index()}; - return value; + void erase(std::size_t i) { + m_active_indices[i] = false; + for (auto [name, array]: m_property_arrays) + array->reset(i); } + std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } + + std::size_t capacity() const { return m_active_indices.size(); } + }; } diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index 50ed354bc69b..820b97aca9f8 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.1...3.23) project(STL_Extension_Tests) +set(CMAKE_CXX_STANDARD 17) # todo: this is the wrong place for this! + find_package(CGAL REQUIRED) find_package(TBB QUIET) diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index da72e38de526..da31f8210f34 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -3,22 +3,105 @@ using namespace CGAL::Properties; -int main() { +void test_property_creation() { + + Property_container properties; + + // Should return an integer array which didn't previously exist + auto [integers, preexisting] = properties.add("integer", 5); + static_assert(std::is_same_v>>); + assert(!preexisting); + assert(properties.n_properties() == 1); + + auto [floats, _] = properties.add("float"); + static_assert(std::is_same_v>>); + assert(properties.n_properties() == 2); + + // get() should retreive the same arrays + assert(integers.get() == properties.get("integer")); + assert(floats.get() == properties.get("float")); + + // remove() should delete a property array & return if it existed + assert(!properties.remove("not-a-real-property")); + auto removed = properties.remove("integer"); + assert(removed); + assert(properties.n_properties() == 1); + +} + +void test_property_array() { Property_container properties; - auto &a = properties.get(); - auto &b = properties.get(); - auto &c = properties.get(); + auto [integers, integers_existed] = properties.add("integers", 5); + + // Reserve space for 100 elements + properties.reserve(100); + assert(properties.capacity() == 100); + assert(properties.size() == 0); - assert((std::is_same::value)); + // Newly emplaced elements should go at the front + assert(properties.emplace() == 0); + assert(properties.emplace() == 1); + assert(properties.emplace() == 2); + assert(properties.size() == 3); - assert(a != b); + // Make sure that the new elements are equal to the default value + assert(integers.get()[0] == 5); + assert(integers.get()[1] == 5); + assert(integers.get()[2] == 5); - auto &a2 = properties.get(); - assert(a == a2); + // Add a new property + auto [floats, floats_existed] = properties.add("floats", 6.0f); + + // The new property array should already be of the right size + assert(floats.get().capacity() == 100); + assert(properties.size() == 3); + + // Pre-existing elements should contain the default value + assert(floats.get()[0] == 6.0f); + assert(floats.get()[1] == 6.0f); + assert(floats.get()[2] == 6.0f); + + // Update values for a few elements + floats.get()[0] = 1.0f; + floats.get()[1] = 2.0f; + floats.get()[2] = 3.0f; + integers.get()[2] = -2; + assert(floats.get()[0] == 1.0f); + assert(floats.get()[1] == 2.0f); + assert(floats.get()[2] == 3.0f); + assert(integers.get()[2] == -2); + + // Reset an element, and all of its properties should revert to the defaults + properties.reset(2); + assert(floats.get()[2] == 6.0f); + assert(integers.get()[2] == 5); + + // Erase an element, and the size should be reduced + properties.erase(1); + assert(properties.size() == 2); + assert(properties.capacity() == 100); + + // A newly emplaced element should take the empty slot + assert(properties.emplace() == 1); + assert(properties.size() == 3); + // todo: should the new element have default properties? + assert(properties.emplace() == 3); + assert(properties.size() == 4); + + // Swapping a pair of elements swaps all of their properties + properties.swap(0, 3); + assert(integers.get()[0] == 5); + assert(floats.get()[0] == 6.0f); + assert(integers.get()[3] == 5); + assert(floats.get()[3] == 1.0f); +} + +int main() { - properties.threadsafe_get(); + test_property_creation(); + test_property_array(); return 0; } \ No newline at end of file From 3adb689071b34833e95403a33787f7fd0188b12f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 9 May 2023 13:40:14 +0200 Subject: [PATCH 032/520] Bump min C++ version requirement to 17 --- Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake b/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake index 7329be331166..de0f5eb7fa4f 100644 --- a/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake +++ b/Installation/cmake/modules/CGAL_SetupCGALDependencies.cmake @@ -97,8 +97,8 @@ function(CGAL_setup_CGAL_dependencies target) target_compile_definitions(${target} INTERFACE CGAL_TEST_SUITE=1) endif() - # CGAL now requires C++14. `decltype(auto)` is used as a marker of C++14. - target_compile_features(${target} INTERFACE cxx_decltype_auto) + # CGAL now requires C++17 + target_compile_features(${target} INTERFACE cxx_std_17) use_CGAL_Boost_support(${target} INTERFACE) From 7e849a5384fc18618b4c67afef7177414499febd Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 9 May 2023 13:40:45 +0200 Subject: [PATCH 033/520] Add support for arbitrary Index types (defaults to std::size_t) --- STL_Extension/include/CGAL/Properties.h | 43 ++++++++++--------- .../test/STL_Extension/CMakeLists.txt | 2 - .../test/STL_Extension/test_Properties.cpp | 4 +- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index e9d4c8198369..c4411074e3cc 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -29,7 +29,7 @@ class Property_array_base { * * @tparam T */ -template +template class Property_array : public Property_array_base { std::vector m_data; @@ -50,12 +50,12 @@ class Property_array : public Property_array_base { m_data.resize(n, m_default_value); }; - virtual void swap(std::size_t a, std::size_t b) override { + virtual void swap(Index a, std::size_t b) override { CGAL_precondition(a < m_data.size() && b < m_data.size()); std::iter_swap(m_data.begin() + a, m_data.begin() + b); }; - virtual void reset(std::size_t i) override { + virtual void reset(Index i) override { CGAL_precondition(i < m_data.size()); m_data[i] = m_default_value; }; @@ -76,14 +76,15 @@ class Property_array : public Property_array_base { public: - bool operator==(const Property_array& other) const { + bool operator==(const Property_array& other) const { return &other == this; } - bool operator!=(const Property_array& other) const { return !operator==(other); } + bool operator!=(const Property_array& other) const { return !operator==(other); } }; +template class Property_container { std::map> m_property_arrays; @@ -92,30 +93,30 @@ class Property_container { public: template - std::pair>, bool> + std::pair>, bool> add(const std::string& name, const T default_value = T()) { auto [it, created] = m_property_arrays.emplace( name, - std::make_shared>( + std::make_shared>( m_active_indices, default_value ) ); auto [key, array] = *it; - auto& typed_array = dynamic_cast&>(*array); + auto& typed_array = dynamic_cast&>(*array); return {{typed_array}, !created}; } template - const Property_array& get(const std::string& name) const { + const Property_array& get(const std::string& name) const { CGAL_precondition(m_property_arrays.count(name) != 0); - return dynamic_cast&>(*m_property_arrays.at(name)); + return dynamic_cast&>(*m_property_arrays.at(name)); } template - Property_array& get(const std::string& name) { + Property_array& get(const std::string& name) { CGAL_precondition(m_property_arrays.count(name) != 0); - return dynamic_cast&>(*m_property_arrays.at(name)); + return dynamic_cast&>(*m_property_arrays.at(name)); } /*! @@ -136,7 +137,12 @@ class Property_container { array->reserve(n); } - std::size_t emplace() { + std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } + + std::size_t capacity() const { return m_active_indices.size(); } + + + Index emplace() { // todo: should emplacing an element also reset it to default values? // If there are empty slots, return the index of one of them and mark it as full @@ -153,26 +159,21 @@ class Property_container { } - void swap(std::size_t a, std::size_t b) { + void swap(Index a, Index b) { for (auto [name, array]: m_property_arrays) array->swap(a, b); } - void reset(std::size_t i) { + void reset(Index i) { for (auto [name, array]: m_property_arrays) array->reset(i); } - void erase(std::size_t i) { + void erase(Index i) { m_active_indices[i] = false; for (auto [name, array]: m_property_arrays) array->reset(i); } - - std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } - - std::size_t capacity() const { return m_active_indices.size(); } - }; } diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index 820b97aca9f8..50ed354bc69b 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -4,8 +4,6 @@ cmake_minimum_required(VERSION 3.1...3.23) project(STL_Extension_Tests) -set(CMAKE_CXX_STANDARD 17) # todo: this is the wrong place for this! - find_package(CGAL REQUIRED) find_package(TBB QUIET) diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index da31f8210f34..a75ef0308d43 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -9,12 +9,12 @@ void test_property_creation() { // Should return an integer array which didn't previously exist auto [integers, preexisting] = properties.add("integer", 5); - static_assert(std::is_same_v>>); + static_assert(std::is_same_v>>); assert(!preexisting); assert(properties.n_properties() == 1); auto [floats, _] = properties.add("float"); - static_assert(std::is_same_v>>); + static_assert(std::is_same_v>>); assert(properties.n_properties() == 2); // get() should retreive the same arrays From 8d89fa75b7b0eb3f5523afd51adf93e7145382b9 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 9 May 2023 14:25:50 +0200 Subject: [PATCH 034/520] Add support for emplacing contiguous groups --- STL_Extension/include/CGAL/Properties.h | 62 ++++++++++++++++--- .../test/STL_Extension/test_Properties.cpp | 43 +++++++++++++ 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index c4411074e3cc..2e3c0834c665 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -9,6 +9,7 @@ namespace CGAL::Properties { +template class Property_array_base { public: @@ -18,9 +19,9 @@ class Property_array_base { virtual void reserve(std::size_t n) = 0; - virtual void swap(std::size_t a, std::size_t b) = 0; + virtual void swap(Index a, Index b) = 0; - virtual void reset(std::size_t i) = 0; + virtual void reset(Index i) = 0; }; @@ -30,7 +31,7 @@ class Property_array_base { * @tparam T */ template -class Property_array : public Property_array_base { +class Property_array : public Property_array_base { std::vector m_data; const std::vector& m_active_indices; @@ -50,7 +51,7 @@ class Property_array : public Property_array_base { m_data.resize(n, m_default_value); }; - virtual void swap(Index a, std::size_t b) override { + virtual void swap(Index a, Index b) override { CGAL_precondition(a < m_data.size() && b < m_data.size()); std::iter_swap(m_data.begin() + a, m_data.begin() + b); }; @@ -87,7 +88,7 @@ class Property_array : public Property_array_base { template class Property_container { - std::map> m_property_arrays; + std::map>> m_property_arrays; std::vector m_active_indices; public: @@ -143,7 +144,6 @@ class Property_container { Index emplace() { - // todo: should emplacing an element also reset it to default values? // If there are empty slots, return the index of one of them and mark it as full auto first_unused = std::find_if(m_active_indices.begin(), m_active_indices.end(), [](bool used) { return !used; }); @@ -153,9 +153,55 @@ class Property_container { } // Otherwise, expand the storage and return the last element - reserve(size() + 1); + reserve(capacity() + 1); m_active_indices.back() = true; - return size() - 1; + // todo: should emplacing an element also reset it to default values? + reset(capacity() - 1); + return capacity() - 1; + + } + + template + Index emplace_group() { + + auto search_start = m_active_indices.begin(); + while (search_start != m_active_indices.end()) { + + // Find the first unused cell + auto unused_begin = std::find_if( + search_start, m_active_indices.end(), + [](bool used) { return !used; } + ); + + // Determine if the group fits + auto unused_end = std::find_if( + unused_begin, std::min(unused_begin + N, m_active_indices.end()), + [](bool used) { return used; } + ); + + // If the discovered range was large enough + if (std::distance(unused_begin, unused_end) >= N) { + + // Mark the indices as used, and reset the properties of each of them + // todo: it would be better to provide a function to set a range + for (auto it = unused_begin; it < unused_end; ++it) { + *it = true; + reset(std::distance(m_active_indices.begin(), it)); + } + + // Return the first index of the range + return std::distance(m_active_indices.begin(), unused_begin); + } + + // If we didn't find a large enough region, continue our search after the end + search_start = unused_end; + } + + // If no empty regions were found, expand the storage + reserve(capacity() + N); + for (auto it = m_active_indices.end() - N; it < m_active_indices.end(); ++it) + *it = true; + return capacity() - N; } diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index a75ef0308d43..90e2b8486fd1 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -27,6 +27,10 @@ void test_property_creation() { assert(removed); assert(properties.n_properties() == 1); + // Add a new property + auto [bools, bools_existed] = properties.add("bools", false); + static_assert(std::is_same_v>>); + Property_array &b = bools.get(); } void test_property_array() { @@ -96,12 +100,51 @@ void test_property_array() { assert(floats.get()[0] == 6.0f); assert(integers.get()[3] == 5); assert(floats.get()[3] == 1.0f); + +} + +void test_property_array_set_group() { + + Property_container properties; + + auto [a, a_existed] = properties.add("a", 5); + + // Insert a group of 100 elements + properties.emplace_group<100>(); + assert(properties.size() == 100); + + // Eliminate a few regions + properties.erase(3); + assert(properties.size() == 99); + for (int i = 20; i < 25; ++i) + properties.erase(i); + assert(properties.size() == 94); + for (int i = 50; i < 80; ++i) + properties.erase(i); + assert(properties.size() == 64); + + // A group of size 4 should only fit in the empty region fo size 5 + assert(properties.emplace_group<4>() == 20); + assert(properties.size() == 68); + assert(properties.capacity() == 100); + + // A group of size 16 should only fit in the empty region fo size 30 + assert(properties.emplace_group<16>() == 50); + assert(properties.size() == 84); + assert(properties.capacity() == 100); + + // Another group of size 16 should require the storage to expand, because the largest empty region is mostly full now + assert(properties.emplace_group<16>() == 100); + assert(properties.size() == 100); + assert(properties.capacity() == 116); + } int main() { test_property_creation(); test_property_array(); + test_property_array_set_group(); return 0; } \ No newline at end of file From 838b9661cc1a7aff0006c17660d0cb5d69a077c1 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 9 May 2023 18:13:55 +0200 Subject: [PATCH 035/520] Add support for appending one property container to another --- STL_Extension/include/CGAL/Properties.h | 44 ++++++++++++--- .../test/STL_Extension/test_Properties.cpp | 55 ++++++++++++++++--- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index 2e3c0834c665..b992ae20425e 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -17,6 +17,8 @@ class Property_array_base { // todo: Declare virtual functions here, for things which need to be done within the Property container + virtual void append(const Property_array_base& other) = 0; + virtual void reserve(std::size_t n) = 0; virtual void swap(Index a, Index b) = 0; @@ -46,6 +48,12 @@ class Property_array : public Property_array_base { m_data.resize(active_indices.size(), m_default_value); } + virtual void append(const Property_array_base& other_base) { + auto& other = dynamic_cast&>(other_base); + CGAL_precondition(m_data.size() + other.m_data.size() == m_active_indices.size()); + m_data.insert(m_data.end(), other.m_data.begin(), other.m_data.end()); + } + virtual void reserve(std::size_t n) override { CGAL_precondition(m_active_indices.size() == n); m_data.resize(n, m_default_value); @@ -142,7 +150,6 @@ class Property_container { std::size_t capacity() const { return m_active_indices.size(); } - Index emplace() { // If there are empty slots, return the index of one of them and mark it as full @@ -161,8 +168,7 @@ class Property_container { } - template - Index emplace_group() { + Index emplace_group(std::size_t n) { auto search_start = m_active_indices.begin(); while (search_start != m_active_indices.end()) { @@ -175,12 +181,12 @@ class Property_container { // Determine if the group fits auto unused_end = std::find_if( - unused_begin, std::min(unused_begin + N, m_active_indices.end()), + unused_begin, std::min(unused_begin + n, m_active_indices.end()), [](bool used) { return used; } ); // If the discovered range was large enough - if (std::distance(unused_begin, unused_end) >= N) { + if (std::distance(unused_begin, unused_end) >= n) { // Mark the indices as used, and reset the properties of each of them // todo: it would be better to provide a function to set a range @@ -198,10 +204,10 @@ class Property_container { } // If no empty regions were found, expand the storage - reserve(capacity() + N); - for (auto it = m_active_indices.end() - N; it < m_active_indices.end(); ++it) + reserve(capacity() + n); + for (auto it = m_active_indices.end() - n; it < m_active_indices.end(); ++it) *it = true; - return capacity() - N; + return capacity() - n; } @@ -220,6 +226,28 @@ class Property_container { for (auto [name, array]: m_property_arrays) array->reset(i); } + + /*! + * Adds the elements of the other container to this container for each property which is present in this container. + * + * Gaps are preserved, and all elements of the other container are guaranteed + * to appear after the elements of this container. + * todo: merge() would be useful as well, but could break contiguous regions in the other container + * + * @param other + */ + void append(const Property_container& other) { + // todo + + m_active_indices.insert(m_active_indices.end(), other.m_active_indices.begin(), other.m_active_indices.end()); + for (auto [name, array]: m_property_arrays) { + auto it = other.m_property_arrays.find(name); + if (it != other.m_property_arrays.end()) + array->append(*it->second); + else + array->reserve(m_active_indices.size()); + } + } }; } diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index 90e2b8486fd1..c0e8220c5e1c 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -30,10 +30,10 @@ void test_property_creation() { // Add a new property auto [bools, bools_existed] = properties.add("bools", false); static_assert(std::is_same_v>>); - Property_array &b = bools.get(); + Property_array& b = bools.get(); } -void test_property_array() { +void test_element_access() { Property_container properties; @@ -103,14 +103,14 @@ void test_property_array() { } -void test_property_array_set_group() { +void test_emplace_group() { Property_container properties; auto [a, a_existed] = properties.add("a", 5); // Insert a group of 100 elements - properties.emplace_group<100>(); + properties.emplace_group(100); assert(properties.size() == 100); // Eliminate a few regions @@ -124,27 +124,64 @@ void test_property_array_set_group() { assert(properties.size() == 64); // A group of size 4 should only fit in the empty region fo size 5 - assert(properties.emplace_group<4>() == 20); + assert(properties.emplace_group(4) == 20); assert(properties.size() == 68); assert(properties.capacity() == 100); // A group of size 16 should only fit in the empty region fo size 30 - assert(properties.emplace_group<16>() == 50); + assert(properties.emplace_group(16) == 50); assert(properties.size() == 84); assert(properties.capacity() == 100); // Another group of size 16 should require the storage to expand, because the largest empty region is mostly full now - assert(properties.emplace_group<16>() == 100); + assert(properties.emplace_group(16) == 100); assert(properties.size() == 100); assert(properties.capacity() == 116); } +void test_append() { + + // Create a pair of property containers with similar contents + Property_container properties_a, properties_b; + properties_a.add("ints", 1); + properties_b.add("ints", 2); + properties_a.add("floats", 3.0f); + properties_b.add("floats", 4.0f); + + // One container will also contain an extra property + properties_a.add("bools", true); + + // Add some values to both property sets + properties_a.emplace_group(10); + properties_b.emplace_group(5); + assert(properties_a.size() == 10); + assert(properties_b.size() == 5); + + // Add the second group to the end of the first + properties_a.append(properties_b); + assert(properties_a.size() == 15); + assert(properties_b.size() == 5); + + // Initialized values from the second group should appear after those of the first + assert(properties_a.get("ints")[5] == 1); + assert(properties_a.get("ints")[12] == 2); + assert(properties_a.get("floats")[5] == 3.0f); + assert(properties_a.get("floats")[12] == 4.0f); + + // Additional properties in the first group should have expanded too, and been filled with defaults + // note: the property array must be const, because non const operator[] doesn't work for vector of bools! + assert(std::as_const(properties_a).get("bools")[12] == true); + +} + + int main() { test_property_creation(); - test_property_array(); - test_property_array_set_group(); + test_element_access(); + test_emplace_group(); + test_append(); return 0; } \ No newline at end of file From b800f8c63422a60d0d3c64445d6f3732792820ac Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 10 May 2023 00:13:17 +0200 Subject: [PATCH 036/520] Add a couple of convenience functions for Surface_mesh --- STL_Extension/include/CGAL/Properties.h | 82 ++++++++++++++----- .../test/STL_Extension/test_Properties.cpp | 61 +++++++------- 2 files changed, 93 insertions(+), 50 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index b992ae20425e..9c19f248019a 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -5,7 +5,8 @@ #include -#include +// todo: maybe this could be avoided +#include namespace CGAL::Properties { @@ -13,6 +14,10 @@ template class Property_array_base { public: + Property_array_base() = default; + + Property_array_base(const Property_array_base& rhs) = delete; + virtual ~Property_array_base() = default; // todo: Declare virtual functions here, for things which need to be done within the Property container @@ -41,6 +46,12 @@ class Property_array : public Property_array_base { public: + // Necessary for use as a boost::property_type + using key_type = Index; + using value_type = T; + using reference = typename std::vector::reference; + using category = boost::readable_property_map_tag; + Property_array(const std::vector& active_indices, const T& default_value) : m_data(), m_active_indices(active_indices), m_default_value(default_value) { @@ -60,13 +71,14 @@ class Property_array : public Property_array_base { }; virtual void swap(Index a, Index b) override { - CGAL_precondition(a < m_data.size() && b < m_data.size()); + // todo: maybe cast to index, instead of casting index to size? + CGAL_precondition(std::size_t(a) < m_data.size() && std::size_t(b) < m_data.size()); std::iter_swap(m_data.begin() + a, m_data.begin() + b); }; virtual void reset(Index i) override { - CGAL_precondition(i < m_data.size()); - m_data[i] = m_default_value; + CGAL_precondition(std::size_t(i) < m_data.size()); + m_data[std::size_t(i)] = m_default_value; }; std::size_t capacity() const { return m_data.size(); } @@ -80,7 +92,7 @@ class Property_array : public Property_array_base { T& operator[](std::size_t i) { CGAL_precondition(i < m_data.size()); - return m_data[i]; + return m_data[std::size_t(i)]; } public: @@ -103,7 +115,7 @@ class Property_container { template std::pair>, bool> - add(const std::string& name, const T default_value = T()) { + get_or_add(const std::string& name, const T default_value = T()) { auto [it, created] = m_property_arrays.emplace( name, std::make_shared>( @@ -113,7 +125,15 @@ class Property_container { ); auto [key, array] = *it; auto& typed_array = dynamic_cast&>(*array); - return {{typed_array}, !created}; + return {{typed_array}, created}; + } + + template + Property_array& add(const std::string& name, const T default_value = T()) { + // todo: I'm not settled on the naming, but it's really convenient to have a function like this + auto [array, created] = get_or_add(name, default_value); + CGAL_precondition(created); + return array.get(); } template @@ -150,22 +170,35 @@ class Property_container { std::size_t capacity() const { return m_active_indices.size(); } + Index emplace_back() { + + // Expand the storage and return the last element + reserve(capacity() + 1); + m_active_indices.back() = true; + Index first_new_index{capacity() - 1}; + reset(first_new_index); + return first_new_index; + } + Index emplace() { // If there are empty slots, return the index of one of them and mark it as full auto first_unused = std::find_if(m_active_indices.begin(), m_active_indices.end(), [](bool used) { return !used; }); if (first_unused != m_active_indices.end()) { *first_unused = true; - return std::distance(m_active_indices.begin(), first_unused); + return Index(std::distance(m_active_indices.begin(), first_unused)); } - // Otherwise, expand the storage and return the last element - reserve(capacity() + 1); - m_active_indices.back() = true; - // todo: should emplacing an element also reset it to default values? - reset(capacity() - 1); - return capacity() - 1; + return emplace_back(); + } + Index emplace_group_back(std::size_t n) { + + // Expand the storage and return the start of the new region + reserve(capacity() + n); + for (auto it = m_active_indices.end() - n; it < m_active_indices.end(); ++it) + *it = true; + return Index(capacity() - n); } Index emplace_group(std::size_t n) { @@ -192,11 +225,11 @@ class Property_container { // todo: it would be better to provide a function to set a range for (auto it = unused_begin; it < unused_end; ++it) { *it = true; - reset(std::distance(m_active_indices.begin(), it)); + reset(Index(std::distance(m_active_indices.begin(), it))); } // Return the first index of the range - return std::distance(m_active_indices.begin(), unused_begin); + return Index(std::distance(m_active_indices.begin(), unused_begin)); } // If we didn't find a large enough region, continue our search after the end @@ -204,11 +237,7 @@ class Property_container { } // If no empty regions were found, expand the storage - reserve(capacity() + n); - for (auto it = m_active_indices.end() - n; it < m_active_indices.end(); ++it) - *it = true; - return capacity() - n; - + return emplace_group_back(n); } void swap(Index a, Index b) { @@ -227,11 +256,22 @@ class Property_container { array->reset(i); } + bool is_erased(Index i) const { + return !m_active_indices[i]; + } + + // todo: I'd prefer to eliminate this, if possible + void mark_active(Index i) { + return m_active_indices[i] = true; + } + /*! * Adds the elements of the other container to this container for each property which is present in this container. * * Gaps are preserved, and all elements of the other container are guaranteed * to appear after the elements of this container. + * Properties in this container which don't appear in the other container are extended with default values. + * Properties in the other container which don't appear in this one are not included. * todo: merge() would be useful as well, but could break contiguous regions in the other container * * @param other diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index c0e8220c5e1c..b9e26ee15b36 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -8,12 +8,12 @@ void test_property_creation() { Property_container properties; // Should return an integer array which didn't previously exist - auto [integers, preexisting] = properties.add("integer", 5); + auto [integers, created] = properties.get_or_add("integer", 5); static_assert(std::is_same_v>>); - assert(!preexisting); + assert(created); assert(properties.n_properties() == 1); - auto [floats, _] = properties.add("float"); + auto [floats, _] = properties.get_or_add("float"); static_assert(std::is_same_v>>); assert(properties.n_properties() == 2); @@ -28,7 +28,7 @@ void test_property_creation() { assert(properties.n_properties() == 1); // Add a new property - auto [bools, bools_existed] = properties.add("bools", false); + auto [bools, bools_created] = properties.get_or_add("bools", false); static_assert(std::is_same_v>>); Property_array& b = bools.get(); } @@ -37,7 +37,7 @@ void test_element_access() { Property_container properties; - auto [integers, integers_existed] = properties.add("integers", 5); + auto &integers = properties.add("integers", 5); // Reserve space for 100 elements properties.reserve(100); @@ -51,36 +51,36 @@ void test_element_access() { assert(properties.size() == 3); // Make sure that the new elements are equal to the default value - assert(integers.get()[0] == 5); - assert(integers.get()[1] == 5); - assert(integers.get()[2] == 5); + assert(integers[0] == 5); + assert(integers[1] == 5); + assert(integers[2] == 5); // Add a new property - auto [floats, floats_existed] = properties.add("floats", 6.0f); + auto &floats = properties.add("floats", 6.0f); // The new property array should already be of the right size - assert(floats.get().capacity() == 100); + assert(floats.capacity() == 100); assert(properties.size() == 3); // Pre-existing elements should contain the default value - assert(floats.get()[0] == 6.0f); - assert(floats.get()[1] == 6.0f); - assert(floats.get()[2] == 6.0f); + assert(floats[0] == 6.0f); + assert(floats[1] == 6.0f); + assert(floats[2] == 6.0f); // Update values for a few elements - floats.get()[0] = 1.0f; - floats.get()[1] = 2.0f; - floats.get()[2] = 3.0f; - integers.get()[2] = -2; - assert(floats.get()[0] == 1.0f); - assert(floats.get()[1] == 2.0f); - assert(floats.get()[2] == 3.0f); - assert(integers.get()[2] == -2); + floats[0] = 1.0f; + floats[1] = 2.0f; + floats[2] = 3.0f; + integers[2] = -2; + assert(floats[0] == 1.0f); + assert(floats[1] == 2.0f); + assert(floats[2] == 3.0f); + assert(integers[2] == -2); // Reset an element, and all of its properties should revert to the defaults properties.reset(2); - assert(floats.get()[2] == 6.0f); - assert(integers.get()[2] == 5); + assert(floats[2] == 6.0f); + assert(integers[2] == 5); // Erase an element, and the size should be reduced properties.erase(1); @@ -96,10 +96,10 @@ void test_element_access() { // Swapping a pair of elements swaps all of their properties properties.swap(0, 3); - assert(integers.get()[0] == 5); - assert(floats.get()[0] == 6.0f); - assert(integers.get()[3] == 5); - assert(floats.get()[3] == 1.0f); + assert(integers[0] == 5); + assert(floats[0] == 6.0f); + assert(integers[3] == 5); + assert(floats[3] == 1.0f); } @@ -107,7 +107,7 @@ void test_emplace_group() { Property_container properties; - auto [a, a_existed] = properties.add("a", 5); + auto &a = properties.add("a", 5); // Insert a group of 100 elements properties.emplace_group(100); @@ -115,12 +115,15 @@ void test_emplace_group() { // Eliminate a few regions properties.erase(3); + assert(properties.is_erased(3)); assert(properties.size() == 99); for (int i = 20; i < 25; ++i) properties.erase(i); + assert(properties.is_erased(23)); assert(properties.size() == 94); for (int i = 50; i < 80; ++i) properties.erase(i); + assert(properties.is_erased(53)); assert(properties.size() == 64); // A group of size 4 should only fit in the empty region fo size 5 @@ -170,7 +173,7 @@ void test_append() { assert(properties_a.get("floats")[12] == 4.0f); // Additional properties in the first group should have expanded too, and been filled with defaults - // note: the property array must be const, because non const operator[] doesn't work for vector of bools! + // note: the property array must be const, because non const operator[] doesn't work for vector! assert(std::as_const(properties_a).get("bools")[12] == true); } From 3607c64c345b14c2f416fd70991e3045e811ae4b Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 11 May 2023 10:56:10 +0200 Subject: [PATCH 037/520] Add support for deep copying of property containers --- STL_Extension/include/CGAL/Properties.h | 66 +++++++++++++++++-- .../test/STL_Extension/test_Properties.cpp | 43 +++++++++++- 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index 9c19f248019a..e3b5df46ad5b 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -22,6 +22,8 @@ class Property_array_base { // todo: Declare virtual functions here, for things which need to be done within the Property container + virtual std::shared_ptr> clone(const std::vector& active_indices) = 0; + virtual void append(const Property_array_base& other) = 0; virtual void reserve(std::size_t n) = 0; @@ -46,11 +48,9 @@ class Property_array : public Property_array_base { public: - // Necessary for use as a boost::property_type - using key_type = Index; using value_type = T; using reference = typename std::vector::reference; - using category = boost::readable_property_map_tag; + using const_reference = typename std::vector::const_reference; Property_array(const std::vector& active_indices, const T& default_value) : m_data(), m_active_indices(active_indices), m_default_value(default_value) { @@ -59,7 +59,13 @@ class Property_array : public Property_array_base { m_data.resize(active_indices.size(), m_default_value); } - virtual void append(const Property_array_base& other_base) { + virtual std::shared_ptr> clone(const std::vector& active_indices) override { + auto new_array = std::make_shared>(active_indices, m_default_value); + new_array->m_data = m_data; + return new_array; + } + + virtual void append(const Property_array_base& other_base) override { auto& other = dynamic_cast&>(other_base); CGAL_precondition(m_data.size() + other.m_data.size() == m_active_indices.size()); m_data.insert(m_data.end(), other.m_data.begin(), other.m_data.end()); @@ -105,14 +111,62 @@ class Property_array : public Property_array_base { }; + +template +class Property_array_handle : public boost::put_get_helper< + typename Property_array::reference, + Property_array_handle +> { + + Property_array& m_array; + +public: + + // Necessary for use as a boost::property_type + using key_type = Index; + using value_type = T; + using reference = typename std::vector::reference; + using category = boost::lvalue_property_map_tag; + + Property_array_handle(Property_array& array) : m_array(array) {} + + T& operator[](std::size_t i) const { return m_array[i]; } + + T& operator[](std::size_t i) { return m_array[i]; } + + bool operator==(const Property_array& other) const { return &other.m_array == m_array; } + + bool operator!=(const Property_array& other) const { return !operator==(other); } + +}; + template class Property_container { - std::map>> m_property_arrays; - std::vector m_active_indices; + std::map>> m_property_arrays{}; + std::vector m_active_indices{}; public: + Property_container() = default; + + Property_container(const Property_container& other) { + m_active_indices = other.m_active_indices; + for (auto [name, array]: other.m_property_arrays) { + // todo: this could probably be made faster using emplace_hint + m_property_arrays.emplace( + name, + array->clone(m_active_indices) + ); + } + } + + Property_container& operator=(Property_container other) { + std::swap(m_active_indices, other.m_active_indices); + std::swap(m_property_arrays, other.m_property_arrays); + return *this; + } + template std::pair>, bool> get_or_add(const std::string& name, const T default_value = T()) { diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index b9e26ee15b36..f426ed689ac1 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -37,7 +37,7 @@ void test_element_access() { Property_container properties; - auto &integers = properties.add("integers", 5); + auto& integers = properties.add("integers", 5); // Reserve space for 100 elements properties.reserve(100); @@ -56,7 +56,7 @@ void test_element_access() { assert(integers[2] == 5); // Add a new property - auto &floats = properties.add("floats", 6.0f); + auto& floats = properties.add("floats", 6.0f); // The new property array should already be of the right size assert(floats.capacity() == 100); @@ -107,7 +107,7 @@ void test_emplace_group() { Property_container properties; - auto &a = properties.add("a", 5); + auto& a = properties.add("a", 5); // Insert a group of 100 elements properties.emplace_group(100); @@ -178,6 +178,42 @@ void test_append() { } +void test_constructors() { + + // Default constructor should have no properties + Property_container a{}; + assert(a.n_properties() == 0); + + // Copy constructor should duplicate all properties + a.add("a.ints", 0); + a.add("a.floats", 0.0f); + a.emplace_group(10); + a.get("a.ints")[3] = 1; + a.get("a.floats")[3] = 1.0f; + Property_container b{a}; + assert(b.n_properties() == a.n_properties() && b.n_properties() == 2); + assert(b.get("a.ints")[3] == a.get("a.ints")[3] && b.get("a.ints")[3] == 1); + assert(b.get("a.floats")[3] == a.get("a.floats")[3] && b.get("a.floats")[3] == 1.0f); + + // Copy-assignment operator should do effectively the same thing as the copy constructor + Property_container c; + c = a; + assert(c.n_properties() == a.n_properties() && c.n_properties() == 2); + assert(c.get("a.ints")[3] == a.get("a.ints")[3] && c.get("a.ints")[3] == 1); + assert(c.get("a.floats")[3] == a.get("a.floats")[3] && c.get("a.floats")[3] == 1.0f); + + // Copied property containers should not be synced with the original + a.add("a.ints2", 2); + assert(a.n_properties() == 3); + assert(b.n_properties() == 2); + assert(c.n_properties() == 2); + a.get("a.ints")[4] = 2; + assert(a.get("a.ints")[4] == 2); + assert(b.get("a.ints")[4] == 0); + assert(c.get("a.ints")[4] == 0); + +} + int main() { @@ -185,6 +221,7 @@ int main() { test_element_access(); test_emplace_group(); test_append(); + test_constructors(); return 0; } \ No newline at end of file From 14384c72af636525471521fb7b2c7737efdb817b Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 11 May 2023 11:50:07 +0200 Subject: [PATCH 038/520] Copy assignment no longer invalidates array references --- STL_Extension/include/CGAL/Properties.h | 30 ++++++++++++++-- .../test/STL_Extension/test_Properties.cpp | 34 +++++++++++-------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index e3b5df46ad5b..e04f88b8ceb6 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -20,10 +20,13 @@ class Property_array_base { virtual ~Property_array_base() = default; - // todo: Declare virtual functions here, for things which need to be done within the Property container + // Declare virtual functions here, for things which need to be done within the Property container + // todo: maybe these should be private, and made available using friend virtual std::shared_ptr> clone(const std::vector& active_indices) = 0; + virtual void copy(const Property_array_base& other) = 0; + virtual void append(const Property_array_base& other) = 0; virtual void reserve(std::size_t n) = 0; @@ -65,6 +68,12 @@ class Property_array : public Property_array_base { return new_array; } + virtual void copy(const Property_array_base& other_base) { + auto& other = dynamic_cast&>(other_base); + m_data = other.m_data; + CGAL_precondition(m_active_indices.size() == m_data.size()); + } + virtual void append(const Property_array_base& other_base) override { auto& other = dynamic_cast&>(other_base); CGAL_precondition(m_data.size() + other.m_data.size() == m_active_indices.size()); @@ -163,7 +172,24 @@ class Property_container { Property_container& operator=(Property_container other) { std::swap(m_active_indices, other.m_active_indices); - std::swap(m_property_arrays, other.m_property_arrays); + for (auto [name, other_array]: other.m_property_arrays) { + + // If this container has a property by the same name + auto it = m_property_arrays.find(name); + if (it != m_property_arrays.end()) { + auto [_, this_array] = *it; + + // No naming collisions with different types allowed + CGAL_precondition(typeid(*this_array) == typeid(*other_array)); + + // Copy the data from the other array + this_array->copy(*other_array); + + } else { + // Adds the new property + m_property_arrays.emplace(name, other_array); + } + } return *this; } diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index f426ed689ac1..92382f38450c 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -185,33 +185,39 @@ void test_constructors() { assert(a.n_properties() == 0); // Copy constructor should duplicate all properties - a.add("a.ints", 0); - a.add("a.floats", 0.0f); + a.add("ints", 0); + a.add("floats", 0.0f); a.emplace_group(10); - a.get("a.ints")[3] = 1; - a.get("a.floats")[3] = 1.0f; + a.get("ints")[3] = 1; + a.get("floats")[3] = 1.0f; Property_container b{a}; assert(b.n_properties() == a.n_properties() && b.n_properties() == 2); - assert(b.get("a.ints")[3] == a.get("a.ints")[3] && b.get("a.ints")[3] == 1); - assert(b.get("a.floats")[3] == a.get("a.floats")[3] && b.get("a.floats")[3] == 1.0f); + assert(b.get("ints")[3] == a.get("ints")[3] && b.get("ints")[3] == 1); + assert(b.get("floats")[3] == a.get("floats")[3] && b.get("floats")[3] == 1.0f); // Copy-assignment operator should do effectively the same thing as the copy constructor Property_container c; c = a; assert(c.n_properties() == a.n_properties() && c.n_properties() == 2); - assert(c.get("a.ints")[3] == a.get("a.ints")[3] && c.get("a.ints")[3] == 1); - assert(c.get("a.floats")[3] == a.get("a.floats")[3] && c.get("a.floats")[3] == 1.0f); + assert(c.get("ints")[3] == a.get("ints")[3] && c.get("ints")[3] == 1); + assert(c.get("floats")[3] == a.get("floats")[3] && c.get("floats")[3] == 1.0f); // Copied property containers should not be synced with the original - a.add("a.ints2", 2); + a.add("more_ints", 2); assert(a.n_properties() == 3); assert(b.n_properties() == 2); assert(c.n_properties() == 2); - a.get("a.ints")[4] = 2; - assert(a.get("a.ints")[4] == 2); - assert(b.get("a.ints")[4] == 0); - assert(c.get("a.ints")[4] == 0); - + a.get("ints")[4] = 2; + assert(a.get("ints")[4] == 2); + assert(b.get("ints")[4] == 0); + assert(c.get("ints")[4] == 0); + + // Copy constructor should not invalidate previously obtained array references, + // but it should update their values + auto &b_ints = b.get("ints"); + assert(b_ints[4] == 0); + b = a; + assert(b_ints[4] == 2); } From a58647dc51ea402000c2f4133511f66f4d0eab00 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 12 May 2023 13:10:49 +0200 Subject: [PATCH 039/520] Add support for move assignment & move construction --- STL_Extension/include/CGAL/Properties.h | 65 ++++++++++++++----- .../test/STL_Extension/test_Properties.cpp | 40 +++++++++--- 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/STL_Extension/include/CGAL/Properties.h b/STL_Extension/include/CGAL/Properties.h index e04f88b8ceb6..cae21815f80f 100644 --- a/STL_Extension/include/CGAL/Properties.h +++ b/STL_Extension/include/CGAL/Properties.h @@ -100,13 +100,13 @@ class Property_array : public Property_array_base { public: - const T& operator[](std::size_t i) const { - CGAL_precondition(i < m_data.size()); - return m_data[i]; + const_reference operator[](Index i) const { + CGAL_precondition(std::size_t(i) < m_data.size()); + return m_data[std::size_t(i)]; } - T& operator[](std::size_t i) { - CGAL_precondition(i < m_data.size()); + reference operator[](Index i) { + CGAL_precondition(std::size_t(i) < m_data.size()); return m_data[std::size_t(i)]; } @@ -122,10 +122,7 @@ class Property_array : public Property_array_base { template -class Property_array_handle : public boost::put_get_helper< - typename Property_array::reference, - Property_array_handle -> { +class Property_array_handle { Property_array& m_array; @@ -135,18 +132,23 @@ class Property_array_handle : public boost::put_get_helper< using key_type = Index; using value_type = T; using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; using category = boost::lvalue_property_map_tag; Property_array_handle(Property_array& array) : m_array(array) {} - T& operator[](std::size_t i) const { return m_array[i]; } + const_reference operator[](Index i) const { return m_array[i]; } - T& operator[](std::size_t i) { return m_array[i]; } + reference operator[](Index i) { return m_array[i]; } bool operator==(const Property_array& other) const { return &other.m_array == m_array; } bool operator!=(const Property_array& other) const { return !operator==(other); } + inline friend reference get(Property_array_handle p, const Index& i) { return p[i]; } + + inline friend void put(Property_array_handle p, const Index& i, const T& v) { p[i] = v; } + }; template @@ -170,8 +172,11 @@ class Property_container { } } - Property_container& operator=(Property_container other) { - std::swap(m_active_indices, other.m_active_indices); + Property_container(Property_container&& other) { *this = std::move(other); } + + // todo: maybe this could be implemented in terms of the move assignment operator? + Property_container& operator=(const Property_container& other) { + m_active_indices = other.m_active_indices; for (auto [name, other_array]: other.m_property_arrays) { // If this container has a property by the same name @@ -187,12 +192,38 @@ class Property_container { } else { // Adds the new property - m_property_arrays.emplace(name, other_array); + m_property_arrays.emplace(name, other_array->clone(m_active_indices)); } } return *this; } + Property_container& operator=(Property_container&& other) { + m_active_indices = std::move(other.m_active_indices); + for (auto [name, other_array]: other.m_property_arrays) { + + // If this container has a property by the same name + auto it = m_property_arrays.find(name); + if (it != m_property_arrays.end()) { + auto [_, this_array] = *it; + + // No naming collisions with different types allowed + CGAL_precondition(typeid(*this_array) == typeid(*other_array)); + + // Copy the data from the other array + this_array->copy(*other_array); + + } else { + // Adds the new property + m_property_arrays.emplace(name, other_array->clone(m_active_indices)); + } + } + + // The moved-from property map should retain all of its properties, but contain 0 elements + other.reserve(0); + return *this; + } + template std::pair>, bool> get_or_add(const std::string& name, const T default_value = T()) { @@ -236,7 +267,7 @@ class Property_container { */ bool remove(const std::string& name) { return m_property_arrays.erase(name) == 1; } - std::size_t n_properties() { return m_property_arrays.size(); } + std::size_t num_properties() { return m_property_arrays.size(); } public: @@ -266,7 +297,9 @@ class Property_container { auto first_unused = std::find_if(m_active_indices.begin(), m_active_indices.end(), [](bool used) { return !used; }); if (first_unused != m_active_indices.end()) { *first_unused = true; - return Index(std::distance(m_active_indices.begin(), first_unused)); + auto index = Index(std::distance(m_active_indices.begin(), first_unused)); + reset(index); + return index; } return emplace_back(); diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/STL_Extension/test/STL_Extension/test_Properties.cpp index 92382f38450c..19598921989f 100644 --- a/STL_Extension/test/STL_Extension/test_Properties.cpp +++ b/STL_Extension/test/STL_Extension/test_Properties.cpp @@ -11,11 +11,11 @@ void test_property_creation() { auto [integers, created] = properties.get_or_add("integer", 5); static_assert(std::is_same_v>>); assert(created); - assert(properties.n_properties() == 1); + assert(properties.num_properties() == 1); auto [floats, _] = properties.get_or_add("float"); static_assert(std::is_same_v>>); - assert(properties.n_properties() == 2); + assert(properties.num_properties() == 2); // get() should retreive the same arrays assert(integers.get() == properties.get("integer")); @@ -25,7 +25,7 @@ void test_property_creation() { assert(!properties.remove("not-a-real-property")); auto removed = properties.remove("integer"); assert(removed); - assert(properties.n_properties() == 1); + assert(properties.num_properties() == 1); // Add a new property auto [bools, bools_created] = properties.get_or_add("bools", false); @@ -182,7 +182,7 @@ void test_constructors() { // Default constructor should have no properties Property_container a{}; - assert(a.n_properties() == 0); + assert(a.num_properties() == 0); // Copy constructor should duplicate all properties a.add("ints", 0); @@ -191,33 +191,53 @@ void test_constructors() { a.get("ints")[3] = 1; a.get("floats")[3] = 1.0f; Property_container b{a}; - assert(b.n_properties() == a.n_properties() && b.n_properties() == 2); + assert(b.num_properties() == a.num_properties() && b.num_properties() == 2); assert(b.get("ints")[3] == a.get("ints")[3] && b.get("ints")[3] == 1); assert(b.get("floats")[3] == a.get("floats")[3] && b.get("floats")[3] == 1.0f); // Copy-assignment operator should do effectively the same thing as the copy constructor Property_container c; c = a; - assert(c.n_properties() == a.n_properties() && c.n_properties() == 2); + assert(c.num_properties() == a.num_properties() && c.num_properties() == 2); assert(c.get("ints")[3] == a.get("ints")[3] && c.get("ints")[3] == 1); assert(c.get("floats")[3] == a.get("floats")[3] && c.get("floats")[3] == 1.0f); // Copied property containers should not be synced with the original a.add("more_ints", 2); - assert(a.n_properties() == 3); - assert(b.n_properties() == 2); - assert(c.n_properties() == 2); + assert(a.num_properties() == 3); + assert(b.num_properties() == 2); + assert(c.num_properties() == 2); a.get("ints")[4] = 2; assert(a.get("ints")[4] == 2); assert(b.get("ints")[4] == 0); assert(c.get("ints")[4] == 0); - // Copy constructor should not invalidate previously obtained array references, + // Copy assignment should not invalidate previously obtained array references, // but it should update their values auto &b_ints = b.get("ints"); assert(b_ints[4] == 0); b = a; + assert(b.num_properties() == 3); assert(b_ints[4] == 2); + + // Move assignment shouldn't invalidate references either + Property_container d{c}; + auto &d_ints = d.get("ints"); + assert(d_ints[4] == 0); + d = std::move(a); + assert(d.num_properties() == 3); + assert(d_ints[4] == 2); + + // Moved-from should be empty + // All properties are preserved, though + assert(a.num_properties() == 3); + assert(a.size() == 0); + + // Move constructor should behave like move assignment + Property_container e{std::move(b)}; + assert(e.num_properties() == 3); + assert(b.num_properties() == 3); + assert(b.size() == 0); } From e48b56b1f16770086c68d11505e3aec35eb7ba45 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 12 May 2023 13:31:27 +0200 Subject: [PATCH 040/520] Move property container to Property_map package --- {STL_Extension => Property_map}/include/CGAL/Properties.h | 0 Property_map/test/Property_map/CMakeLists.txt | 1 + .../test/Property_map}/test_Properties.cpp | 0 STL_Extension/test/STL_Extension/CMakeLists.txt | 1 - 4 files changed, 1 insertion(+), 1 deletion(-) rename {STL_Extension => Property_map}/include/CGAL/Properties.h (100%) rename {STL_Extension/test/STL_Extension => Property_map/test/Property_map}/test_Properties.cpp (100%) diff --git a/STL_Extension/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h similarity index 100% rename from STL_Extension/include/CGAL/Properties.h rename to Property_map/include/CGAL/Properties.h diff --git a/Property_map/test/Property_map/CMakeLists.txt b/Property_map/test/Property_map/CMakeLists.txt index b0480bfa2098..842e795b6494 100644 --- a/Property_map/test/Property_map/CMakeLists.txt +++ b/Property_map/test/Property_map/CMakeLists.txt @@ -8,6 +8,7 @@ create_single_source_cgal_program("test_property_map.cpp") create_single_source_cgal_program("dynamic_property_map.cpp") create_single_source_cgal_program("dynamic_properties_test.cpp") create_single_source_cgal_program("kernel_converter_properties_test.cpp") +create_single_source_cgal_program("test_Properties.cpp") find_package(OpenMesh QUIET) if(OpenMesh_FOUND) diff --git a/STL_Extension/test/STL_Extension/test_Properties.cpp b/Property_map/test/Property_map/test_Properties.cpp similarity index 100% rename from STL_Extension/test/STL_Extension/test_Properties.cpp rename to Property_map/test/Property_map/test_Properties.cpp diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index 50ed354bc69b..d25906091092 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -44,7 +44,6 @@ create_single_source_cgal_program("test_N_tuple.cpp") create_single_source_cgal_program("test_namespaces.cpp") create_single_source_cgal_program("test_Nested_iterator.cpp") create_single_source_cgal_program("test_Object.cpp") -create_single_source_cgal_program("test_Properties.cpp") create_single_source_cgal_program("test_stl_extension.cpp") create_single_source_cgal_program("test_type_traits.cpp") create_single_source_cgal_program("test_Uncertain.cpp") From c9bd102fee19be8cdec87eb3c8d017ce30ac50a1 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 12 May 2023 13:40:13 +0200 Subject: [PATCH 041/520] Add _property suffix to methods which change the property list (& not the properties themselves) --- Property_map/include/CGAL/Properties.h | 12 ++-- .../test/Property_map/test_Properties.cpp | 70 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index cae21815f80f..20d449fb7416 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -226,7 +226,7 @@ class Property_container { template std::pair>, bool> - get_or_add(const std::string& name, const T default_value = T()) { + get_or_add_property(const std::string& name, const T default_value = T()) { auto [it, created] = m_property_arrays.emplace( name, std::make_shared>( @@ -240,21 +240,21 @@ class Property_container { } template - Property_array& add(const std::string& name, const T default_value = T()) { + Property_array& add_property(const std::string& name, const T default_value = T()) { // todo: I'm not settled on the naming, but it's really convenient to have a function like this - auto [array, created] = get_or_add(name, default_value); + auto [array, created] = get_or_add_property(name, default_value); CGAL_precondition(created); return array.get(); } template - const Property_array& get(const std::string& name) const { + const Property_array& get_property(const std::string& name) const { CGAL_precondition(m_property_arrays.count(name) != 0); return dynamic_cast&>(*m_property_arrays.at(name)); } template - Property_array& get(const std::string& name) { + Property_array& get_property(const std::string& name) { CGAL_precondition(m_property_arrays.count(name) != 0); return dynamic_cast&>(*m_property_arrays.at(name)); } @@ -265,7 +265,7 @@ class Property_container { * @param name * @return True if a container with this name existed, false otherwise */ - bool remove(const std::string& name) { return m_property_arrays.erase(name) == 1; } + bool remove_property(const std::string& name) { return m_property_arrays.erase(name) == 1; } std::size_t num_properties() { return m_property_arrays.size(); } diff --git a/Property_map/test/Property_map/test_Properties.cpp b/Property_map/test/Property_map/test_Properties.cpp index 19598921989f..2d7fd5c7c8d7 100644 --- a/Property_map/test/Property_map/test_Properties.cpp +++ b/Property_map/test/Property_map/test_Properties.cpp @@ -8,27 +8,27 @@ void test_property_creation() { Property_container properties; // Should return an integer array which didn't previously exist - auto [integers, created] = properties.get_or_add("integer", 5); + auto [integers, created] = properties.get_or_add_property("integer", 5); static_assert(std::is_same_v>>); assert(created); assert(properties.num_properties() == 1); - auto [floats, _] = properties.get_or_add("float"); + auto [floats, _] = properties.get_or_add_property("float"); static_assert(std::is_same_v>>); assert(properties.num_properties() == 2); // get() should retreive the same arrays - assert(integers.get() == properties.get("integer")); - assert(floats.get() == properties.get("float")); + assert(integers.get() == properties.get_property("integer")); + assert(floats.get() == properties.get_property("float")); // remove() should delete a property array & return if it existed - assert(!properties.remove("not-a-real-property")); - auto removed = properties.remove("integer"); + assert(!properties.remove_property("not-a-real-property")); + auto removed = properties.remove_property("integer"); assert(removed); assert(properties.num_properties() == 1); // Add a new property - auto [bools, bools_created] = properties.get_or_add("bools", false); + auto [bools, bools_created] = properties.get_or_add_property("bools", false); static_assert(std::is_same_v>>); Property_array& b = bools.get(); } @@ -37,7 +37,7 @@ void test_element_access() { Property_container properties; - auto& integers = properties.add("integers", 5); + auto& integers = properties.add_property("integers", 5); // Reserve space for 100 elements properties.reserve(100); @@ -56,7 +56,7 @@ void test_element_access() { assert(integers[2] == 5); // Add a new property - auto& floats = properties.add("floats", 6.0f); + auto& floats = properties.add_property("floats", 6.0f); // The new property array should already be of the right size assert(floats.capacity() == 100); @@ -107,7 +107,7 @@ void test_emplace_group() { Property_container properties; - auto& a = properties.add("a", 5); + auto& a = properties.add_property("a", 5); // Insert a group of 100 elements properties.emplace_group(100); @@ -147,13 +147,13 @@ void test_append() { // Create a pair of property containers with similar contents Property_container properties_a, properties_b; - properties_a.add("ints", 1); - properties_b.add("ints", 2); - properties_a.add("floats", 3.0f); - properties_b.add("floats", 4.0f); + properties_a.add_property("ints", 1); + properties_b.add_property("ints", 2); + properties_a.add_property("floats", 3.0f); + properties_b.add_property("floats", 4.0f); // One container will also contain an extra property - properties_a.add("bools", true); + properties_a.add_property("bools", true); // Add some values to both property sets properties_a.emplace_group(10); @@ -167,14 +167,14 @@ void test_append() { assert(properties_b.size() == 5); // Initialized values from the second group should appear after those of the first - assert(properties_a.get("ints")[5] == 1); - assert(properties_a.get("ints")[12] == 2); - assert(properties_a.get("floats")[5] == 3.0f); - assert(properties_a.get("floats")[12] == 4.0f); + assert(properties_a.get_property("ints")[5] == 1); + assert(properties_a.get_property("ints")[12] == 2); + assert(properties_a.get_property("floats")[5] == 3.0f); + assert(properties_a.get_property("floats")[12] == 4.0f); // Additional properties in the first group should have expanded too, and been filled with defaults // note: the property array must be const, because non const operator[] doesn't work for vector! - assert(std::as_const(properties_a).get("bools")[12] == true); + assert(std::as_const(properties_a).get_property("bools")[12] == true); } @@ -185,36 +185,36 @@ void test_constructors() { assert(a.num_properties() == 0); // Copy constructor should duplicate all properties - a.add("ints", 0); - a.add("floats", 0.0f); + a.add_property("ints", 0); + a.add_property("floats", 0.0f); a.emplace_group(10); - a.get("ints")[3] = 1; - a.get("floats")[3] = 1.0f; + a.get_property("ints")[3] = 1; + a.get_property("floats")[3] = 1.0f; Property_container b{a}; assert(b.num_properties() == a.num_properties() && b.num_properties() == 2); - assert(b.get("ints")[3] == a.get("ints")[3] && b.get("ints")[3] == 1); - assert(b.get("floats")[3] == a.get("floats")[3] && b.get("floats")[3] == 1.0f); + assert(b.get_property("ints")[3] == a.get_property("ints")[3] && b.get_property("ints")[3] == 1); + assert(b.get_property("floats")[3] == a.get_property("floats")[3] && b.get_property("floats")[3] == 1.0f); // Copy-assignment operator should do effectively the same thing as the copy constructor Property_container c; c = a; assert(c.num_properties() == a.num_properties() && c.num_properties() == 2); - assert(c.get("ints")[3] == a.get("ints")[3] && c.get("ints")[3] == 1); - assert(c.get("floats")[3] == a.get("floats")[3] && c.get("floats")[3] == 1.0f); + assert(c.get_property("ints")[3] == a.get_property("ints")[3] && c.get_property("ints")[3] == 1); + assert(c.get_property("floats")[3] == a.get_property("floats")[3] && c.get_property("floats")[3] == 1.0f); // Copied property containers should not be synced with the original - a.add("more_ints", 2); + a.add_property("more_ints", 2); assert(a.num_properties() == 3); assert(b.num_properties() == 2); assert(c.num_properties() == 2); - a.get("ints")[4] = 2; - assert(a.get("ints")[4] == 2); - assert(b.get("ints")[4] == 0); - assert(c.get("ints")[4] == 0); + a.get_property("ints")[4] = 2; + assert(a.get_property("ints")[4] == 2); + assert(b.get_property("ints")[4] == 0); + assert(c.get_property("ints")[4] == 0); // Copy assignment should not invalidate previously obtained array references, // but it should update their values - auto &b_ints = b.get("ints"); + auto &b_ints = b.get_property("ints"); assert(b_ints[4] == 0); b = a; assert(b.num_properties() == 3); @@ -222,7 +222,7 @@ void test_constructors() { // Move assignment shouldn't invalidate references either Property_container d{c}; - auto &d_ints = d.get("ints"); + auto &d_ints = d.get_property("ints"); assert(d_ints[4] == 0); d = std::move(a); assert(d.num_properties() == 3); From ac6fbf04674501f3ab0a6a36fb03ea015f627aa2 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 17 May 2023 16:49:46 +0200 Subject: [PATCH 042/520] Add partly-working adaptation of Surface_mesh to use the new Property map system --- .../graph/IO/Generic_facegraph_builder.h | 3 + Property_map/include/CGAL/Properties.h | 18 +- .../test/Property_map/test_Properties.cpp | 2 + .../include/CGAL/Surface_mesh/IO/OFF.h | 86 +- .../include/CGAL/Surface_mesh/Surface_mesh.h | 3337 +++++++---------- .../boost/graph/graph_traits_Surface_mesh.h | 2 +- .../boost/graph/properties_Surface_mesh.h | 288 +- .../test/Surface_mesh/sm_join_test.cpp | 31 +- .../test/Surface_mesh/sm_open_colored_off.cpp | 34 +- .../test/Surface_mesh/surface_mesh_test.cpp | 10 +- 10 files changed, 1611 insertions(+), 2200 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h index 331f8a0f44b0..4952f5f0a5f5 100644 --- a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h +++ b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h @@ -102,6 +102,9 @@ class Generic_facegraph_builder // Construct the graph VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_property_map(CGAL::vertex_point, g)); + // Default constructors should only be used when VNM, VCM, etc. are defined as Constant_property_maps + // This fails in cases where get_parameter() succeeds + // even though internal_np::Lookup_named_param_def defaulted to Constant_property_map VNM vnm = choose_parameter(get_parameter(np, internal_np::vertex_normal_map), VNM()); VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map), VCM()); VTM vtm = choose_parameter(get_parameter(np, internal_np::vertex_texture_map), VTM()); diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 20d449fb7416..445ebfcbd5dd 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -277,9 +277,9 @@ class Property_container { array->reserve(n); } - std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } + [[nodiscard]] std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } - std::size_t capacity() const { return m_active_indices.size(); } + [[nodiscard]] std::size_t capacity() const { return m_active_indices.size(); } Index emplace_back() { @@ -378,6 +378,20 @@ class Property_container { return m_active_indices[i] = true; } + std::vector active_list() const { + std::vector indices; + for (std::size_t i = 0; i < m_active_indices.size(); ++i) + if (m_active_indices[i]) indices.emplace_back(i); + return indices; + } + + std::vector inactive_list() const { + std::vector indices; + for (std::size_t i = 0; i < m_active_indices.size(); ++i) + if (!m_active_indices[i]) indices.emplace_back(i); + return indices; + } + /*! * Adds the elements of the other container to this container for each property which is present in this container. * diff --git a/Property_map/test/Property_map/test_Properties.cpp b/Property_map/test/Property_map/test_Properties.cpp index 2d7fd5c7c8d7..af1b4ca2cf46 100644 --- a/Property_map/test/Property_map/test_Properties.cpp +++ b/Property_map/test/Property_map/test_Properties.cpp @@ -86,6 +86,8 @@ void test_element_access() { properties.erase(1); assert(properties.size() == 2); assert(properties.capacity() == 100); + assert(properties.active_list().size() == 2); + assert(properties.inactive_list().size() == 98); // A newly emplaced element should take the empty slot assert(properties.emplace() == 1); diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h index fced570172d6..0ebeba94af60 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h @@ -108,24 +108,11 @@ bool read_OFF_with_or_without_fcolors(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; - typename Mesh::template Property_map fcm; - bool is_fcm_requested = !(is_default_parameter::value); - if(!is_fcm_requested && scanner.has_colors()) - { - bool created; - std::tie(fcm, created) = sm.template add_property_map("f:color", Color(0,0,0)); - CGAL_assertion(created); - is_fcm_requested = true; - } - - if(is_fcm_requested) - { - FCM fcolors = choose_parameter(get_parameter(np, internal_np::face_color_map), fcm); - return CGAL::IO::internal::read_OFF_BGL(is, sm, np.face_color_map(fcolors)); - } - else - { + if (is_fcm_requested || scanner.has_colors()) { + auto [fcm, created] = sm.template property_map("f:color"); + return CGAL::IO::internal::read_OFF_BGL(is, sm, np.face_color_map(fcm)); + } else { return CGAL::IO::internal::read_OFF_BGL(is, sm, np); } } @@ -147,24 +134,11 @@ bool read_OFF_with_or_without_vtextures(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; - typename Mesh::template Property_map vtm; - bool is_vtm_requested = !(is_default_parameter::value); - if(!is_vtm_requested && scanner.has_textures()) - { - bool created; - std::tie(vtm, created) = sm.template add_property_map("v:texcoord"); - CGAL_assertion(created); - is_vtm_requested = true; - } - - if(is_vtm_requested) - { - VTM vtextures = choose_parameter(get_parameter(np, internal_np::vertex_texture_map), vtm); - return read_OFF_with_or_without_fcolors(is, sm, scanner, np.vertex_texture_map(vtextures)); - } - else - { + if (is_vtm_requested || scanner.has_textures()) { + auto [vtm, created] = sm.template property_map("v:texcoord"); + return read_OFF_with_or_without_fcolors(is, sm, scanner, np.vertex_texture_map(vtm)); + } else { return read_OFF_with_or_without_fcolors(is, sm, scanner, np); } } @@ -185,24 +159,11 @@ bool read_OFF_with_or_without_vcolors(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; - typename Mesh::template Property_map vcm; - - bool is_vcm_requested = !(is_default_parameter::value); - if(!is_vcm_requested && scanner.has_colors()) - { - bool created; - std::tie(vcm, created) = sm.template add_property_map("v:color", Color(0,0,0)); - CGAL_assertion(created); - is_vcm_requested = true; - } - - if(is_vcm_requested) - { - VCM vcolors = choose_parameter(get_parameter(np, internal_np::vertex_color_map), vcm); - return read_OFF_with_or_without_vtextures(is, sm, scanner, np.vertex_color_map(vcolors)); - } - else - { + bool is_vcm_requested = !(is_default_parameter::value); + if (is_vcm_requested || scanner.has_colors()) { + auto [vcm, created] = sm.template property_map("v:color"); + return read_OFF_with_or_without_vtextures(is, sm, scanner, np.vertex_color_map(vcm)); + } else { return read_OFF_with_or_without_vtextures(is, sm, scanner, np); } } @@ -224,24 +185,11 @@ bool read_OFF_with_or_without_vnormals(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; - typename Mesh::template Property_map vnm; - bool is_vnm_requested = !(is_default_parameter::value); - if(!is_vnm_requested && scanner.has_normals()) - { - bool created; - std::tie(vnm, created) = sm.template add_property_map("v:normal"); - CGAL_assertion(created); - is_vnm_requested = true; - } - - if(is_vnm_requested) - { - VNM vnormals = choose_parameter(get_parameter(np, internal_np::vertex_normal_map), vnm); - return read_OFF_with_or_without_vcolors(is, sm, scanner, np.vertex_normal_map(vnormals)); - } - else - { + if (is_vnm_requested || scanner.has_normals()) { + auto [vnm, created] = sm.template property_map("v:normal"); + return read_OFF_with_or_without_vcolors(is, sm, scanner, np.vertex_normal_map(vnm)); + } else { return read_OFF_with_or_without_vcolors(is, sm, scanner, np); } } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 47232cbabd51..5a45933e06b2 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -239,7 +239,7 @@ namespace CGAL { class SM_Edge_index { public: - typedef boost::uint32_t size_type; + typedef std::size_t size_type; SM_Edge_index() : halfedge_((std::numeric_limits::max)()) { } @@ -341,334 +341,319 @@ class Surface_mesh public: #ifndef DOXYGEN_RUNNING - template - struct Property_map : Properties::Property_map_base > - { - typedef Properties::Property_map_base > Base; - typedef typename Base::reference reference; - Property_map() = default; - Property_map(const Base& pm): Base(pm) {} - }; - template - struct Get_property_map { - typedef Property_map type; - }; + template + using Property_container = Properties::Property_container; + + template + using Property_array = Properties::Property_array; + + template + using Property_map = Properties::Property_array_handle; + #endif // DOXYGEN_RUNNING - /// \name Basic Types - /// - ///@{ + /// \name Basic Types + /// + ///@{ - /// The point type. - typedef P Point; + /// The point type. + typedef P Point; - /// The type used to represent an index. - typedef boost::uint32_t size_type; + /// The type used to represent an index. + typedef boost::uint32_t size_type; - ///@} + ///@} - /// \name Basic Elements - /// - ///@{ + /// \name Basic Elements + /// + ///@{ #ifdef DOXYGEN_RUNNING - /// This class represents a vertex. - /// \cgalModels `Index` - /// \cgalModels `LessThanComparable` - /// \cgalModels `Hashable` - /// \sa `Halfedge_index`, `Edge_index`, `Face_index` - class Vertex_index - { - public: - /// %Default constructor. - Vertex_index(){} + /// This class represents a vertex. + /// \cgalModels `Index` + /// \cgalModels `LessThanComparable` + /// \cgalModels `Hashable` + /// \sa `Halfedge_index`, `Edge_index`, `Face_index` + class Vertex_index + { + public: + /// %Default constructor. + Vertex_index(){} - Vertex_index(size_type _idx){} + Vertex_index(size_type _idx){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Vertex_index const& v) - {} - }; + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Vertex_index const& v) + {} + }; #else typedef SM_Vertex_index Vertex_index; #endif #ifdef DOXYGEN_RUNNING - /// This class represents a halfedge. - /// \cgalModels `Index` - /// \cgalModels `LessThanComparable` - /// \cgalModels `Hashable` - /// \sa `Vertex_index`, `Edge_index`, `Face_index` - class Halfedge_index - { - public: - /// %Default constructor - Halfedge_index(){} + /// This class represents a halfedge. + /// \cgalModels `Index` + /// \cgalModels `LessThanComparable` + /// \cgalModels `Hashable` + /// \sa `Vertex_index`, `Edge_index`, `Face_index` + class Halfedge_index + { + public: + /// %Default constructor + Halfedge_index(){} - Halfedge_index(size_type _idx){} + Halfedge_index(size_type _idx){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Halfedge_index const& h) - { - } + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Halfedge_index const& h) + { + } - }; + }; #else typedef SM_Halfedge_index Halfedge_index; #endif #ifdef DOXYGEN_RUNNING - /// This class represents a face - /// \cgalModels `Index` - /// \cgalModels `LessThanComparable` - /// \cgalModels `Hashable` - /// \sa `Vertex_index`, `Halfedge_index`, `Edge_index` - class Face_index - { - public: - /// %Default constructor - Face_index(){} + /// This class represents a face + /// \cgalModels `Index` + /// \cgalModels `LessThanComparable` + /// \cgalModels `Hashable` + /// \sa `Vertex_index`, `Halfedge_index`, `Edge_index` + class Face_index + { + public: + /// %Default constructor + Face_index(){} - Face_index(size_type _idx){} + Face_index(size_type _idx){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Face_index const& f) - {} - }; + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Face_index const& f) + {} + }; #else typedef SM_Face_index Face_index; #endif #ifdef DOXYGEN_RUNNING - /// This class represents an edge. - /// \cgalModels `Index` - /// \cgalModels `LessThanComparable` - /// \cgalModels `Hashable` - /// \sa `Vertex_index`, `Halfedge_index`, `Face_index` - class Edge_index - { - public: - /// %Default constructor - Edge_index(){} + /// This class represents an edge. + /// \cgalModels `Index` + /// \cgalModels `LessThanComparable` + /// \cgalModels `Hashable` + /// \sa `Vertex_index`, `Halfedge_index`, `Face_index` + class Edge_index + { + public: + /// %Default constructor + Edge_index(){} - Edge_index(size_type idx){} + Edge_index(size_type idx){} - /// constructs an `Edge_index` from a halfedge. - Edge_index(Halfedge_index he){} + /// constructs an `Edge_index` from a halfedge. + Edge_index(Halfedge_index he){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Edge_index const& e) - {} - }; + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Edge_index const& e) + {} + }; #else typedef SM_Edge_index Edge_index; #endif - ///@} + ///@} #ifndef CGAL_TEST_SURFACE_MESH private: //-------------------------------------------------- connectivity types #endif - /// This type stores the vertex connectivity - /// \sa `Halfedge_connectivity`, `Face_connectivity` - struct Vertex_connectivity - { - /// an incoming halfedge per vertex (it will be a border halfedge for border vertices) - Halfedge_index halfedge_; - }; + /// This type stores the vertex connectivity + /// \sa `Halfedge_connectivity`, `Face_connectivity` + struct Vertex_connectivity { + /// an incoming halfedge per vertex (it will be a border halfedge for border vertices) + Halfedge_index halfedge_; + }; - /// This type stores the halfedge connectivity - /// \sa `Vertex_connectivity`, `Face_connectivity` - struct Halfedge_connectivity - { - /// face incident to halfedge - Face_index face_; - /// vertex the halfedge points to - Vertex_index vertex_; - /// next halfedge within a face (or along a border) - Halfedge_index next_halfedge_; - /// previous halfedge within a face (or along a border) - Halfedge_index prev_halfedge_; - }; + /// This type stores the halfedge connectivity + /// \sa `Vertex_connectivity`, `Face_connectivity` + struct Halfedge_connectivity { + /// face incident to halfedge + Face_index face_; + /// vertex the halfedge points to + Vertex_index vertex_; + /// next halfedge within a face (or along a border) + Halfedge_index next_halfedge_; + /// previous halfedge within a face (or along a border) + Halfedge_index prev_halfedge_; + }; - /// This type stores the face connectivity - /// \sa `Vertex_connectivity`, `Halfedge_connectivity` - struct Face_connectivity - { - /// a halfedge that is part of the face - Halfedge_index halfedge_; - }; + /// This type stores the face connectivity + /// \sa `Vertex_connectivity`, `Halfedge_connectivity` + struct Face_connectivity { + /// a halfedge that is part of the face + Halfedge_index halfedge_; + }; private: //------------------------------------------------------ iterator types - template - class Index_iterator - : public boost::iterator_facade< Index_iterator, - Index_, - std::random_access_iterator_tag, - Index_ - > - { - typedef boost::iterator_facade< Index_iterator, - Index_, - std::random_access_iterator_tag, - Index_> Facade; - public: - Index_iterator() : hnd_(), mesh_(nullptr) {} - Index_iterator(const Index_& h, const Surface_mesh* m) - : hnd_(h), mesh_(m) { - if (mesh_ && mesh_->has_garbage()){ - while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; - } - } - private: - friend class boost::iterator_core_access; - void increment() - { - ++hnd_; - CGAL_assertion(mesh_ != nullptr); + template + class Index_iterator + : public boost::iterator_facade, + Index_, + std::random_access_iterator_tag, + Index_ + > { + typedef boost::iterator_facade, + Index_, + std::random_access_iterator_tag, + Index_> Facade; + public: + Index_iterator() : hnd_(), mesh_(nullptr) {} + + Index_iterator(const Index_& h, const Surface_mesh* m) + : hnd_(h), mesh_(m) { + if (mesh_ && mesh_->has_garbage()) { + while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; + } + } - if(mesh_->has_garbage()) - while ( mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; - } + private: + friend class boost::iterator_core_access; - void decrement() - { - --hnd_; - CGAL_assertion(mesh_ != nullptr); - if(mesh_->has_garbage()) - while ( mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) --hnd_; - } + void increment() { + ++hnd_; + CGAL_assertion(mesh_ != nullptr); - void advance(std::ptrdiff_t n) - { - CGAL_assertion(mesh_ != nullptr); - - if (mesh_->has_garbage()) - { - if (n > 0) - for (std::ptrdiff_t i = 0; i < n; ++ i) - increment(); - else - for (std::ptrdiff_t i = 0; i < -n; ++ i) - decrement(); - } - else - hnd_ += n; - } + if (mesh_->has_garbage()) + while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; + } - std::ptrdiff_t distance_to(const Index_iterator& other) const - { - if (mesh_->has_garbage()) - { - bool forward = (other.hnd_ > hnd_); - - std::ptrdiff_t out = 0; - Index_iterator it = *this; - while (!it.equal(other)) - { - if (forward) - { - ++ it; - ++ out; - } - else - { - -- it; - -- out; - } - } - return out; - } - - // else - return std::ptrdiff_t(other.hnd_) - std::ptrdiff_t(this->hnd_); - } + void decrement() { + --hnd_; + CGAL_assertion(mesh_ != nullptr); + if (mesh_->has_garbage()) + while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) --hnd_; + } - bool equal(const Index_iterator& other) const - { - return this->hnd_ == other.hnd_; + void advance(std::ptrdiff_t n) { + CGAL_assertion(mesh_ != nullptr); + + if (mesh_->has_garbage()) { + if (n > 0) + for (std::ptrdiff_t i = 0; i < n; ++i) + increment(); + else + for (std::ptrdiff_t i = 0; i < -n; ++i) + decrement(); + } else + hnd_ += n; + } + + std::ptrdiff_t distance_to(const Index_iterator& other) const { + if (mesh_->has_garbage()) { + bool forward = (other.hnd_ > hnd_); + + std::ptrdiff_t out = 0; + Index_iterator it = *this; + while (!it.equal(other)) { + if (forward) { + ++it; + ++out; + } else { + --it; + --out; + } } + return out; + } - Index_ dereference() const { return hnd_; } + // else + return std::ptrdiff_t(other.hnd_) - std::ptrdiff_t(this->hnd_); + } - Index_ hnd_; - const Surface_mesh* mesh_; + bool equal(const Index_iterator& other) const { + return this->hnd_ == other.hnd_; + } + + Index_ dereference() const { return hnd_; } + + Index_ hnd_; + const Surface_mesh* mesh_; + + }; - }; public: - /// \name Range Types - /// - /// Each range `R` in this section has a nested type `R::iterator`, - /// is convertible to `std::pair`, so that one can use `boost::tie()`, - /// and can be used with `BOOST_FOREACH()`, as well as with the C++11 range based for-loop. + /// \name Range Types + /// + /// Each range `R` in this section has a nested type `R::iterator`, + /// is convertible to `std::pair`, so that one can use `boost::tie()`, + /// and can be used with `BOOST_FOREACH()`, as well as with the C++11 range based for-loop. - ///@{ + ///@{ #ifndef DOXYGEN_RUNNING - typedef Index_iterator Vertex_iterator; + typedef Index_iterator Vertex_iterator; #endif - /// \brief The range over all vertex indices. - /// - /// A model of BidirectionalRange with value type `Vertex_index`. - /// \sa `vertices()` - /// \sa `Halfedge_range`, `Edge_range`, `Face_range` + /// \brief The range over all vertex indices. + /// + /// A model of BidirectionalRange with value type `Vertex_index`. + /// \sa `vertices()` + /// \sa `Halfedge_range`, `Edge_range`, `Face_range` #ifdef DOXYGEN_RUNNING - typedef unspecified_type Vertex_range; + typedef unspecified_type Vertex_range; #else - typedef Iterator_range Vertex_range; + typedef Iterator_range Vertex_range; #endif #ifndef DOXYGEN_RUNNING - typedef Index_iterator Halfedge_iterator; + typedef Index_iterator Halfedge_iterator; #endif - /// \brief The range over all halfedge indices. - /// - /// A model of BidirectionalRange with value type `Halfedge_index`. - /// \sa `halfedges()` - /// \sa `Vertex_range`, `Edge_range`, `Face_range` + /// \brief The range over all halfedge indices. + /// + /// A model of BidirectionalRange with value type `Halfedge_index`. + /// \sa `halfedges()` + /// \sa `Vertex_range`, `Edge_range`, `Face_range` #ifdef DOXYGEN_RUNNING - typedef unspecified_type Halfedge_range; + typedef unspecified_type Halfedge_range; #else - typedef Iterator_range Halfedge_range; + typedef Iterator_range Halfedge_range; #endif #ifndef DOXYGEN_RUNNING - typedef Index_iterator Edge_iterator; + typedef Index_iterator Edge_iterator; #endif - /// \brief The range over all edge indices. - /// - /// A model of BidirectionalRange with value type `Edge_index`. - /// \sa `edges()` - /// \sa `Halfedge_range`, `Vertex_range`, `Face_range` + /// \brief The range over all edge indices. + /// + /// A model of BidirectionalRange with value type `Edge_index`. + /// \sa `edges()` + /// \sa `Halfedge_range`, `Vertex_range`, `Face_range` #ifdef DOXYGEN_RUNNING - typedef unspecified_type Edge_range; + typedef unspecified_type Edge_range; #else - typedef Iterator_range Edge_range; + typedef Iterator_range Edge_range; #endif #ifndef DOXYGEN_RUNNING - typedef Index_iterator Face_iterator; + typedef Index_iterator Face_iterator; #endif - /// \brief The range over all face indices. - /// - /// A model of BidirectionalRange with value type `Face_index`. - /// \sa `faces()` - /// \sa `Vertex_range`, `Halfedge_range`, `Edge_range` - #ifdef DOXYGEN_RUNNING - typedef unspecified_type Face_range; + /// \brief The range over all face indices. + /// + /// A model of BidirectionalRange with value type `Face_index`. + /// \sa `faces()` + /// \sa `Vertex_range`, `Halfedge_range`, `Edge_range` +#ifdef DOXYGEN_RUNNING + typedef unspecified_type Face_range; #else - typedef Iterator_range Face_range; + typedef Iterator_range Face_range; #endif #ifndef DOXYGEN_RUNNING @@ -676,229 +661,215 @@ class Surface_mesh typedef CGAL::Vertex_around_target_iterator Vertex_around_target_iterator; typedef Iterator_range Vertex_around_target_range; - typedef CGAL::Halfedge_around_target_iterator Halfedge_around_target_iterator; + typedef CGAL::Halfedge_around_target_iterator Halfedge_around_target_iterator; typedef Iterator_range Halfedge_around_target_range; - typedef CGAL::Face_around_target_iterator Face_around_target_iterator; + typedef CGAL::Face_around_target_iterator Face_around_target_iterator; typedef Iterator_range Face_around_target_range; - typedef CGAL::Vertex_around_face_iterator Vertex_around_face_iterator; + typedef CGAL::Vertex_around_face_iterator Vertex_around_face_iterator; typedef Iterator_range Vertex_around_face_range; - typedef CGAL::Halfedge_around_face_iterator Halfedge_around_face_iterator; + typedef CGAL::Halfedge_around_face_iterator Halfedge_around_face_iterator; typedef Iterator_range Halfedge_around_face_range; - typedef CGAL::Face_around_face_iterator Face_around_face_iterator; + typedef CGAL::Face_around_face_iterator Face_around_face_iterator; typedef Iterator_range Face_around_face_range; #endif - /// @cond CGAL_BEGIN_END - /// Start iterator for vertices. - Vertex_iterator vertices_begin() const - { - return Vertex_iterator(Vertex_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for vertices. + Vertex_iterator vertices_begin() const { + return Vertex_iterator(Vertex_index(0), this); + } - /// End iterator for vertices. - Vertex_iterator vertices_end() const - { - return Vertex_iterator(Vertex_index(num_vertices()), this); - } - /// @endcond + /// End iterator for vertices. + Vertex_iterator vertices_end() const { + return Vertex_iterator(Vertex_index(number_of_vertices()), this); + } + /// @endcond - /// returns the iterator range of the vertices of the mesh. - Vertex_range vertices() const { - return make_range(vertices_begin(), vertices_end()); - } + /// returns the iterator range of the vertices of the mesh. + Vertex_range vertices() const { + return make_range(vertices_begin(), vertices_end()); + } - /// @cond CGAL_BEGIN_END - /// Start iterator for halfedges. - Halfedge_iterator halfedges_begin() const - { - return Halfedge_iterator(Halfedge_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for halfedges. + Halfedge_iterator halfedges_begin() const { + return Halfedge_iterator(Halfedge_index(0), this); + } - /// End iterator for halfedges. - Halfedge_iterator halfedges_end() const - { - return Halfedge_iterator(Halfedge_index(num_halfedges()), this); - } - /// @endcond + /// End iterator for halfedges. + Halfedge_iterator halfedges_end() const { + return Halfedge_iterator(Halfedge_index(number_of_halfedges()), this); + } + /// @endcond - /// returns the iterator range of the halfedges of the mesh. - Halfedge_range halfedges() const { - return make_range(halfedges_begin(), halfedges_end()); - } + /// returns the iterator range of the halfedges of the mesh. + Halfedge_range halfedges() const { + return make_range(halfedges_begin(), halfedges_end()); + } - /// @cond CGAL_BEGIN_END - /// Start iterator for edges. - Edge_iterator edges_begin() const - { - return Edge_iterator(Edge_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for edges. + Edge_iterator edges_begin() const { + return Edge_iterator(Edge_index(0), this); + } - /// End iterator for edges. - Edge_iterator edges_end() const - { - return Edge_iterator(Edge_index(num_edges()), this); - } - /// @endcond + /// End iterator for edges. + Edge_iterator edges_end() const { + return Edge_iterator(Edge_index(number_of_edges()), this); + } + /// @endcond - /// returns the iterator range of the edges of the mesh. - Edge_range edges() const - { - return make_range(edges_begin(), edges_end()); - } + /// returns the iterator range of the edges of the mesh. + Edge_range edges() const { + return make_range(edges_begin(), edges_end()); + } - /// @cond CGAL_BEGIN_END - /// Start iterator for faces. - Face_iterator faces_begin() const - { - return Face_iterator(Face_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for faces. + Face_iterator faces_begin() const { + return Face_iterator(Face_index(0), this); + } - /// End iterator for faces. - Face_iterator faces_end() const - { - return Face_iterator(Face_index(num_faces()), this); - } - /// @endcond + /// End iterator for faces. + Face_iterator faces_end() const { + return Face_iterator(Face_index(number_of_faces()), this); + } + /// @endcond - /// returns the iterator range of the faces of the mesh. - Face_range faces() const { - return make_range(faces_begin(), faces_end()); - } + /// returns the iterator range of the faces of the mesh. + Face_range faces() const { + return make_range(faces_begin(), faces_end()); + } #ifndef DOXYGEN_RUNNING - /// returns the iterator range for vertices around vertex `target(h)`, starting at `source(h)`. - Vertex_around_target_range vertices_around_target(Halfedge_index h) const - { - return CGAL::vertices_around_target(h,*this); - } - /// returns the iterator range for incoming halfedges around vertex `target(h)`, starting at `h`. - Halfedge_around_target_range halfedges_around_target(Halfedge_index h) const - { - return CGAL::halfedges_around_target(h,*this); - } + /// returns the iterator range for vertices around vertex `target(h)`, starting at `source(h)`. + Vertex_around_target_range vertices_around_target(Halfedge_index h) const { + return CGAL::vertices_around_target(h, *this); + } - /// returns the iterator range for faces around vertex `target(h)`, starting at `face(h)`. - Face_around_target_range faces_around_target(Halfedge_index h) const - { - return CGAL::faces_around_target(h,*this); - } + /// returns the iterator range for incoming halfedges around vertex `target(h)`, starting at `h`. + Halfedge_around_target_range halfedges_around_target(Halfedge_index h) const { + return CGAL::halfedges_around_target(h, *this); + } - /// returns the iterator range for vertices around face `face(h)`, starting at `target(h)`. - Vertex_around_face_range vertices_around_face(Halfedge_index h) const - { - return CGAL::vertices_around_face(h,*this); - } + /// returns the iterator range for faces around vertex `target(h)`, starting at `face(h)`. + Face_around_target_range faces_around_target(Halfedge_index h) const { + return CGAL::faces_around_target(h, *this); + } - /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. - Halfedge_around_face_range halfedges_around_face(Halfedge_index h) const - { - return CGAL::halfedges_around_face(h,*this); - } + /// returns the iterator range for vertices around face `face(h)`, starting at `target(h)`. + Vertex_around_face_range vertices_around_face(Halfedge_index h) const { + return CGAL::vertices_around_face(h, *this); + } - /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. - Face_around_face_range faces_around_face(Halfedge_index h) const - { - return CGAL::faces_around_face(h,*this); - } + /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. + Halfedge_around_face_range halfedges_around_face(Halfedge_index h) const { + return CGAL::halfedges_around_face(h, *this); + } + + /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. + Face_around_face_range faces_around_face(Halfedge_index h) const { + return CGAL::faces_around_face(h, *this); + } #endif - ///@} + ///@} public: #ifndef DOXYGEN_RUNNING - /// \name Circulator Types - /// - /// The following circulators enable to iterate through the elements around a face or vertex. - /// As explained in the \ref SurfaceMeshOrientation "User Manual", we can speak of a - /// *clockwise* or *counterclockwise* - /// traversal, by looking at the surface from the right side. - ///@{ + /// \name Circulator Types + /// + /// The following circulators enable to iterate through the elements around a face or vertex. + /// As explained in the \ref SurfaceMeshOrientation "User Manual", we can speak of a + /// *clockwise* or *counterclockwise* + /// traversal, by looking at the surface from the right side. + ///@{ - /// \brief This class circulates clockwise through all - /// one-ring neighbors of a vertex. - /// A model of `BidirectionalCirculator` with value type `Vertex_index`. - /// \sa `Halfedge_around_target_circulator`, `Face_around_target_circulator` + /// \brief This class circulates clockwise through all + /// one-ring neighbors of a vertex. + /// A model of `BidirectionalCirculator` with value type `Vertex_index`. + /// \sa `Halfedge_around_target_circulator`, `Face_around_target_circulator` typedef CGAL::Vertex_around_target_circulator Vertex_around_target_circulator; - /// \brief This class circulates clockwise through all incident faces of a vertex. - /// A model of `BidirectionalCirculator` with value type `Face_index`. - /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` + /// \brief This class circulates clockwise through all incident faces of a vertex. + /// A model of `BidirectionalCirculator` with value type `Face_index`. + /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` typedef CGAL::Face_around_target_circulator Face_around_target_circulator; - /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as target. - /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. - /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` + /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as target. + /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. + /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` typedef CGAL::Halfedge_around_target_circulator Halfedge_around_target_circulator; - /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as source. - /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. - /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` + /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as source. + /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. + /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` typedef CGAL::Halfedge_around_source_circulator Halfedge_around_source_circulator; - /// \brief This class circulates counterclockwise through all vertices around a face. - /// A model of `BidirectionalCirculator` with value type `Vertex_index`. + /// \brief This class circulates counterclockwise through all vertices around a face. + /// A model of `BidirectionalCirculator` with value type `Vertex_index`. - typedef CGAL::Vertex_around_face_circulator Vertex_around_face_circulator; + typedef CGAL::Vertex_around_face_circulator Vertex_around_face_circulator; - /// \brief This class circulates counterclockwise through all halfedges around a face. - /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. + /// \brief This class circulates counterclockwise through all halfedges around a face. + /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. - typedef CGAL::Halfedge_around_face_circulator Halfedge_around_face_circulator; + typedef CGAL::Halfedge_around_face_circulator Halfedge_around_face_circulator; - /// \brief This class circulates counterclockwise through all faces around a face. - /// A model of `BidirectionalCirculator` with value type `Face_index`. - /// Note that the face index is the same after `operator++`, if the neighboring faces share - /// several halfedges. + /// \brief This class circulates counterclockwise through all faces around a face. + /// A model of `BidirectionalCirculator` with value type `Face_index`. + /// Note that the face index is the same after `operator++`, if the neighboring faces share + /// several halfedges. - typedef CGAL::Face_around_face_circulator Face_around_face_circulator; + typedef CGAL::Face_around_face_circulator Face_around_face_circulator; /// @} #endif /// @cond CGAL_DOCUMENT_INTERNALS // typedefs which make it easier to write the partial specialisation of boost::graph_traits - typedef Vertex_index vertex_index; - typedef P vertex_property_type; + typedef Vertex_index vertex_index; + typedef P vertex_property_type; typedef Halfedge_index halfedge_index; - typedef Edge_index edge_index; - typedef Face_index face_index; + typedef Edge_index edge_index; + typedef Face_index face_index; - typedef Vertex_iterator vertex_iterator; - typedef Halfedge_iterator halfedge_iterator; - typedef Edge_iterator edge_iterator; - typedef Face_iterator face_iterator; - typedef CGAL::Out_edge_iterator out_edge_iterator; + typedef Vertex_iterator vertex_iterator; + typedef Halfedge_iterator halfedge_iterator; + typedef Edge_iterator edge_iterator; + typedef Face_iterator face_iterator; + typedef CGAL::Out_edge_iterator out_edge_iterator; - typedef boost::undirected_tag directed_category; + typedef boost::undirected_tag directed_category; typedef boost::disallow_parallel_edge_tag edge_parallel_category; struct traversal_category : public virtual boost::bidirectional_graph_tag, public virtual boost::vertex_list_graph_tag, - public virtual boost::edge_list_graph_tag - {}; + public virtual boost::edge_list_graph_tag { + }; typedef size_type vertices_size_type; typedef size_type halfedges_size_type; @@ -906,1153 +877,936 @@ class Surface_mesh typedef size_type faces_size_type; typedef size_type degree_size_type; - /// @endcond + /// @endcond public: - /// \name Construction, Destruction, Assignment - /// - /// Copy constructors as well as assignment do also copy simplices marked as removed. - ///@{ - - /// %Default constructor. - Surface_mesh(); - - /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. - Surface_mesh(const Surface_mesh& rhs) { *this = rhs; } - - /// Move constructor. - Surface_mesh(Surface_mesh&& sm) - : vprops_(std::move(sm.vprops_)) - , hprops_(std::move(sm.hprops_)) - , eprops_(std::move(sm.eprops_)) - , fprops_(std::move(sm.fprops_)) - , vconn_(std::move(sm.vconn_)) - , hconn_(std::move(sm.hconn_)) - , fconn_(std::move(sm.fconn_)) - , vremoved_(std::move(sm.vremoved_)) - , eremoved_(std::move(sm.eremoved_)) - , fremoved_(std::move(sm.fremoved_)) - , vpoint_(std::move(sm.vpoint_)) - , removed_vertices_(std::exchange(sm.removed_vertices_, 0)) - , removed_edges_(std::exchange(sm.removed_edges_, 0)) - , removed_faces_(std::exchange(sm.removed_faces_, 0)) - , vertices_freelist_(std::exchange(sm.vertices_freelist_,(std::numeric_limits::max)())) - , edges_freelist_(std::exchange(sm.edges_freelist_,(std::numeric_limits::max)())) - , faces_freelist_(std::exchange(sm.faces_freelist_,(std::numeric_limits::max)())) - , garbage_(std::exchange(sm.garbage_, false)) - , recycle_(std::exchange(sm.recycle_, true)) - , anonymous_property_(std::exchange(sm.anonymous_property_, 0)) - {} - - /// assigns `rhs` to `*this`. Performs a deep copy of all properties. - Surface_mesh& operator=(const Surface_mesh& rhs); - - /// move assignment - Surface_mesh& operator=(Surface_mesh&& sm) - { - vprops_ = std::move(sm.vprops_); - hprops_ = std::move(sm.hprops_); - eprops_ = std::move(sm.eprops_); - fprops_ = std::move(sm.fprops_); - vconn_ = std::move(sm.vconn_); - hconn_ = std::move(sm.hconn_); - fconn_ = std::move(sm.fconn_); - vremoved_ = std::move(sm.vremoved_); - eremoved_ = std::move(sm.eremoved_); - fremoved_ = std::move(sm.fremoved_); - vpoint_ = std::move(sm.vpoint_); - removed_vertices_ = std::exchange(sm.removed_vertices_, 0); - removed_edges_ = std::exchange(sm.removed_edges_, 0); - removed_faces_ = std::exchange(sm.removed_faces_, 0); - vertices_freelist_ = std::exchange(sm.vertices_freelist_, (std::numeric_limits::max)()); - edges_freelist_ = std::exchange(sm.edges_freelist_,(std::numeric_limits::max)()); - faces_freelist_ = std::exchange(sm.faces_freelist_,(std::numeric_limits::max)()); - garbage_ = std::exchange(sm.garbage_, false); - recycle_ = std::exchange(sm.recycle_, true); - anonymous_property_ = std::exchange(sm.anonymous_property_, 0); - return *this; - } + /// \name Construction, Destruction, Assignment + /// + /// Copy constructors as well as assignment do also copy simplices marked as removed. + ///@{ + + /// %Default constructor. + Surface_mesh() : + vconn_(vprops_.add_property("v:connectivity")), + hconn_(hprops_.add_property("h:connectivity")), + fconn_(fprops_.add_property("f:connectivity")), + vpoint_(vprops_.add_property("v:point")), + // todo: the following shouldn't be necessary + vremoved_(vprops_.add_property("v:removed", false)), + eremoved_(eprops_.add_property("e:removed", false)), + fremoved_(fprops_.add_property("f:removed", false)), + removed_vertices_(0), removed_edges_(0), removed_faces_(0), + garbage_(false), + anonymous_property_(0) {} + + /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. + Surface_mesh(const Surface_mesh& rhs) : + vprops_(rhs.vprops_), + hprops_(rhs.hprops_), + fprops_(rhs.fprops_), + eprops_(rhs.eprops_), + vconn_(vprops_.get_property("v:connectivity")), + vpoint_(vprops_.get_property("v:point")), + hconn_(hprops_.get_property("h:connectivity")), + fconn_(fprops_.get_property("f:connectivity")), + // todo: the following shouldn't be necessary + vremoved_(vprops_.get_property("v:removed")), + eremoved_(eprops_.get_property("e:removed")), + fremoved_(fprops_.get_property("f:removed")), + removed_vertices_(0), removed_edges_(0), removed_faces_(0), + garbage_(false), + anonymous_property_(0) {} + + /// Move constructor. + Surface_mesh(Surface_mesh&& sm) : + vprops_(std::move(sm.vprops_)), + hprops_(std::move(sm.hprops_)), + eprops_(std::move(sm.eprops_)), + fprops_(std::move(sm.fprops_)), + vconn_(vprops_.get_property("v:connectivity")), + vpoint_(vprops_.get_property("v:point")), + hconn_(hprops_.get_property("h:connectivity")), + fconn_(fprops_.get_property("f:connectivity")), + // todo: the following shouldn't be necessary + vremoved_(vprops_.get_property("v:removed")), + eremoved_(eprops_.get_property("e:removed")), + fremoved_(fprops_.get_property("f:removed")), + removed_vertices_(0), removed_edges_(0), removed_faces_(0), + garbage_(false), + anonymous_property_(0) {} + + /// assigns `rhs` to `*this`. Performs a deep copy of all properties. + Surface_mesh& operator=(const Surface_mesh& rhs); + + /// move assignment + Surface_mesh& operator=(Surface_mesh&& sm) { + vprops_ = std::move(sm.vprops_); + hprops_ = std::move(sm.hprops_); + eprops_ = std::move(sm.eprops_); + fprops_ = std::move(sm.fprops_); + return *this; + } - /// assigns `rhs` to `*this`. Does not copy custom properties. - Surface_mesh& assign(const Surface_mesh& rhs); + /// assigns `rhs` to `*this`. Does not copy custom properties. + //Surface_mesh& assign(const Surface_mesh& rhs); - ///@} + ///@} public: - /// \name Adding Vertices, Edges, and Faces - ///@{ - - /// adds a new vertex, and resizes vertex properties if necessary. - Vertex_index add_vertex() - { - size_type inf = (std::numeric_limits::max)(); - if(recycle_ && (vertices_freelist_ != inf)){ - size_type idx = vertices_freelist_; - vertices_freelist_ = (size_type)vconn_[Vertex_index(vertices_freelist_)].halfedge_; - --removed_vertices_; - vremoved_[Vertex_index(idx)] = false; - vprops_.reset(Vertex_index(idx)); - return Vertex_index(idx); - } else { - vprops_.push_back(); - return Vertex_index(num_vertices()-1); - } - } + /// \name Adding Vertices, Edges, and Faces + ///@{ - /// adds a new vertex, resizes vertex properties if necessary, - /// and sets the \em point property to `p`. - /// \note Several vertices may have the same point property. - Vertex_index add_vertex(const Point& p) - { - Vertex_index v = add_vertex(); - vpoint_[v] = p; - return v; - } + /// adds a new vertex, and resizes vertex properties if necessary. + Vertex_index add_vertex() { + return vprops_.emplace(); + } + /// adds a new vertex, resizes vertex properties if necessary, + /// and sets the \em point property to `p`. + /// \note Several vertices may have the same point property. + Vertex_index add_vertex(const Point& p) { + Vertex_index v = add_vertex(); + vpoint_[v] = p; + return v; + } public: - /// adds a new edge, and resizes edge and halfedge properties if necessary. - Halfedge_index add_edge() - { - Halfedge_index h0, h1; - size_type inf = (std::numeric_limits::max)(); - if(recycle_ && (edges_freelist_ != inf)){ - size_type idx = edges_freelist_; - edges_freelist_ = (size_type)hconn_[Halfedge_index(edges_freelist_)].next_halfedge_; - --removed_edges_; - eremoved_[Edge_index(Halfedge_index(idx))] = false; - hprops_.reset(Halfedge_index(idx)); - hprops_.reset(opposite(Halfedge_index(idx))); - eprops_.reset(Edge_index(Halfedge_index(idx))); - return Halfedge_index(idx); - } else { - eprops_.push_back(); - hprops_.push_back(); - hprops_.push_back(); - - return Halfedge_index(num_halfedges()-2); - } - } + /// adds a new edge, and resizes edge and halfedge properties if necessary. + Halfedge_index add_edge() { - /// adds two opposite halfedges, and resizes edge and halfedge properties if necessary. - /// Sets the targets of the halfedge to the given vertices, but does not modify the halfedge - /// associated to the vertices. - /// \note The function does not check whether there is already an edge between the vertices. - /// \returns the halfedge with `v1` as target + // Add properties for a new edge + eprops_.emplace(); - Halfedge_index add_edge(Vertex_index v0, Vertex_index v1) - { - CGAL_assertion(v0 != v1); - Halfedge_index h = add_edge(); + // Add properties for a pair of new half-edges + // The new half-edges are placed adjacently, and we return the index of the first + return hprops_.emplace_group(2); + } - set_target(h, v1); - set_target(opposite(h), v0); + /// adds two opposite halfedges, and resizes edge and halfedge properties if necessary. + /// Sets the targets of the halfedge to the given vertices, but does not modify the halfedge + /// associated to the vertices. + /// \note The function does not check whether there is already an edge between the vertices. + /// \returns the halfedge with `v1` as target - return h; - } + Halfedge_index add_edge(Vertex_index v0, Vertex_index v1) { + CGAL_assertion(v0 != v1); + Halfedge_index h = add_edge(); - /// adds a new face, and resizes face properties if necessary. - Face_index add_face() - { - size_type inf = (std::numeric_limits::max)(); - if(recycle_ && (faces_freelist_ != inf)){ - size_type idx = faces_freelist_; - faces_freelist_ = (size_type)fconn_[Face_index(faces_freelist_)].halfedge_; - --removed_faces_; - fprops_.reset(Face_index(idx)); - fremoved_[Face_index(idx)] = false; - return Face_index(idx); - } else { - fprops_.push_back(); - return Face_index(num_faces()-1); - } - } + set_target(h, v1); + set_target(opposite(h), v0); - /// if possible, adds a new face with vertices from a range with value type `Vertex_index`. - /// The function adds halfedges between successive vertices if they are not yet indicent to halfedges, - /// or updates the connectivity of halfedges already in place. - /// Resizes halfedge, edge, and face properties if necessary. - /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. - template - Face_index add_face(const Range& vertices); + return h; + } + /// adds a new face, and resizes face properties if necessary. + Face_index add_face() { + return fprops_.emplace(); + } - /// adds a new triangle connecting vertices `v0`, `v1`, `v2`. - /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. - Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2) - { - boost::array - v = {{v0, v1, v2}}; - return add_face(v); - } + /// if possible, adds a new face with vertices from a range with value type `Vertex_index`. + /// The function adds halfedges between successive vertices if they are not yet indicent to halfedges, + /// or updates the connectivity of halfedges already in place. + /// Resizes halfedge, edge, and face properties if necessary. + /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. + template + Face_index add_face(const Range& vertices); + + + /// adds a new triangle connecting vertices `v0`, `v1`, `v2`. + /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. + Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2) { + boost::array + v = {{v0, v1, v2}}; + return add_face(v); + } - /// adds a new quad connecting vertices `v0`, `v1`, `v2`, `v3`. - /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. - Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2, Vertex_index v3) - { - boost::array - v = {{v0, v1, v2, v3}}; - return add_face(v); - } + /// adds a new quad connecting vertices `v0`, `v1`, `v2`, `v3`. + /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. + Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2, Vertex_index v3) { + boost::array + v = {{v0, v1, v2, v3}}; + return add_face(v); + } - ///@} + ///@} - /// \name Low-Level Removal Functions - /// - /// Although the elements are only marked as removed - /// their connectivity and properties should not be used. - /// - /// \warning Functions in this group do not adjust any of - /// connected elements and usually leave the surface mesh in an - /// invalid state. - /// - /// - /// @{ + /// \name Low-Level Removal Functions + /// + /// Although the elements are only marked as removed + /// their connectivity and properties should not be used. + /// + /// \warning Functions in this group do not adjust any of + /// connected elements and usually leave the surface mesh in an + /// invalid state. + /// + /// + /// @{ - /// removes vertex `v` from the halfedge data structure without - /// adjusting anything. - void remove_vertex(Vertex_index v) - { - vremoved_[v] = true; ++removed_vertices_; garbage_ = true; - vconn_[v].halfedge_ = Halfedge_index(vertices_freelist_); - vertices_freelist_ = (size_type)v; - } + /// removes vertex `v` from the halfedge data structure without + /// adjusting anything. + void remove_vertex(Vertex_index v) { + // todo: confirm this behaves correctly + vprops_.erase(v); + } - /// removes the two halfedges corresponding to `e` from the halfedge data structure without - /// adjusting anything. - void remove_edge(Edge_index e) - { - eremoved_[e] = true; ++removed_edges_; garbage_ = true; - hconn_[Halfedge_index((size_type)e << 1)].next_halfedge_ = Halfedge_index(edges_freelist_ ); - edges_freelist_ = ((size_type)e << 1); - } + /// removes the two halfedges corresponding to `e` from the halfedge data structure without + /// adjusting anything. + void remove_edge(Edge_index e) { + // todo: confirm this behaves correctly + eprops_.erase(e); + } - /// removes face `f` from the halfedge data structure without - /// adjusting anything. + /// removes face `f` from the halfedge data structure without + /// adjusting anything. - void remove_face(Face_index f) - { - fremoved_[f] = true; ++removed_faces_; garbage_ = true; - fconn_[f].halfedge_ = Halfedge_index(faces_freelist_); - faces_freelist_ = (size_type)f; - } + void remove_face(Face_index f) { + // todo: confirm this behaves correctly + fprops_.erase(f); + } - ///@} + ///@} - /// \name Memory Management - /// - /// Functions to check the number of elements, the amount of space - /// allocated for elements, and to clear the structure. - ///@{ + /// \name Memory Management + /// + /// Functions to check the number of elements, the amount of space + /// allocated for elements, and to clear the structure. + ///@{ /// returns the number of vertices in the mesh. - size_type number_of_vertices() const - { - return num_vertices() - number_of_removed_vertices(); + size_type number_of_vertices() const { + return vprops_.size(); } /// returns the number of halfedges in the mesh. - size_type number_of_halfedges() const - { - return num_halfedges() - number_of_removed_halfedges(); + size_type number_of_halfedges() const { + return hprops_.size(); } /// returns the number of edges in the mesh. - size_type number_of_edges() const - { - return num_edges() - number_of_removed_edges(); + size_type number_of_edges() const { + return eprops_.size(); } /// returns the number of faces in the mesh. - size_type number_of_faces() const - { - return num_faces() - number_of_removed_faces(); + size_type number_of_faces() const { + return fprops_.size(); } - /// returns `true` iff the mesh is empty, i.e., has no vertices, halfedges and faces. - bool is_empty() const - { - return ( num_vertices() == number_of_removed_vertices() - && num_halfedges() == number_of_removed_halfedges() - && num_faces() == number_of_removed_faces()); + /// returns `true` iff the mesh is empty, i.e., has no vertices, halfedges and faces. + bool is_empty() const { + return (vprops_.size() == 0 + && hprops_.size() == 0 + && fprops_.size() == 0); } - /// removes all vertices, halfedge, edges and faces. Collects garbage but keeps all property maps. - void clear_without_removing_property_maps(); - - /// removes all vertices, halfedge, edges and faces. Collects garbage and removes all property maps added by a call to `add_property_map()` for all simplex types. - /// - /// After calling this method, the object is the same as a newly constructed object. The additional property maps are also removed and must thus be re-added if needed. - void clear(); + /// removes all vertices, halfedge, edges and faces. Collects garbage and removes all property maps added by a call to `add_property_map()` for all simplex types. + /// + /// After calling this method, the object is the same as a newly constructed object. The additional property maps are also removed and must thus be re-added if needed. + void clear() { + // todo + } - /// reserves space for vertices, halfedges, edges, faces, and their currently - /// associated properties. - void reserve(size_type nvertices, - size_type nedges, - size_type nfaces ) - { - vprops_.reserve(nvertices); - hprops_.reserve(2*nedges); - eprops_.reserve(nedges); - fprops_.reserve(nfaces); - } + /// reserves space for vertices, halfedges, edges, faces, and their currently + /// associated properties. + void reserve(size_type nvertices, + size_type nedges, + size_type nfaces) { + vprops_.reserve(nvertices); + hprops_.reserve(2 * nedges); + eprops_.reserve(nedges); + fprops_.reserve(nfaces); + } - void resize(size_type nvertices, - size_type nedges, - size_type nfaces ) - { - vprops_.resize(nvertices); - hprops_.resize(2*nedges); - eprops_.resize(nedges); - fprops_.resize(nfaces); - } +// void resize(size_type nvertices, +// size_type nedges, +// size_type nfaces) { +// vprops_.resize(nvertices); +// hprops_.resize(2 * nedges); +// eprops_.resize(nedges); +// fprops_.resize(nfaces); +// } /// copies the simplices from `other`, and copies values of /// properties that already exist under the same name in `*this`. /// In case `*this` has a property that does not exist in `other` /// the copied simplices get the default value of the property. - bool join(const Surface_mesh& other) - { - // increase capacity - const size_type nv = num_vertices(), nh = num_halfedges(), nf = num_faces(); - resize(num_vertices()+ other.num_vertices(), - num_edges()+ other.num_edges(), - num_faces()+ other.num_faces()); + bool join(const Surface_mesh& other) { + + // Record the original sizes of the property maps + const size_type nv = number_of_vertices(), nh = number_of_halfedges(), nf = number_of_faces(); // append properties in the free space created by resize - vprops_.transfer(other.vprops_); - hprops_.transfer(other.hprops_); - fprops_.transfer(other.fprops_); - eprops_.transfer(other.eprops_); + vprops_.append(other.vprops_); + hprops_.append(other.hprops_); + fprops_.append(other.fprops_); + eprops_.append(other.eprops_); + + // todo: the below code assumes no gaps were present in the properties! That might be okay for this situation. // translate halfedge index in vertex -> halfedge - for(size_type i = nv; i < nv+other.num_vertices(); i++){ + for (size_type i = nv; i < nv + other.number_of_vertices(); i++) { Vertex_index vi(i); - if(vconn_[vi].halfedge_ != null_halfedge()){ - vconn_[vi].halfedge_ = Halfedge_index(size_type(vconn_[vi].halfedge_)+nh); + if (vconn_[vi].halfedge_ != null_halfedge()) { + vconn_[vi].halfedge_ = Halfedge_index(size_type(vconn_[vi].halfedge_) + nh); } } // translate halfedge index in face -> halfedge - for(size_type i = nf; i < nf+other.num_faces(); i++){ + for (size_type i = nf; i < nf + other.number_of_faces(); i++) { Face_index fi(i); - if(fconn_[fi].halfedge_ != null_halfedge()){ - fconn_[fi].halfedge_ = Halfedge_index(size_type(fconn_[fi].halfedge_)+nh); + if (fconn_[fi].halfedge_ != null_halfedge()) { + fconn_[fi].halfedge_ = Halfedge_index(size_type(fconn_[fi].halfedge_) + nh); } } // translate indices in halfedge -> face, halfedge -> target, halfedge -> prev, and halfedge -> next - for(size_type i = nh; i < nh+other.num_halfedges(); i++){ + for (size_type i = nh; i < nh + other.number_of_halfedges(); i++) { Halfedge_index hi(i); - if(hconn_[hi].face_ != null_face()){ - hconn_[hi].face_ = Face_index(size_type(hconn_[hi].face_)+nf); - } - if( hconn_[hi].vertex_ != null_vertex()){ - hconn_[hi].vertex_ = Vertex_index(size_type(hconn_[hi].vertex_)+nv); - } - if(hconn_[hi].next_halfedge_ != null_halfedge()){ - hconn_[hi].next_halfedge_ = Halfedge_index(size_type(hconn_[hi].next_halfedge_)+nh); - } - if(hconn_[hi].prev_halfedge_ != null_halfedge()){ - hconn_[hi].prev_halfedge_ = Halfedge_index(size_type(hconn_[hi].prev_halfedge_)+nh); + if (hconn_[hi].face_ != null_face()) { + hconn_[hi].face_ = Face_index(size_type(hconn_[hi].face_) + nf); } - } - size_type inf_value = (std::numeric_limits::max)(); - - // merge vertex free list - if(other.vertices_freelist_ != inf_value){ - Vertex_index vi(nv+other.vertices_freelist_); - Halfedge_index inf((std::numeric_limits::max)()); - // correct the indices in the linked list of free vertices copied (due to vconn_ translation) - while(vconn_[vi].halfedge_ != inf){ - Vertex_index corrected_vi = Vertex_index(size_type(vconn_[vi].halfedge_)+nv-nh); - vconn_[vi].halfedge_ = Halfedge_index(corrected_vi); - vi = corrected_vi; + if (hconn_[hi].vertex_ != null_vertex()) { + hconn_[hi].vertex_ = Vertex_index(size_type(hconn_[hi].vertex_) + nv); } - // append the vertex free linked list of `this` to the copy of `other` - vconn_[vi].halfedge_ = Halfedge_index(vertices_freelist_); - // update the begin of the vertex free linked list - vertices_freelist_ = nv + other.vertices_freelist_; - } - // merge face free list - if(other.faces_freelist_ != inf_value){ - Face_index fi(nf+other.faces_freelist_); - Halfedge_index inf((std::numeric_limits::max)()); - // correct the indices in the linked list of free faces copied (due to fconn_ translation) - while(fconn_[fi].halfedge_ != inf){ - Face_index corrected_fi = Face_index(size_type(fconn_[fi].halfedge_)+nf-nh); - fconn_[fi].halfedge_ = Halfedge_index(corrected_fi); - fi = corrected_fi; + if (hconn_[hi].next_halfedge_ != null_halfedge()) { + hconn_[hi].next_halfedge_ = Halfedge_index(size_type(hconn_[hi].next_halfedge_) + nh); } - // append the face free linked list of `this` to the copy of `other` - fconn_[fi].halfedge_ = Halfedge_index(faces_freelist_); - // update the begin of the face free linked list - faces_freelist_ = nf + other.faces_freelist_; - } - // merge edge free list - if(other.edges_freelist_ != inf_value){ - Halfedge_index hi(nh+other.edges_freelist_); - Halfedge_index inf((std::numeric_limits::max)()); - while(hconn_[hi].next_halfedge_ != inf){ - hi = hconn_[hi].next_halfedge_; + if (hconn_[hi].prev_halfedge_ != null_halfedge()) { + hconn_[hi].prev_halfedge_ = Halfedge_index(size_type(hconn_[hi].prev_halfedge_) + nh); } - // append the halfedge free linked list of `this` to the copy of `other` - hconn_[hi].next_halfedge_ = Halfedge_index(edges_freelist_); - // update the begin of the halfedge free linked list - edges_freelist_ = nh + other.edges_freelist_; } - // update garbage infos - garbage_ = garbage_ || other.garbage_; - removed_vertices_ += other.removed_vertices_; - removed_edges_ += other.removed_edges_; - removed_faces_ += other.removed_faces_; return true; } - ///@} + ///@} - /// \name Garbage Collection - /// - /// While removing elements only marks them as removed - /// garbage collection really removes them. - /// The API in this section allows to check whether - /// an element is removed, to get the number of - /// removed elements, and to collect garbage. - /// The number of elements together with the number of removed elements is - /// an upperbound on the index, and is needed - /// by algorithms that temporarily store a - /// property in a vector of the appropriate size. - /// Note however that by garbage collecting elements get new indices. - /// In case you store indices in an auxiliary data structure - /// or in a property these indices are potentially no longer - /// referring to the right elements. - /// When adding elements, by default elements that are marked as removed - /// are recycled. - - ///@{ -#ifndef DOXYGEN_RUNNING - /// returns the number of used and removed vertices in the mesh. - size_type num_vertices() const { return (size_type) vprops_.size(); } + /// \name Garbage Collection + /// + /// While removing elements only marks them as removed + /// garbage collection really removes them. + /// The API in this section allows to check whether + /// an element is removed, to get the number of + /// removed elements, and to collect garbage. + /// The number of elements together with the number of removed elements is + /// an upperbound on the index, and is needed + /// by algorithms that temporarily store a + /// property in a vector of the appropriate size. + /// Note however that by garbage collecting elements get new indices. + /// In case you store indices in an auxiliary data structure + /// or in a property these indices are potentially no longer + /// referring to the right elements. + /// When adding elements, by default elements that are marked as removed + /// are recycled. + + ///@{ + + /// returns the number of vertices in the mesh which are marked removed. + size_type number_of_removed_vertices() const { return vprops_.capacity() - vprops_.size(); } + + /// returns the number of halfedges in the mesh which are marked removed. + size_type number_of_removed_halfedges() const { return hprops_.capacity() - hprops_.size(); } + + /// returns the number of edges in the mesh which are marked removed. + size_type number_of_removed_edges() const { return eprops_.capacity() - eprops_.size(); } + + /// returns the number offaces in the mesh which are marked removed. + size_type number_of_removed_faces() const { return fprops_.capacity() - fprops_.size(); } + + + /// returns whether vertex `v` is marked removed. + bool is_removed(Vertex_index v) const { + return vprops_.is_erased(v); + } - /// returns the number of used and removed halfedges in the mesh. - size_type num_halfedges() const { return (size_type) hprops_.size(); } + /// returns whether halfedge `h` is marked removed. + bool is_removed(Halfedge_index h) const { + return hprops_.is_erased(h); + } - /// returns the number of used and removed edges in the mesh. - size_type num_edges() const { return (size_type) eprops_.size(); } + /// returns whether edge `e` is marked removed. + bool is_removed(Edge_index e) const { + return eprops_.is_erased(e); + } - /// returns the number of used and removed faces in the mesh. - size_type num_faces() const { return (size_type) fprops_.size(); } + /// returns whether face `f` is marked removed. + bool is_removed(Face_index f) const { + return fprops_.is_erased(f); + } -#endif + /// checks if any vertices, halfedges, edges, or faces are marked as removed. + /// \sa collect_garbage + // todo: remove + bool has_garbage() const { return false; } - /// returns the number of vertices in the mesh which are marked removed. - size_type number_of_removed_vertices() const { return removed_vertices_; } + /// really removes vertices, halfedges, edges, and faces which are marked removed. + /// \sa `has_garbage()` + /// \attention By garbage collecting elements get new indices. + /// In case you store indices in an auxiliary data structure + /// or in a property these indices are potentially no longer + /// referring to the right elements. + void collect_garbage(); - /// returns the number of halfedges in the mesh which are marked removed. - size_type number_of_removed_halfedges() const { return 2*removed_edges_; } +// //undocumented convenience function that allows to get old-index->new-index information +// template +// void collect_garbage(Visitor& visitor); - /// returns the number of edges in the mesh which are marked removed. - size_type number_of_removed_edges() const { return removed_edges_; } + /// controls the recycling or not of simplices previously marked as removed + /// upon addition of new elements. + /// When set to `true` (default value), new elements are first picked in the garbage (if any) + /// while if set to `false` only new elements are created. + void set_recycle_garbage(bool b); - /// returns the number offaces in the mesh which are marked removed. - size_type number_of_removed_faces() const { return removed_faces_; } + /// Getter + bool does_recycle_garbage() const; + /// @cond CGAL_DOCUMENT_INTERNALS + /// removes unused memory from vectors. This shrinks the storage + /// of all properties to the minimal required size. + /// \attention Invalidates all existing references to properties. + +// void shrink_to_fit() { +// vprops_.shrink_to_fit(); +// hprops_.shrink_to_fit(); +// eprops_.shrink_to_fit(); +// fprops_.shrink_to_fit(); +// } + /// @endcond + ///@} - /// returns whether vertex `v` is marked removed. - /// \sa `collect_garbage()` - bool is_removed(Vertex_index v) const - { - return vremoved_[v]; - } - /// returns whether halfedge `h` is marked removed. - /// \sa `collect_garbage()` - bool is_removed(Halfedge_index h) const - { - return eremoved_[edge(h)]; - } - /// returns whether edge `e` is marked removed. - /// \sa `collect_garbage()` - bool is_removed(Edge_index e) const - { - return eremoved_[e]; - } - /// returns whether face `f` is marked removed. - /// \sa `collect_garbage()` - bool is_removed(Face_index f) const - { - return fremoved_[f]; - } + /// @cond CGAL_DOCUMENT_INTERNALS + /// + /// \name Simple Validity Checks + /// + /// Functions in this group check if the index is valid, that is between + /// `0` and the currently allocated maximum amount of the + /// elements. They do not check if an element is marked as removed. + ///@{ + + /// returns whether the index of vertex `v` is valid, that is within the current array bounds. + bool has_valid_index(Vertex_index v) const { + return ((size_type) v < number_of_vertices()); + } - /// checks if any vertices, halfedges, edges, or faces are marked as removed. - /// \sa collect_garbage - bool has_garbage() const { return garbage_; } - - /// really removes vertices, halfedges, edges, and faces which are marked removed. - /// \sa `has_garbage()` - /// \attention By garbage collecting elements get new indices. - /// In case you store indices in an auxiliary data structure - /// or in a property these indices are potentially no longer - /// referring to the right elements. - void collect_garbage(); - - //undocumented convenience function that allows to get old-index->new-index information - template - void collect_garbage(Visitor& visitor); - - /// controls the recycling or not of simplices previously marked as removed - /// upon addition of new elements. - /// When set to `true` (default value), new elements are first picked in the garbage (if any) - /// while if set to `false` only new elements are created. - void set_recycle_garbage(bool b); - - /// Getter - bool does_recycle_garbage() const; - - /// @cond CGAL_DOCUMENT_INTERNALS - /// removes unused memory from vectors. This shrinks the storage - /// of all properties to the minimal required size. - /// \attention Invalidates all existing references to properties. - - void shrink_to_fit() - { - vprops_.shrink_to_fit(); - hprops_.shrink_to_fit(); - eprops_.shrink_to_fit(); - fprops_.shrink_to_fit(); - } - /// @endcond + /// returns whether the index of halfedge `h` is valid, that is within the current array bounds. + bool has_valid_index(Halfedge_index h) const { + return ((size_type) h < number_of_halfedges()); + } - ///@} + /// returns whether the index of edge `e` is valid, that is within the current array bounds. + bool has_valid_index(Edge_index e) const { + return ((size_type) e < number_of_edges()); + } - /// @cond CGAL_DOCUMENT_INTERNALS - /// - /// \name Simple Validity Checks - /// - /// Functions in this group check if the index is valid, that is between - /// `0` and the currently allocated maximum amount of the - /// elements. They do not check if an element is marked as removed. - ///@{ + /// returns whether the index of face `f` is valid, that is within the current array bounds. + bool has_valid_index(Face_index f) const { + return ((size_type) f < number_of_faces()); + } - /// returns whether the index of vertex `v` is valid, that is within the current array bounds. - bool has_valid_index(Vertex_index v) const - { - return ((size_type)v < num_vertices()); - } + /// @} + /// @endcond - /// returns whether the index of halfedge `h` is valid, that is within the current array bounds. - bool has_valid_index(Halfedge_index h) const - { - return ((size_type)h < num_halfedges()); - } - /// returns whether the index of edge `e` is valid, that is within the current array bounds. - bool has_valid_index(Edge_index e) const - { - return ((size_type)e < num_edges()); - } - /// returns whether the index of face `f` is valid, that is within the current array bounds. - bool has_valid_index(Face_index f) const - { - return ((size_type)f < num_faces()); - } - - /// @} - /// @endcond + /// \name Validity Checks + /// + /// Functions in this group perform checks for structural + /// consistency of a complete surface mesh, or an individual element. + /// They are expensive and should only be used in debug configurations. + + ///@{ + + /// perform an expensive validity check on the data structure and + /// print found errors to `std::cerr` when `verbose == true`. + bool is_valid(bool verbose = false) const { + bool valid = true; + size_type vcount = 0, hcount = 0, fcount = 0; + for (Halfedge_iterator it = halfedges_begin(); it != halfedges_end(); ++it) { + ++hcount; + valid = valid && next(*it).is_valid(); + valid = valid && opposite(*it).is_valid(); + if (!valid) { + if (verbose) + std::cerr << "Integrity of halfedge " << *it << " corrupted." << std::endl; + break; + } - /// \name Validity Checks - /// - /// Functions in this group perform checks for structural - /// consistency of a complete surface mesh, or an individual element. - /// They are expensive and should only be used in debug configurations. + valid = valid && (opposite(*it) != *it); + valid = valid && (opposite(opposite(*it)) == *it); + if (!valid) { + if (verbose) + std::cerr << "Integrity of opposite halfedge of " << *it << " corrupted." << std::endl; + break; + } - ///@{ + valid = valid && (next(prev(*it)) == *it); + if (!valid) { + if (verbose) + std::cerr << "Integrity of previous halfedge of " << *it << " corrupted." << std::endl; + break; + } - /// perform an expensive validity check on the data structure and - /// print found errors to `std::cerr` when `verbose == true`. - bool is_valid(bool verbose = false) const - { - bool valid = true; - size_type vcount = 0, hcount = 0, fcount = 0; - for(Halfedge_iterator it = halfedges_begin(); it != halfedges_end(); ++it) { - ++hcount; - valid = valid && next(*it).is_valid(); - valid = valid && opposite(*it).is_valid(); - if(!valid) { - if (verbose) - std::cerr << "Integrity of halfedge " << *it << " corrupted." << std::endl; - break; - } - - valid = valid && (opposite(*it) != *it); - valid = valid && (opposite(opposite(*it)) == *it); - if(!valid) { - if (verbose) - std::cerr << "Integrity of opposite halfedge of " << *it << " corrupted." << std::endl; - break; - } - - valid = valid && (next(prev(*it)) == *it); - if(!valid) { - if (verbose) - std::cerr << "Integrity of previous halfedge of " << *it << " corrupted." << std::endl; - break; - } - - valid = valid && (prev(next(*it)) == *it); - if(!valid) { - if (verbose) - std::cerr << "Integrity of next halfedge of " << *it << " corrupted." << std::endl; - break; - } - - valid = valid && target(*it).is_valid(); - if(!valid) { - if (verbose) - std::cerr << "Integrity of vertex of halfedge " << *it << " corrupted." << std::endl; - break; - } - - valid = valid && (target(*it) == target(opposite(next(*it)))); - if(!valid) { - if (verbose) - std::cerr << "Halfedge vertex of next opposite is not the same for " << *it << "." << std::endl; - break; - } - } + valid = valid && (prev(next(*it)) == *it); + if (!valid) { + if (verbose) + std::cerr << "Integrity of next halfedge of " << *it << " corrupted." << std::endl; + break; + } - for(Vertex_iterator it = vertices_begin(); it != vertices_end(); ++it) { - ++vcount; - if(halfedge(*it).is_valid()) { - // not an isolated vertex - valid = valid && (target(halfedge(*it)) == *it); - if(!valid) { - if (verbose) - std::cerr << "Halfedge of " << *it << " is not an incoming halfedge." << std::endl; - break; - } - } - } - for(Face_iterator it = faces_begin(); it != faces_end(); ++it) { - ++fcount; - } + valid = valid && target(*it).is_valid(); + if (!valid) { + if (verbose) + std::cerr << "Integrity of vertex of halfedge " << *it << " corrupted." << std::endl; + break; + } - valid = valid && (vcount == number_of_vertices()); - if(!valid && verbose){ - std::cerr << "#vertices: iterated: " << vcount << " vs number_of_vertices(): " << number_of_vertices()<< std::endl; - } + valid = valid && (target(*it) == target(opposite(next(*it)))); + if (!valid) { + if (verbose) + std::cerr << "Halfedge vertex of next opposite is not the same for " << *it << "." << std::endl; + break; + } + } - valid = valid && (hcount == number_of_halfedges()); - if(!valid && verbose){ - std::cerr << "#halfedges: iterated: " << hcount << " vs number_of_halfedges(): " << number_of_halfedges()<< std::endl; + for (Vertex_iterator it = vertices_begin(); it != vertices_end(); ++it) { + ++vcount; + if (halfedge(*it).is_valid()) { + // not an isolated vertex + valid = valid && (target(halfedge(*it)) == *it); + if (!valid) { + if (verbose) + std::cerr << "Halfedge of " << *it << " is not an incoming halfedge." << std::endl; + break; } + } + } + for (Face_iterator it = faces_begin(); it != faces_end(); ++it) { + ++fcount; + } - valid = valid && (fcount == number_of_faces()); - if(!valid && verbose){ - std::cerr << "#faces: iterated: " << fcount << " vs number_of_faces(): " << number_of_faces()<< std::endl; - } + valid = valid && (vcount == number_of_vertices()); + if (!valid && verbose) { + std::cerr << "#vertices: iterated: " << vcount << " vs number_of_vertices(): " << number_of_vertices() + << std::endl; + } - size_type inf = (std::numeric_limits::max)(); - size_type vfl = vertices_freelist_; - size_type rv = 0; - while(vfl != inf){ - vfl = (size_type)vconn_[Vertex_index(vfl)].halfedge_; - rv++; - } - valid = valid && ( rv == removed_vertices_ ); + valid = valid && (hcount == number_of_halfedges()); + if (!valid && verbose) { + std::cerr << "#halfedges: iterated: " << hcount << " vs number_of_halfedges(): " << number_of_halfedges() + << std::endl; + } + valid = valid && (fcount == number_of_faces()); + if (!valid && verbose) { + std::cerr << "#faces: iterated: " << fcount << " vs number_of_faces(): " << number_of_faces() << std::endl; + } - size_type efl = edges_freelist_; - size_type re = 0; - while(efl != inf){ - efl = (size_type)hconn_[Halfedge_index(efl)].next_halfedge_; - re++; - } - valid = valid && ( re == removed_edges_ ); + return valid; + } - size_type ffl = faces_freelist_; - size_type rf = 0; - while(ffl != inf){ - ffl = (size_type)fconn_[Face_index(ffl)].halfedge_; - rf++; - } - valid = valid && ( rf == removed_faces_ ); + /// performs a validity check on a single vertex. + bool is_valid(Vertex_index v, + bool verbose = false) const { + Verbose_ostream verr(verbose); - return valid; + if (!has_valid_index(v)) { + verr << "Vertex has invalid index: " << (size_type) v << std::endl; + return false; } - /// performs a validity check on a single vertex. - bool is_valid(Vertex_index v, - bool verbose = false) const - { - Verbose_ostream verr(verbose); - - if(!has_valid_index(v)) - { - verr << "Vertex has invalid index: " << (size_type)v << std::endl; - return false; - } - - Halfedge_index h = vconn_[v].halfedge_; - if(h != null_halfedge() && (!has_valid_index(h) || is_removed(h))) { - verr << "Vertex connectivity halfedge error: Vertex " << (size_type)v - << " with " << (size_type)h << std::endl; - return false; - } - return true; + Halfedge_index h = vconn_[v].halfedge_; + if (h != null_halfedge() && (!has_valid_index(h) || is_removed(h))) { + verr << "Vertex connectivity halfedge error: Vertex " << (size_type) v + << " with " << (size_type) h << std::endl; + return false; } + return true; + } - /// performs a validity check on a single halfedge. - bool is_valid(Halfedge_index h, - bool verbose = false) const - { - Verbose_ostream verr(verbose); + /// performs a validity check on a single halfedge. + bool is_valid(Halfedge_index h, + bool verbose = false) const { + Verbose_ostream verr(verbose); - if(!has_valid_index(h)) - { - verr << "Halfedge has invalid index: " << (size_type)h << std::endl; - return false; - } + if (!has_valid_index(h)) { + verr << "Halfedge has invalid index: " << (size_type) h << std::endl; + return false; + } - Face_index f = hconn_[h].face_; - Vertex_index v = hconn_[h].vertex_; - Halfedge_index hn = hconn_[h].next_halfedge_; - Halfedge_index hp = hconn_[h].prev_halfedge_; - - bool valid = true; - // don't validate the face if this is a border halfedge - if(!is_border(h)) { - if(!has_valid_index(f) || is_removed(f)) { - verr << "Halfedge connectivity error: Face " - << (!has_valid_index(f) ? "invalid" : "removed") - << " in " << (size_type)h << std::endl; - valid = false; - } - } + Face_index f = hconn_[h].face_; + Vertex_index v = hconn_[h].vertex_; + Halfedge_index hn = hconn_[h].next_halfedge_; + Halfedge_index hp = hconn_[h].prev_halfedge_; - if(!has_valid_index(v) || is_removed(v)) { - verr << "Halfedge connectivity error: Vertex " - << (!has_valid_index(v) ? "invalid" : "removed") - << " in " << (size_type)h << std::endl; - valid = false; - } + bool valid = true; + // don't validate the face if this is a border halfedge + if (!is_border(h)) { + if (!has_valid_index(f) || is_removed(f)) { + verr << "Halfedge connectivity error: Face " + << (!has_valid_index(f) ? "invalid" : "removed") + << " in " << (size_type) h << std::endl; + valid = false; + } + } - if(!has_valid_index(hn) || is_removed(hn)) { - verr << "Halfedge connectivity error: hnext " - << (!has_valid_index(hn) ? "invalid" : "removed") - << " in " << (size_type)h << std::endl; - valid = false; - } - if(!has_valid_index(hp) || is_removed(hp)) { - verr << "Halfedge connectivity error: hprev " - << (!has_valid_index(hp) ? "invalid" : "removed") - << " in " << (size_type)h << std::endl; - valid = false; - } - return valid; + if (!has_valid_index(v) || is_removed(v)) { + verr << "Halfedge connectivity error: Vertex " + << (!has_valid_index(v) ? "invalid" : "removed") + << " in " << (size_type) h << std::endl; + valid = false; } + if (!has_valid_index(hn) || is_removed(hn)) { + verr << "Halfedge connectivity error: hnext " + << (!has_valid_index(hn) ? "invalid" : "removed") + << " in " << (size_type) h << std::endl; + valid = false; + } + if (!has_valid_index(hp) || is_removed(hp)) { + verr << "Halfedge connectivity error: hprev " + << (!has_valid_index(hp) ? "invalid" : "removed") + << " in " << (size_type) h << std::endl; + valid = false; + } + return valid; + } - /// performs a validity check on a single edge. - bool is_valid(Edge_index e, - bool verbose = false) const - { - Verbose_ostream verr(verbose); - if(!has_valid_index(e)) - { - verr << "Edge has invalid index: " << (size_type)e << std::endl; - return false; - } + /// performs a validity check on a single edge. + bool is_valid(Edge_index e, + bool verbose = false) const { + Verbose_ostream verr(verbose); - Halfedge_index h = halfedge(e); - return is_valid(h, verbose) && is_valid(opposite(h), verbose); + if (!has_valid_index(e)) { + verr << "Edge has invalid index: " << (size_type) e << std::endl; + return false; } + Halfedge_index h = halfedge(e); + return is_valid(h, verbose) && is_valid(opposite(h), verbose); + } - /// performs a validity check on a single face. - bool is_valid(Face_index f, - bool verbose = false) const - { - Verbose_ostream verr(verbose); - if(!has_valid_index(f)) - { - verr << "Face has invalid index: " << (size_type)f << std::endl; - return false; - } + /// performs a validity check on a single face. + bool is_valid(Face_index f, + bool verbose = false) const { + Verbose_ostream verr(verbose); - Halfedge_index h = fconn_[f].halfedge_; - if(!has_valid_index(h) || is_removed(h)) { - verr << "Face connectivity halfedge error: Face " << (size_type)f - << " with " << (size_type)h << std::endl; - return false; - } - return true; + if (!has_valid_index(f)) { + verr << "Face has invalid index: " << (size_type) f << std::endl; + return false; } - ///@} + Halfedge_index h = fconn_[f].halfedge_; + if (!has_valid_index(h) || is_removed(h)) { + verr << "Face connectivity halfedge error: Face " << (size_type) f + << " with " << (size_type) h << std::endl; + return false; + } + return true; + } + ///@} - /// \name Low-Level Connectivity - ///@{ - /// returns the vertex the halfedge `h` points to. - Vertex_index target(Halfedge_index h) const - { - return hconn_[h].vertex_; - } + /// \name Low-Level Connectivity + ///@{ - /// sets the vertex the halfedge `h` points to to `v`. - void set_target(Halfedge_index h, Vertex_index v) - { - hconn_[h].vertex_ = v; - } + /// returns the vertex the halfedge `h` points to. + Vertex_index target(Halfedge_index h) const { + return hconn_[h].vertex_; + } - /// returns the face incident to halfedge `h`. - Face_index face(Halfedge_index h) const - { - return hconn_[h].face_; - } + /// sets the vertex the halfedge `h` points to to `v`. + void set_target(Halfedge_index h, Vertex_index v) { + hconn_[h].vertex_ = v; + } - /// sets the incident face to halfedge `h` to `f`. - void set_face(Halfedge_index h, Face_index f) - { - hconn_[h].face_ = f; - } + /// returns the face incident to halfedge `h`. + Face_index face(Halfedge_index h) const { + return hconn_[h].face_; + } - /// returns the next halfedge within the incident face. - Halfedge_index next(Halfedge_index h) const - { - return hconn_[h].next_halfedge_; - } + /// sets the incident face to halfedge `h` to `f`. + void set_face(Halfedge_index h, Face_index f) { + hconn_[h].face_ = f; + } - /// returns the previous halfedge within the incident face. - Halfedge_index prev(Halfedge_index h) const - { - return hconn_[h].prev_halfedge_; - } + /// returns the next halfedge within the incident face. + Halfedge_index next(Halfedge_index h) const { + return hconn_[h].next_halfedge_; + } - /// @cond CGAL_DOCUMENT_INTERNALS - // sets the next halfedge of `h` within the face to `nh`. - void set_next_only(Halfedge_index h, Halfedge_index nh) - { - hconn_[h].next_halfedge_ = nh; - } + /// returns the previous halfedge within the incident face. + Halfedge_index prev(Halfedge_index h) const { + return hconn_[h].prev_halfedge_; + } - // sets previous halfedge of `h` to `nh`. - void set_prev_only(Halfedge_index h, Halfedge_index nh) - { - if(h != null_halfedge()){ - hconn_[h].prev_halfedge_ = nh; - } - } - /// @endcond + /// @cond CGAL_DOCUMENT_INTERNALS + // sets the next halfedge of `h` within the face to `nh`. + void set_next_only(Halfedge_index h, Halfedge_index nh) { + hconn_[h].next_halfedge_ = nh; + } - /// sets the next halfedge of `h` within the face to `nh` and - /// the previous halfedge of `nh` to `h`. - void set_next(Halfedge_index h, Halfedge_index nh) - { - set_next_only(h, nh); - set_prev_only(nh, h); + // sets previous halfedge of `h` to `nh`. + void set_prev_only(Halfedge_index h, Halfedge_index nh) { + if (h != null_halfedge()) { + hconn_[h].prev_halfedge_ = nh; } + } + /// @endcond - /// returns an incoming halfedge of vertex `v`. - /// If `v` is a border vertex this will be a border halfedge. - /// \invariant `target(halfedge(v)) == v` - Halfedge_index halfedge(Vertex_index v) const - { - return vconn_[v].halfedge_; - } + /// sets the next halfedge of `h` within the face to `nh` and + /// the previous halfedge of `nh` to `h`. + void set_next(Halfedge_index h, Halfedge_index nh) { + set_next_only(h, nh); + set_prev_only(nh, h); + } - /// sets the incoming halfedge of vertex `v` to `h`. - void set_halfedge(Vertex_index v, Halfedge_index h) - { - vconn_[v].halfedge_ = h; - } + /// returns an incoming halfedge of vertex `v`. + /// If `v` is a border vertex this will be a border halfedge. + /// \invariant `target(halfedge(v)) == v` + Halfedge_index halfedge(Vertex_index v) const { + return vconn_[v].halfedge_; + } + /// sets the incoming halfedge of vertex `v` to `h`. + void set_halfedge(Vertex_index v, Halfedge_index h) { + vconn_[v].halfedge_ = h; + } - /// returns a halfedge of face `f`. - Halfedge_index halfedge(Face_index f) const - { - return fconn_[f].halfedge_; - } - /// sets the halfedge of face `f` to `h`. - void set_halfedge(Face_index f, Halfedge_index h) - { - fconn_[f].halfedge_ = h; - } + /// returns a halfedge of face `f`. + Halfedge_index halfedge(Face_index f) const { + return fconn_[f].halfedge_; + } - /// returns the opposite halfedge of `h`. Note that there is no function `set_opposite()`. - Halfedge_index opposite(Halfedge_index h) const - { - return Halfedge_index(((size_type)h & 1) ? (size_type)h-1 : (size_type)h+1); - } + /// sets the halfedge of face `f` to `h`. + void set_halfedge(Face_index f, Halfedge_index h) { + fconn_[f].halfedge_ = h; + } - ///@} + /// returns the opposite halfedge of `h`. Note that there is no function `set_opposite()`. + Halfedge_index opposite(Halfedge_index h) const { + return Halfedge_index(((size_type) h & 1) ? (size_type) h - 1 : (size_type) h + 1); + } - /// \name Low-Level Connectivity Convenience Functions - ///@{ + ///@} - /// returns the vertex the halfedge `h` emanates from. - Vertex_index source(Halfedge_index h) const - { - return target(opposite(h)); - } + /// \name Low-Level Connectivity Convenience Functions + ///@{ - /// returns `opposite(next(h))`, that is the next halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the target vertex of `h`. - Halfedge_index next_around_target(Halfedge_index h) const - { - return opposite(next(h)); - } + /// returns the vertex the halfedge `h` emanates from. + Vertex_index source(Halfedge_index h) const { + return target(opposite(h)); + } - /// returns `prev(opposite(h))`, that is the previous halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the target vertex of `h`. - Halfedge_index prev_around_target(Halfedge_index h) const - { - return prev(opposite(h)); - } + /// returns `opposite(next(h))`, that is the next halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the target vertex of `h`. + Halfedge_index next_around_target(Halfedge_index h) const { + return opposite(next(h)); + } - /// returns `next(opposite(h))`, that is the next halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the source vertex of `h`. - Halfedge_index next_around_source(Halfedge_index h) const - { - return next(opposite(h)); - } + /// returns `prev(opposite(h))`, that is the previous halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the target vertex of `h`. + Halfedge_index prev_around_target(Halfedge_index h) const { + return prev(opposite(h)); + } - /// returns `opposite(prev(h))`, that is the previous halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the source vertex of `h`. - Halfedge_index prev_around_source(Halfedge_index h) const - { - return opposite(prev(h)); - } + /// returns `next(opposite(h))`, that is the next halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the source vertex of `h`. + Halfedge_index next_around_source(Halfedge_index h) const { + return next(opposite(h)); + } - /// returns the i'th vertex of edge `e`, for `i=0` or `1`. - Vertex_index vertex(Edge_index e, unsigned int i) const - { - CGAL_assertion(i<=1); - return target(halfedge(e, i)); - } + /// returns `opposite(prev(h))`, that is the previous halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the source vertex of `h`. + Halfedge_index prev_around_source(Halfedge_index h) const { + return opposite(prev(h)); + } - /// finds a halfedge between two vertices. Returns a default constructed - /// `Halfedge_index`, if `source` and `target` are not connected. - Halfedge_index halfedge(Vertex_index source, Vertex_index target) const; + /// returns the i'th vertex of edge `e`, for `i=0` or `1`. + Vertex_index vertex(Edge_index e, unsigned int i) const { + CGAL_assertion(i <= 1); + return target(halfedge(e, i)); + } - ///@} + /// finds a halfedge between two vertices. Returns a default constructed + /// `Halfedge_index`, if `source` and `target` are not connected. + Halfedge_index halfedge(Vertex_index source, Vertex_index target) const; + ///@} - /// \name Switching between Halfedges and Edges - ///@{ - /// returns the edge that contains halfedge `h` as one of its two halfedges. - Edge_index edge(Halfedge_index h) const - { - return Edge_index(h); - } + /// \name Switching between Halfedges and Edges + ///@{ - /// returns the halfedge corresponding to the edge `e`. - Halfedge_index halfedge(Edge_index e) const - { - return Halfedge_index(e.halfedge()); - } + /// returns the edge that contains halfedge `h` as one of its two halfedges. + Edge_index edge(Halfedge_index h) const { + return Edge_index(h); + } - /// returns the i'th halfedge of edge `e`, for `i=0` or `1`. - Halfedge_index halfedge(Edge_index e, unsigned int i) const - { - CGAL_assertion(i<=1); - return Halfedge_index(((size_type)e << 1) + i); - } + /// returns the halfedge corresponding to the edge `e`. + Halfedge_index halfedge(Edge_index e) const { + return Halfedge_index(e.halfedge()); + } - ///@} + /// returns the i'th halfedge of edge `e`, for `i=0` or `1`. + Halfedge_index halfedge(Edge_index e, unsigned int i) const { + CGAL_assertion(i <= 1); + return Halfedge_index(((size_type) e << 1) + i); + } + ///@} - /// \name Degree Functions - ///@{ - /// returns the number of incident halfedges of vertex `v`. - size_type degree(Vertex_index v) const; + /// \name Degree Functions + ///@{ - /// returns the number of incident halfedges of face `f`. - size_type degree(Face_index f) const; + /// returns the number of incident halfedges of vertex `v`. + size_type degree(Vertex_index v) const; - ///@} + /// returns the number of incident halfedges of face `f`. + size_type degree(Face_index f) const; + ///@} - /// \name Borders - /// - /// A halfedge, or edge is on the border of a surface mesh - /// if it is incident to a `null_face()`. A vertex is on a border - /// if it is isolated or incident to a border halfedge. While for a halfedge and - /// edge this is a constant time operation, for a vertex it means - /// to look at all incident halfedges. If algorithms operating on a - /// surface mesh maintain that the halfedge associated to a border vertex is - /// a border halfedge, this is a constant time operation too. - /// This section provides functions to check if an element is on a - /// border and to change the halfedge associated to a border vertex. - ///@{ - - /// returns whether `v` is a border vertex. - /// \cgalAdvancedBegin - /// With the default value for - /// `check_all_incident_halfedges` the function iteratates over the incident halfedges. - /// With `check_all_incident_halfedges == false` the function returns `true`, if the incident - /// halfedge associated to vertex `v` is a border halfedge, or if the vertex is isolated. - /// \cgalAdvancedEnd - /// \attention If the data contained in the `Surface_mesh` is not a 2-manifold, then - /// this operation is not guaranteed to return the right result. - bool is_border(Vertex_index v, bool check_all_incident_halfedges = true) const - { - Halfedge_index h(halfedge(v)); - if (h == null_halfedge()){ + + /// \name Borders + /// + /// A halfedge, or edge is on the border of a surface mesh + /// if it is incident to a `null_face()`. A vertex is on a border + /// if it is isolated or incident to a border halfedge. While for a halfedge and + /// edge this is a constant time operation, for a vertex it means + /// to look at all incident halfedges. If algorithms operating on a + /// surface mesh maintain that the halfedge associated to a border vertex is + /// a border halfedge, this is a constant time operation too. + /// This section provides functions to check if an element is on a + /// border and to change the halfedge associated to a border vertex. + ///@{ + + /// returns whether `v` is a border vertex. + /// \cgalAdvancedBegin + /// With the default value for + /// `check_all_incident_halfedges` the function iteratates over the incident halfedges. + /// With `check_all_incident_halfedges == false` the function returns `true`, if the incident + /// halfedge associated to vertex `v` is a border halfedge, or if the vertex is isolated. + /// \cgalAdvancedEnd + /// \attention If the data contained in the `Surface_mesh` is not a 2-manifold, then + /// this operation is not guaranteed to return the right result. + bool is_border(Vertex_index v, bool check_all_incident_halfedges = true) const { + Halfedge_index h(halfedge(v)); + if (h == null_halfedge()) { + return true; + } + if (check_all_incident_halfedges) { + Halfedge_around_target_circulator hatc(h, *this), done(hatc); + do { + if (is_border(*hatc)) { return true; } - if(check_all_incident_halfedges){ - Halfedge_around_target_circulator hatc(h,*this), done(hatc); - do { - if(is_border(*hatc)){ - return true; - } - }while(++hatc != done); - return false; - } - return is_border(h); + } while (++hatc != done); + return false; } + return is_border(h); + } - /// returns whether `h` is a border halfege, that is if its incident face is `sm.null_face()`. - bool is_border(Halfedge_index h) const - { - return !face(h).is_valid(); - } + /// returns whether `h` is a border halfege, that is if its incident face is `sm.null_face()`. + bool is_border(Halfedge_index h) const { + return !face(h).is_valid(); + } - /// returns whether `e` is a border edge, i.e., if any - /// of its two halfedges is a border halfedge. - bool is_border(Edge_index e) const - { - return is_border(e.halfedge()) || is_border(opposite(e.halfedge())); - } + /// returns whether `e` is a border edge, i.e., if any + /// of its two halfedges is a border halfedge. + bool is_border(Edge_index e) const { + return is_border(e.halfedge()) || is_border(opposite(e.halfedge())); + } /// iterates over the incident halfedges and sets the incident halfedge /// associated to vertex `v` to a border halfedge and returns `true` if it exists. - bool set_vertex_halfedge_to_border_halfedge(Vertex_index v) - { - if(halfedge(v) == null_halfedge()){ + bool set_vertex_halfedge_to_border_halfedge(Vertex_index v) { + if (halfedge(v) == null_halfedge()) { return false; } - Halfedge_around_target_circulator hatc(halfedge(v),*this), done(hatc); + Halfedge_around_target_circulator hatc(halfedge(v), *this), done(hatc); do { - if(is_border(*hatc)){ - set_halfedge(v,*hatc); + if (is_border(*hatc)) { + set_halfedge(v, *hatc); return true; } - }while(++hatc != done); + } while (++hatc != done); return false; } /// applies `set_vertex_halfedge_to_border_halfedge(Vertex_index)` on all vertices /// around the face associated to `h`. - void set_vertex_halfedge_to_border_halfedge(Halfedge_index h) - { - if(is_border(h)){ - Halfedge_around_face_circulator hafc(h,*this),done(hafc); + void set_vertex_halfedge_to_border_halfedge(Halfedge_index h) { + if (is_border(h)) { + Halfedge_around_face_circulator hafc(h, *this), done(hafc); do { - set_halfedge(target(*hafc),*hafc); - }while(++hafc != done); + set_halfedge(target(*hafc), *hafc); + } while (++hafc != done); } else { - Vertex_around_face_circulator vafc(h,*this),done(vafc); + Vertex_around_face_circulator vafc(h, *this), done(vafc); do { set_vertex_halfedge_to_border_halfedge(*vafc); - }while(++vafc != done); + } while (++vafc != done); } } /// applies `set_vertex_halfedge_to_border_halfedge(Vertex_index)` on all vertices /// of the surface mesh. - void set_vertex_halfedge_to_border_halfedge() - { - for(Halfedge_index h : halfedges()){ - if(is_border(h)){ - set_halfedge(target(h),h); - } + void set_vertex_halfedge_to_border_halfedge() { + for (Halfedge_index h: halfedges()) { + if (is_border(h)) { + set_halfedge(target(h), h); + } } } - /// returns whether `v` is isolated, i.e., incident to `Surface_mesh::null_halfedge()`. - bool is_isolated(Vertex_index v) const - { - return !halfedge(v).is_valid(); - } + /// returns whether `v` is isolated, i.e., incident to `Surface_mesh::null_halfedge()`. + bool is_isolated(Vertex_index v) const { + return !halfedge(v).is_valid(); + } - ///@} + ///@} -private: //--------------------------------------------------- property handling +public: //--------------------------------------------------- property handling - // Property_selector maps an index type to a property_container, the - // dummy is necessary to make it a partial specialization (full - // specializations are only allowed at namespace scope). - template - struct Property_selector {}; + template + Property_container& get_property_container() { + if constexpr (std::is_same_v) + return vprops_; + if constexpr (std::is_same_v) + return hprops_; + if constexpr (std::is_same_v) + return eprops_; + if constexpr (std::is_same_v) + return fprops_; + } - template - struct Property_selector::Vertex_index, dummy> { - CGAL::Surface_mesh

* m_; - Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} - Properties::Property_container::Vertex_index>& - operator()() { return m_->vprops_; } - void resize_property_array() { m_->vprops_.resize_property_array(3); } - }; - template - struct Property_selector::Halfedge_index, dummy> { - CGAL::Surface_mesh

* m_; - Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} - Properties::Property_container::Halfedge_index>& - operator()() { return m_->hprops_; } - void resize_property_array() { m_->hprops_.resize_property_array(1); } - }; - template - struct Property_selector::Edge_index, dummy> { - CGAL::Surface_mesh

* m_; - Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} - Properties::Property_container::Edge_index>& - operator()() { return m_->eprops_; } - void resize_property_array() { m_->eprops_.resize_property_array(1); } - }; - template - struct Property_selector::Face_index, dummy> { - CGAL::Surface_mesh

* m_; - Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} - Properties::Property_container::Face_index>& - operator()() { return m_->fprops_; } - void resize_property_array() { m_->fprops_.resize_property_array(2); } - }; - public: +public: - /*! \name Property Handling + /*! \name Property Handling - A `Properties::Property_map` allows to associate properties of type `T` to a vertex, halfdge, edge, or face index type I. - Properties can be added, and looked up with a string, and they can be removed at runtime. - The \em point property of type `P` is associated to the string "v:point". + A `Properties::Property_map` allows to associate properties of type `T` to a vertex, halfdge, edge, or face index type I. + Properties can be added, and looked up with a string, and they can be removed at runtime. + The \em point property of type `P` is associated to the string "v:point". - */ - ///@{ + */ + ///@{ /// Model of `LvaluePropertyMap` with `I` as key type and `T` as value type, where `I` /// is either a vertex, halfedge, edge, or face index type. @@ -2065,190 +1819,189 @@ class Surface_mesh #endif - /// adds a property map named `name` with value type `T` and default `t` - /// for index type `I`. Returns the property map together with a Boolean - /// that is `true` if a new map was created. In case it already exists - /// the existing map together with `false` is returned. - + // todo: I can't see a good reason for these two functions to exist separately, but do almost the same thing - template - std::pair, bool> - add_property_map(std::string name=std::string(), const T t=T()) { - if(name.empty()){ - std::ostringstream oss; - oss << "anonymous-property-" << anonymous_property_++; - name = std::string(oss.str()); - } - return Property_selector(this)().template add(name, t); - } + /// adds a property map named `name` with value type `T` and default `t` + /// for index type `I`. Returns the property map together with a Boolean + /// that is `true` if a new map was created. In case it already exists + /// the existing map together with `false` is returned. + template + std::pair, bool> + add_property_map(std::string name = std::string(), const T t = T()) { + if (name.empty()) { + // todo: maybe this should be done by the property container itself? + std::ostringstream oss; + oss << "anonymous-property-" << anonymous_property_++; + name = std::string(oss.str()); + } + // todo: double check this is working + auto [array, created] = + const_cast*>(this)->get_property_container().template get_or_add_property(name, t); + return {{array.get()}, created}; + } - /// returns a property map named `name` with key type `I` and value type `T`, - /// and a Boolean that is `true` if the property exists. - /// In case it does not exist the Boolean is `false` and the behavior of - /// the property map is undefined. - template - std::pair,bool> property_map(const std::string& name) const - { - return Property_selector(const_cast(this))().template get(name); - } + /// returns a property map named `name` with key type `I` and value type `T`, + /// and a Boolean that is `true` if the property was created. + template + std::pair, bool> + property_map(const std::string& name) const { + auto [array, created] = + const_cast*>(this)->get_property_container().template get_or_add_property(name); + return {{array.get()}, created}; + } - /// removes property map `p`. The memory allocated for that property map is - /// freed. - template - void remove_property_map(Property_map& p) - { - (Property_selector(this)()).template remove(p); - } + /// removes property map `p`. The memory allocated for that property map is + /// freed. + template + void remove_property_map(Property_map p) { + // todo: this is never used, but it should probably still work + } - /// removes all property maps for index type `I` added by a call to `add_property_map()`. - /// The memory allocated for those property maps is freed. - template - void remove_property_maps() - { - Property_selector(this).resize_property_array(); - } +// /// removes all property maps for index type `I` added by a call to `add_property_map()`. +// /// The memory allocated for those property maps is freed. +// template +// void remove_property_maps() { +// Property_selector(this).resize_property_array(); +// } - /// removes all property maps for all index types added by a call to `add_property_map()`. - /// The memory allocated for those property maps is freed. - void remove_all_property_maps() - { - remove_property_maps(); - remove_property_maps(); - remove_property_maps(); - remove_property_maps(); - } +// /// removes all property maps for all index types added by a call to `add_property_map()`. +// /// The memory allocated for those property maps is freed. +// void remove_all_property_maps() { +// remove_property_maps(); +// remove_property_maps(); +// remove_property_maps(); +// remove_property_maps(); +// } - /// @cond CGAL_DOCUMENT_INTERNALS - /// returns the std::type_info of the value type of the - /// property identified by `name`. `typeid(void)` if `name` - /// does not identify any property. - /// - /// @tparam I The key type of the property. + /// @cond CGAL_DOCUMENT_INTERNALS + /// returns the std::type_info of the value type of the + /// property identified by `name`. `typeid(void)` if `name` + /// does not identify any property. + /// + /// @tparam I The key type of the property. - template - const std::type_info& property_type(const std::string& name) - { - return Property_selector(this)().get_type(name); - } - /// @endcond + template + const std::type_info& property_type(const std::string& name) { + return Property_selector(this)().get_type(name); + } + /// @endcond - /// returns a vector with all strings that describe properties with the key type `I`. - /// @tparam I The key type of the properties. - template - std::vector properties() const - { - return Property_selector(const_cast(this))().properties(); - } + /// returns a vector with all strings that describe properties with the key type `I`. + /// @tparam I The key type of the properties. + template + std::vector properties() const { + return Property_selector(const_cast(this))().properties(); + } - /// returns the property for the string "v:point". - Property_map - points() const { return vpoint_; } + /// returns the property for the string "v:point". + // todo: shouldn't this return a const pmap? + // In the original version, there was no difference between const & non-const maps + Property_array& + points() const { return vpoint_; } - Property_map& - points() { return vpoint_; } + Property_array& + points() { return vpoint_; } - /// returns the point associated to vertex `v`. - const Point& - point(Vertex_index v) const { return vpoint_[v]; } + /// returns the point associated to vertex `v`. + const Point& + point(Vertex_index v) const { return vpoint_[v]; } - /// returns the point associated to vertex `v`. - Point& - point(Vertex_index v) { return vpoint_[v]; } + /// returns the point associated to vertex `v`. + Point& + point(Vertex_index v) { return vpoint_[v]; } - /// @cond CGAL_DOCUMENT_INTERNALS - /// prints property statistics to the stream `out`. The output is human-readable but - /// not machine-friendly. - /// - void property_stats(std::ostream& out = std::cout) const; - /// @endcond - ///@} + /// @cond CGAL_DOCUMENT_INTERNALS + /// prints property statistics to the stream `out`. The output is human-readable but + /// not machine-friendly. + /// + void property_stats(std::ostream& out = std::cout) const; + /// @endcond + ///@} - /// \name Null Elements - ///@{ + /// \name Null Elements + ///@{ /// returns `Vertex_index(std::numeric_limits::%max())`. - static Vertex_index null_vertex() - { + static Vertex_index null_vertex() { return vertex_index((std::numeric_limits::max)()); } /// returns `Edge_index(std::numeric_limits::%max())`. - static Edge_index null_edge() - { + static Edge_index null_edge() { return edge_index((std::numeric_limits::max)()); } + /// returns `Halfedge_index(std::numeric_limits::%max())`. - static Halfedge_index null_halfedge() - { + static Halfedge_index null_halfedge() { return halfedge_index((std::numeric_limits::max)()); } + /// returns `Face_index(std::numeric_limits::%max())`. - static Face_index null_face() - { + static Face_index null_face() { return face_index((std::numeric_limits::max)()); } /// @} #if defined(CGAL_SURFACE_MESH_TEST_SUITE) - Vertex_index vertex_freelist() const - { - return Vertex_index(vertices_freelist_); + + std::vector vertex_freelist() const { + return vprops_.inactive_list(); } - Face_index face_freelist() const - { - return Face_index(faces_freelist_); + std::vector face_freelist() const { + return fprops_.inactive_list(); } - Edge_index edge_freelist() const - { - return Edge_index(edges_freelist_>>1); + std::vector edge_freelist() const { + return eprops_.inactive_list(); } + #endif private: //--------------------------------------------------- helper functions - /// make sure that the incoming halfedge of vertex v is a border halfedge - /// if `v` is a border vertex. - void adjust_incoming_halfedge(Vertex_index v); + /// make sure that the incoming halfedge of vertex v is a border halfedge + /// if `v` is a border vertex. + void adjust_incoming_halfedge(Vertex_index v); private: //------------------------------------------------------- private data - Properties::Property_container vprops_; - Properties::Property_container hprops_; - Properties::Property_container eprops_; - Properties::Property_container fprops_; - Property_map vconn_; - Property_map hconn_; - Property_map fconn_; + Property_container vprops_; + Property_container hprops_; + Property_container eprops_; + Property_container fprops_; + + Property_array& vconn_; + Property_array& hconn_; + Property_array& fconn_; - Property_map vremoved_; - Property_map eremoved_; - Property_map fremoved_; + Property_array& vpoint_; - Property_map vpoint_; + Property_array& vremoved_; + Property_array& eremoved_; + Property_array& fremoved_; - size_type removed_vertices_; - size_type removed_edges_; - size_type removed_faces_; + size_type removed_vertices_; + size_type removed_edges_; + size_type removed_faces_; - size_type vertices_freelist_; - size_type edges_freelist_; - size_type faces_freelist_; - bool garbage_; - bool recycle_; + size_type vertices_freelist_; + size_type edges_freelist_; + size_type faces_freelist_; + bool garbage_; + bool recycle_; - size_type anonymous_property_; + size_type anonymous_property_; }; - /*! \addtogroup PkgSurface_mesh - * - * @{ - */ +/*! \addtogroup PkgSurface_mesh + * + * @{ + */ /// \relates Surface_mesh /// Inserts `other` into `sm`. @@ -2258,181 +2011,65 @@ class Surface_mesh /// that is, property maps which are only in `other` are ignored. /// Also copies elements which are marked as removed, and concatenates the freelists of `sm` and `other`. - template - Surface_mesh

& operator+=(Surface_mesh

& sm, const Surface_mesh

& other) - { - sm.join(other); - return sm; - } - - /// \relates Surface_mesh - /// - /// This operator calls `write_OFF(std::ostream& os, const CGAL::Surface_mesh& sm)`. - template - std::ostream& operator<<(std::ostream& os, const Surface_mesh

& sm) - { - IO::write_OFF(os, sm); - return os; - } - - /// \relates Surface_mesh - /// Extracts the surface mesh from an input stream in OFF - /// and appends it to the surface mesh `sm`. - /// - /// This operator calls `read_OFF(std::istream& is, CGAL::Surface_mesh& sm)`. - template - std::istream& operator>>(std::istream& is, Surface_mesh

& sm) - { - IO::read_OFF(is, sm); - return is; - } - - /*! @} */ - template -Surface_mesh

:: -Surface_mesh() -{ - // allocate standard properties - // same list is used in operator=() and assign() - vconn_ = add_property_map("v:connectivity").first; - hconn_ = add_property_map("h:connectivity").first; - fconn_ = add_property_map("f:connectivity").first; - vpoint_ = add_property_map("v:point").first; - vremoved_ = add_property_map("v:removed", false).first; - eremoved_ = add_property_map("e:removed", false).first; - fremoved_ = add_property_map("f:removed", false).first; - - removed_vertices_ = removed_edges_ = removed_faces_ = 0; - vertices_freelist_ = edges_freelist_ = faces_freelist_ = (std::numeric_limits::max)(); - garbage_ = false; - recycle_ = true; - anonymous_property_ = 0; +Surface_mesh

& operator+=(Surface_mesh

& sm, const Surface_mesh

& other) { + sm.join(other); + return sm; } - -//----------------------------------------------------------------------------- +/// \relates Surface_mesh +/// +/// This operator calls `write_OFF(std::ostream& os, const CGAL::Surface_mesh& sm)`. template -Surface_mesh

& -Surface_mesh

:: -operator=(const Surface_mesh

& rhs) -{ - if (this != &rhs) - { - // deep copy of property containers - vprops_ = rhs.vprops_; - hprops_ = rhs.hprops_; - eprops_ = rhs.eprops_; - fprops_ = rhs.fprops_; - - // property handles contain pointers, have to be reassigned - vconn_ = property_map("v:connectivity").first; - hconn_ = property_map("h:connectivity").first; - fconn_ = property_map("f:connectivity").first; - vremoved_ = property_map("v:removed").first; - eremoved_ = property_map("e:removed").first; - fremoved_ = property_map("f:removed").first; - vpoint_ = property_map("v:point").first; - - // how many elements are removed? - removed_vertices_ = rhs.removed_vertices_; - removed_edges_ = rhs.removed_edges_; - removed_faces_ = rhs.removed_faces_; - vertices_freelist_ = rhs.vertices_freelist_; - edges_freelist_ = rhs.edges_freelist_; - faces_freelist_ = rhs.faces_freelist_; - garbage_ = rhs.garbage_; - recycle_ = rhs.recycle_; - anonymous_property_ = rhs.anonymous_property_; - } - - return *this; +std::ostream& operator<<(std::ostream& os, const Surface_mesh

& sm) { + IO::write_OFF(os, sm); + return os; } - -//----------------------------------------------------------------------------- +/// \relates Surface_mesh +/// Extracts the surface mesh from an input stream in OFF +/// and appends it to the surface mesh `sm`. +/// +/// This operator calls `read_OFF(std::istream& is, CGAL::Surface_mesh& sm)`. template -Surface_mesh

& -Surface_mesh

:: -assign(const Surface_mesh

& rhs) -{ - if (this != &rhs) - { - // clear properties - vprops_.clear(); - hprops_.clear(); - eprops_.clear(); - fprops_.clear(); - - // allocate standard properties - vconn_ = add_property_map("v:connectivity").first; - hconn_ = add_property_map("h:connectivity").first; - fconn_ = add_property_map("f:connectivity").first; - vpoint_ = add_property_map("v:point").first; - vremoved_ = add_property_map("v:removed", false).first; - eremoved_ = add_property_map("e:removed", false).first; - fremoved_ = add_property_map("f:removed", false).first; - - // copy properties from other mesh - vconn_.array() = rhs.vconn_.array(); - hconn_.array() = rhs.hconn_.array(); - fconn_.array() = rhs.fconn_.array(); - vpoint_.array() = rhs.vpoint_.array(); - vremoved_.array() = rhs.vremoved_.array(); - eremoved_.array() = rhs.eremoved_.array(); - fremoved_.array() = rhs.fremoved_.array(); - - // resize (needed by property containers) - vprops_.resize(rhs.num_vertices()); - hprops_.resize(rhs.num_halfedges()); - eprops_.resize(rhs.num_edges()); - fprops_.resize(rhs.num_faces()); - - // how many elements are removed? - removed_vertices_ = rhs.removed_vertices_; - removed_edges_ = rhs.removed_edges_; - removed_faces_ = rhs.removed_faces_; - vertices_freelist_ = rhs.vertices_freelist_; - edges_freelist_ = rhs.edges_freelist_; - faces_freelist_ = rhs.faces_freelist_; - garbage_ = rhs.garbage_; - recycle_ = rhs.recycle_; - anonymous_property_ = rhs.anonymous_property_; - } - - return *this; +std::istream& operator>>(std::istream& is, Surface_mesh

& sm) { + IO::read_OFF(is, sm); + return is; } +/*! @} */ + + //----------------------------------------------------------------------------- template -void +Surface_mesh

& Surface_mesh

:: -clear() -{ - clear_without_removing_property_maps(); - remove_all_property_maps(); -} +operator=(const Surface_mesh

& rhs) { + if (this != &rhs) { + + // Deep copy of properties + vprops_ = rhs.vprops_; + hprops_ = rhs.hprops_; + eprops_ = rhs.eprops_; + fprops_ = rhs.fprops_; + + // Property array refs don't need to be reassigned, + // because the deep copy updated the values they point to + + + // how many elements are removed? + removed_vertices_ = rhs.removed_vertices_; + removed_edges_ = rhs.removed_edges_; + removed_faces_ = rhs.removed_faces_; + vertices_freelist_ = rhs.vertices_freelist_; + edges_freelist_ = rhs.edges_freelist_; + faces_freelist_ = rhs.faces_freelist_; + garbage_ = rhs.garbage_; + recycle_ = rhs.recycle_; + anonymous_property_ = rhs.anonymous_property_; + } -template -void -Surface_mesh

:: -clear_without_removing_property_maps() -{ - vprops_.resize(0); - hprops_.resize(0); - eprops_.resize(0); - fprops_.resize(0); - - vprops_.shrink_to_fit(); - hprops_.shrink_to_fit(); - eprops_.shrink_to_fit(); - fprops_.shrink_to_fit(); - - removed_vertices_ = removed_edges_ = removed_faces_ = 0; - vertices_freelist_ = edges_freelist_ = faces_freelist_ = (std::numeric_limits::max)(); - garbage_ = false; - recycle_ = true; - anonymous_property_ = 0; + return *this; } //----------------------------------------------------------------------------- @@ -2440,29 +2077,28 @@ clear_without_removing_property_maps() template void Surface_mesh

:: -property_stats(std::ostream& out) const -{ - std::vector props; - - out << "vertex properties:\n"; - props = properties(); - for (unsigned int i=0; i(); - for (unsigned int i=0; i(); - for (unsigned int i=0; i(); - for (unsigned int i=0; i props; + + out << "vertex properties:\n"; + props = properties(); + for (unsigned int i = 0; i < props.size(); ++i) + out << "\t" << props[i] << std::endl; + + out << "halfedge properties:\n"; + props = properties(); + for (unsigned int i = 0; i < props.size(); ++i) + out << "\t" << props[i] << std::endl; + + out << "edge properties:\n"; + props = properties(); + for (unsigned int i = 0; i < props.size(); ++i) + out << "\t" << props[i] << std::endl; + + out << "face properties:\n"; + props = properties(); + for (unsigned int i = 0; i < props.size(); ++i) + out << "\t" << props[i] << std::endl; } /// @endcond @@ -2470,25 +2106,21 @@ property_stats(std::ostream& out) const template typename Surface_mesh

::Halfedge_index Surface_mesh

:: -halfedge(Vertex_index source, Vertex_index target) const -{ - CGAL_assertion(has_valid_index(source) && has_valid_index(target)); +halfedge(Vertex_index source, Vertex_index target) const { + CGAL_assertion(has_valid_index(source) && has_valid_index(target)); - Halfedge_index h = halfedge(target); - const Halfedge_index hh = h; + Halfedge_index h = halfedge(target); + const Halfedge_index hh = h; - if (h.is_valid()) - { - do - { - if (this->source(h) == source) - return h; - h = next_around_target(h); - } - while (h != hh); - } + if (h.is_valid()) { + do { + if (this->source(h) == source) + return h; + h = next_around_target(h); + } while (h != hh); + } - return Halfedge_index(); + return Halfedge_index(); } @@ -2496,67 +2128,59 @@ halfedge(Vertex_index source, Vertex_index target) const template void Surface_mesh

:: -adjust_incoming_halfedge(Vertex_index v) -{ - Halfedge_index h = halfedge(v); - Halfedge_index hh = h; - - if (h.is_valid()) - { - if (target(h) != v) - { - // wrong target, flip - h = opposite(h); - hh = h; - set_halfedge(v, h); - } +adjust_incoming_halfedge(Vertex_index v) { + Halfedge_index h = halfedge(v); + Halfedge_index hh = h; - do - { - if (is_border(h)) - { - set_halfedge(v, h); - return; - } - h = next_around_target(h); - } - while (h != hh); + if (h.is_valid()) { + if (target(h) != v) { + // wrong target, flip + h = opposite(h); + hh = h; + set_halfedge(v, h); } + + do { + if (is_border(h)) { + set_halfedge(v, h); + return; + } + h = next_around_target(h); + } while (h != hh); + } } //----------------------------------------------------------------------------- - /// @cond CGAL_DOCUMENT_INTERNALS +/// @cond CGAL_DOCUMENT_INTERNALS template template typename Surface_mesh

::Face_index -Surface_mesh

::add_face(const Range& r) -{ +Surface_mesh

::add_face(const Range& r) { return CGAL::Euler::add_face(r, *this); } - /// @endcond +/// @endcond //----------------------------------------------------------------------------- template typename Surface_mesh

::size_type Surface_mesh

:: -degree(Vertex_index v) const -{ - Halfedge_index h = halfedge(v); +degree(Vertex_index v) const { + Halfedge_index h = halfedge(v); - if(h == null_halfedge()){ - return 0; - } - size_type count(0); - Halfedge_index done = h; - do { - ++count; - h = opposite(next(h)); - }while(h != done); - - return count; + if (h == null_halfedge()) { + return 0; + } + size_type count(0); + Halfedge_index done = h; + do { + ++count; + h = opposite(next(h)); + } while (h != done); + + return count; } @@ -2564,191 +2188,25 @@ degree(Vertex_index v) const template typename Surface_mesh

::size_type Surface_mesh

:: -degree(Face_index f) const -{ - size_type count(0); - if(halfedge(f) == null_halfedge()){ - return 0; - } - Vertex_around_face_circulator fvit(halfedge(f),*this); - Vertex_around_face_circulator fvend = fvit; - if(fvit) do { - ++count; +degree(Face_index f) const { + size_type count(0); + if (halfedge(f) == null_halfedge()) { + return 0; + } + Vertex_around_face_circulator fvit(halfedge(f), *this); + Vertex_around_face_circulator fvend = fvit; + if (fvit) + do { + ++count; } while (++fvit != fvend); - return count; + return count; } -template template< typename Visitor> -void -Surface_mesh

:: -collect_garbage(Visitor &visitor) -{ - if (!has_garbage()) - { - return; - } - - int i, i0, i1, - nV(num_vertices()), - nE(num_edges()), - nH(num_halfedges()), - nF(num_faces()); - - Vertex_index v; - Halfedge_index h; - Face_index f; - - - // setup index mapping% - Property_map vmap = add_property_map("v:garbage-collection").first; - Property_map hmap = add_property_map("h:garbage-collection").first; - Property_map fmap = add_property_map("f:garbage-collection").first; - for (i=0; i 0) - { - i0=0; i1=nV-1; - - while (1) - { - // find first removed and last un-removed - while (!vremoved_[Vertex_index(i0)] && i0 < i1) ++i0; - while ( vremoved_[Vertex_index(i1)] && i0 < i1) --i1; - if (i0 >= i1) break; - - // swap - vprops_.swap(i0, i1); - }; - - // remember new size - nV = vremoved_[Vertex_index(i0)] ? i0 : i0+1; - } - - // really remove edges - if (nE > 0) - { - i0=0; i1=nE-1; - - while (1) - { - // find first removed and last un-removed - while (!eremoved_[Edge_index(i0)] && i0 < i1) ++i0; - while ( eremoved_[Edge_index(i1)] && i0 < i1) --i1; - if (i0 >= i1) break; - - // swap - eprops_.swap(i0, i1); - hprops_.swap(2*i0, 2*i1); - hprops_.swap(2*i0+1, 2*i1+1); - }; - - // remember new size - nE = eremoved_[Edge_index(i0)] ? i0 : i0+1; - nH = 2*nE; - } - - - // really remove faces - if (nF > 0) - { - i0=0; i1=nF-1; - - while (1) - { - // find 1st removed and last un-removed - while (!fremoved_[Face_index(i0)] && i0 < i1) ++i0; - while ( fremoved_[Face_index(i1)] && i0 < i1) --i1; - if (i0 >= i1) break; - - // swap - fprops_.swap(i0, i1); - }; - - // remember new size - nF = fremoved_[Face_index(i0)] ? i0 : i0+1; - } - - - // update vertex connectivity - for (i=0; i(vmap); - remove_property_map(hmap); - remove_property_map(fmap); - - // finally resize arrays - vprops_.resize(nV); vprops_.shrink_to_fit(); - hprops_.resize(nH); hprops_.shrink_to_fit(); - eprops_.resize(nE); eprops_.shrink_to_fit(); - fprops_.resize(nF); fprops_.shrink_to_fit(); - - removed_vertices_ = removed_edges_ = removed_faces_ = 0; - vertices_freelist_ = edges_freelist_ = faces_freelist_ = -1; - garbage_ = false; -} - -#ifndef DOXYGEN_RUNNING -namespace collect_garbage_internal { -struct Dummy_visitor{ - template - void operator()(const A&, const B&, const C&) - {} -}; - -} -#endif - template void Surface_mesh

:: -collect_garbage() -{ - collect_garbage_internal::Dummy_visitor visitor; - collect_garbage(visitor); -} - - -template -void -Surface_mesh

:: -set_recycle_garbage(bool b) -{ +set_recycle_garbage(bool b) { recycle_ = b; } @@ -2756,50 +2214,45 @@ set_recycle_garbage(bool b) template bool Surface_mesh

:: -does_recycle_garbage() const -{ +does_recycle_garbage() const { return recycle_; } -namespace internal{ - namespace handle { - template <> - struct Hash_functor{ - std::size_t - operator()(const SM_Vertex_index i) - { - return i; - } - }; +namespace internal { +namespace handle { +template <> +struct Hash_functor { + std::size_t + operator()(const SM_Vertex_index i) { + return i; + } +}; - template <> - struct Hash_functor{ - std::size_t - operator()(const SM_Halfedge_index i) - { - return i; - } - }; +template <> +struct Hash_functor { + std::size_t + operator()(const SM_Halfedge_index i) { + return i; + } +}; - template <> - struct Hash_functor{ - std::size_t - operator()(const SM_Edge_index i) - { - return i; - } - }; +template <> +struct Hash_functor { + std::size_t + operator()(const SM_Edge_index i) { + return i; + } +}; - template <> - struct Hash_functor{ - std::size_t - operator()(const SM_Face_index i) - { - return i; - } - }; +template <> +struct Hash_functor { + std::size_t + operator()(const SM_Face_index i) { + return i; } +}; +} } } // namespace CGAL @@ -2815,45 +2268,42 @@ namespace std { #ifndef CGAL_CFG_NO_STD_HASH - template <> - struct hash - : public CGAL::cpp98::unary_function { +template <> +struct hash + : public CGAL::cpp98::unary_function { - std::size_t operator()(const CGAL::SM_Halfedge_index& i) const - { - return i; - } - }; + std::size_t operator()(const CGAL::SM_Halfedge_index& i) const { + return i; + } +}; - template <> - struct hash - : public CGAL::cpp98::unary_function { +template <> +struct hash + : public CGAL::cpp98::unary_function { - std::size_t operator()(const CGAL::SM_Vertex_index& i) const - { - return i; - } - }; + std::size_t operator()(const CGAL::SM_Vertex_index& i) const { + return i; + } +}; - template <> - struct hash - : public CGAL::cpp98::unary_function { +template <> +struct hash + : public CGAL::cpp98::unary_function { - std::size_t operator()(const CGAL::SM_Face_index& i) const - { - return i; - } - }; + std::size_t operator()(const CGAL::SM_Face_index& i) const { + return i; + } +}; - template <> - struct hash - : public CGAL::cpp98::unary_function { +template <> +struct hash + : public CGAL::cpp98::unary_function { + + std::size_t operator()(const CGAL::SM_Edge_index& i) const { + return i; + } +}; - std::size_t operator()(const CGAL::SM_Edge_index& i) const - { - return i; - } - }; #endif // CGAL_CFG_NO_STD_HASH #if defined(BOOST_MSVC) @@ -2863,13 +2313,12 @@ namespace std { } // namespace std namespace boost { - template <> - struct hash { - std::size_t operator()(const CGAL::SM_Vertex_index& i) const - { - return i; - } - }; +template <> +struct hash { + std::size_t operator()(const CGAL::SM_Vertex_index& i) const { + return i; + } +}; } // namespace boost diff --git a/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h b/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h index d044c38a5570..d99be1fd8d67 100644 --- a/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h +++ b/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h @@ -429,7 +429,7 @@ template typename boost::graph_traits >::faces_size_type num_faces(const CGAL::Surface_mesh

& sm) { - return sm.num_faces(); + return sm.number_of_faces(); } template diff --git a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h index 5bff49ebab5b..a446c589eab9 100644 --- a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h +++ b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -31,58 +31,51 @@ namespace CGAL { template -class SM_edge_weight_pmap -{ +class SM_edge_weight_pmap { typedef CGAL::Surface_mesh SM; public: - typedef boost::readable_property_map_tag category; - typedef typename CGAL::Kernel_traits::type::FT value_type; - typedef value_type reference; - typedef typename SM::Edge_index key_type; + typedef boost::readable_property_map_tag category; + typedef typename CGAL::Kernel_traits::type::FT value_type; + typedef value_type reference; + typedef typename SM::Edge_index key_type; SM_edge_weight_pmap(const CGAL::Surface_mesh& sm) - : pm_(sm. template property_map< - typename SM::Vertex_index, - typename SM::Point >("v:point").first), - sm_(sm) - {} - - value_type operator[](const key_type& e) const - { + : pm_(sm.template property_map< + typename SM::Vertex_index, + typename SM::Point>("v:point").first), + sm_(sm) {} + + value_type operator[](const key_type& e) const { return approximate_sqrt(CGAL::squared_distance(pm_[source(e, sm_)], pm_[target(e, sm_)])); } friend inline - value_type get(const SM_edge_weight_pmap& m, const key_type& k) - { + value_type get(const SM_edge_weight_pmap& m, const key_type& k) { return m[k]; } private: - typename SM::template Property_map< typename SM::Vertex_index, - typename SM::Point > pm_; + typename SM::template Property_map pm_; const SM& sm_; }; template -class SM_index_pmap -{ +class SM_index_pmap { public: typedef boost::readable_property_map_tag category; - typedef boost::uint32_t value_type; - typedef boost::uint32_t reference; - typedef VEF key_type; + typedef boost::uint32_t value_type; + typedef boost::uint32_t reference; + typedef VEF key_type; - value_type operator[](const key_type& vd) const - { + value_type operator[](const key_type& vd) const { return vd; } friend inline - value_type get(const SM_index_pmap& m, const key_type& k) - { + value_type get(const SM_index_pmap& m, const key_type& k) { return m[k]; } }; @@ -95,31 +88,31 @@ namespace boost { // template -struct property_map, boost::edge_weight_t > -{ +struct property_map, boost::edge_weight_t> { typedef CGAL::SM_edge_weight_pmap type; typedef CGAL::SM_edge_weight_pmap const_type; }; } -namespace CGAL{ +namespace CGAL { template typename boost::property_map, boost::edge_weight_t>::const_type -get(boost::edge_weight_t, const CGAL::Surface_mesh& sm) -{ +get(boost::edge_weight_t, const CGAL::Surface_mesh& sm) { return CGAL::SM_edge_weight_pmap(sm); } // forward declarations, see class SM_Vertex_index; + class SM_Edge_index; + class SM_Halfedge_index; + class SM_Face_index; template typename CGAL::Kernel_traits::type::FT get(boost::edge_weight_t, const CGAL::Surface_mesh& sm, - const SM_Edge_index& e) -{ + const SM_Edge_index& e) { return CGAL::SM_edge_weight_pmap(sm)[e]; } } @@ -127,129 +120,139 @@ get(boost::edge_weight_t, const CGAL::Surface_mesh& sm, // vertex_index // -namespace boost{ +namespace boost { template -struct property_map, boost::vertex_index_t > -{ +struct property_map, boost::vertex_index_t> { typedef CGAL::SM_index_pmap >::vertex_descriptor> type; typedef CGAL::SM_index_pmap >::vertex_descriptor> const_type; }; } -namespace CGAL{ +namespace CGAL { template CGAL::SM_index_pmap -get(const boost::vertex_index_t&, const CGAL::Surface_mesh&) -{ +get(const boost::vertex_index_t&, const CGAL::Surface_mesh&) { return CGAL::SM_index_pmap >::vertex_descriptor>(); } } // // face_index // -namespace boost{ +namespace boost { template -struct property_map, boost::face_index_t > -{ +struct property_map, boost::face_index_t> { typedef CGAL::SM_index_pmap >::face_descriptor> type; typedef CGAL::SM_index_pmap >::face_descriptor> const_type; }; } -namespace CGAL{ +namespace CGAL { template CGAL::SM_index_pmap -get(const boost::face_index_t&, const CGAL::Surface_mesh&) -{ +get(const boost::face_index_t&, const CGAL::Surface_mesh&) { return CGAL::SM_index_pmap >::face_descriptor>(); } } + +// +// Face color +// todo +namespace boost { +template +struct property_map, CGAL::internal_np::face_color_map_t> { + typedef typename CGAL::Surface_mesh::template Property_map type; + typedef typename CGAL::Surface_mesh::template Property_map const_type; +}; +} +namespace CGAL { +template +typename CGAL::Surface_mesh::template Property_map +get(const CGAL::internal_np::face_color_map_t&, const CGAL::Surface_mesh&sm) { + return sm.template property_map("f:color").first; +} +} + // // edge_index // -namespace boost{ +namespace boost { template -struct property_map, boost::edge_index_t > -{ +struct property_map, boost::edge_index_t> { typedef CGAL::SM_index_pmap >::edge_descriptor> type; typedef CGAL::SM_index_pmap >::edge_descriptor> const_type; }; } -namespace CGAL{ +namespace CGAL { template CGAL::SM_index_pmap -get(const boost::edge_index_t&, const CGAL::Surface_mesh&) -{ +get(const boost::edge_index_t&, const CGAL::Surface_mesh&) { return CGAL::SM_index_pmap >::edge_descriptor>(); } } // // halfedge_index // -namespace boost{ +namespace boost { template -struct property_map, boost::halfedge_index_t > -{ +struct property_map, boost::halfedge_index_t> { typedef CGAL::SM_index_pmap >::halfedge_descriptor> type; typedef CGAL::SM_index_pmap >::halfedge_descriptor> const_type; }; } -namespace CGAL{ +namespace CGAL { template CGAL::SM_index_pmap -get(const boost::halfedge_index_t&, const CGAL::Surface_mesh&) -{ +get(const boost::halfedge_index_t&, const CGAL::Surface_mesh&) { return CGAL::SM_index_pmap >::halfedge_descriptor>(); } } // // vertex_point // -namespace boost{ -template -struct property_map, CGAL::vertex_point_t > -{ +namespace boost { +template +struct property_map, CGAL::vertex_point_t> { typedef CGAL::Surface_mesh

SM; typedef typename - SM::template Property_map< typename SM::Vertex_index, - P - > type; + SM::template Property_map type; typedef type const_type; }; } -namespace CGAL{ +namespace CGAL { namespace internal { - template - struct Get_vertex_point_map_for_Surface_mesh_return_type { - typedef typename boost::property_map +template +struct Get_vertex_point_map_for_Surface_mesh_return_type { + typedef typename boost::property_map < CGAL::Surface_mesh, CGAL::vertex_point_t - >::const_type type; - }; + >::const_type type; +}; } // end namespace internal -template +template typename boost::lazy_disable_if -< - boost::is_const, - internal::Get_vertex_point_map_for_Surface_mesh_return_type ->::type + < + boost::is_const, + internal::Get_vertex_point_map_for_Surface_mesh_return_type + >::type get(CGAL::vertex_point_t, const CGAL::Surface_mesh& g) { return g.points(); } namespace internal { - template - struct Get_graph_traits_of_SM { - typedef boost::graph_traits< CGAL::Surface_mesh > type; - }; +template +struct Get_graph_traits_of_SM { + typedef boost::graph_traits > type; +}; } // end namespace internal // get for intrinsic properties @@ -260,88 +263,98 @@ namespace internal { const TYPE& x) \ { return get(get(p, sm), x); } \ + CGAL_SM_INTRINSIC_PROPERTY(boost::uint32_t, boost::vertex_index_t, -SM_Vertex_index) + SM_Vertex_index) + CGAL_SM_INTRINSIC_PROPERTY(boost::uint32_t, boost::edge_index_t, -SM_Edge_index) + SM_Edge_index) + CGAL_SM_INTRINSIC_PROPERTY(boost::uint32_t, boost::halfedge_index_t, -SM_Halfedge_index) + SM_Halfedge_index) + CGAL_SM_INTRINSIC_PROPERTY(boost::uint32_t, boost::face_index_t, -SM_Face_index) -CGAL_SM_INTRINSIC_PROPERTY(Point&, CGAL::vertex_point_t, SM_Vertex_index) + SM_Face_index) + +CGAL_SM_INTRINSIC_PROPERTY(Point &, CGAL::vertex_point_t, SM_Vertex_index) #undef CGAL_SM_INTRINSIC_PROPERTY // put for intrinsic properties // only available for vertex_point -template +template void put(CGAL::vertex_point_t p, const CGAL::Surface_mesh& g, - typename boost::graph_traits< CGAL::Surface_mesh >::vertex_descriptor x, + typename boost::graph_traits >::vertex_descriptor x, const Point& point) { typedef CGAL::Surface_mesh SM; CGAL_assertion(g.is_valid(x)); - typename SM::template Property_map< typename boost::graph_traits::vertex_descriptor, - Point> prop = get(p, g); + typename SM::template Property_map::vertex_descriptor, + Point> prop = get(p, g); prop[x] = point; } -template +template struct graph_has_property, boost::vertex_index_t> - : CGAL::Tag_true {}; -template + : CGAL::Tag_true { +}; +template struct graph_has_property, boost::edge_index_t> - : CGAL::Tag_true {}; -template + : CGAL::Tag_true { +}; +template struct graph_has_property, boost::halfedge_index_t> - : CGAL::Tag_true {}; -template + : CGAL::Tag_true { +}; +template struct graph_has_property, boost::face_index_t> - : CGAL::Tag_true {}; -template + : CGAL::Tag_true { +}; +template struct graph_has_property, CGAL::vertex_point_t> - : CGAL::Tag_true {}; -template + : CGAL::Tag_true { +}; +template struct graph_has_property, boost::edge_weight_t> - : CGAL::Tag_true {}; + : CGAL::Tag_true { +}; +template +struct graph_has_property, CGAL::internal_np::face_color_map_t> + : CGAL::Tag_true { +}; } // CGAL // dynamic properties -namespace boost -{ +namespace boost { template -struct property_map, CGAL::dynamic_vertex_property_t > -{ +struct property_map, CGAL::dynamic_vertex_property_t > { typedef CGAL::Surface_mesh SM; - typedef typename SM:: template Property_map SMPM; + typedef typename SM::template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; template -struct property_map, CGAL::dynamic_face_property_t > -{ +struct property_map, CGAL::dynamic_face_property_t > { typedef CGAL::Surface_mesh SM; - typedef typename SM:: template Property_map SMPM; + typedef typename SM::template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; template -struct property_map, CGAL::dynamic_halfedge_property_t > -{ +struct property_map, CGAL::dynamic_halfedge_property_t > { typedef CGAL::Surface_mesh SM; - typedef typename SM:: template Property_map SMPM; + typedef typename SM::template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; template -struct property_map, CGAL::dynamic_edge_property_t > -{ +struct property_map, CGAL::dynamic_edge_property_t > { typedef CGAL::Surface_mesh SM; - typedef typename SM:: template Property_map SMPM; + typedef typename SM::template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; @@ -353,80 +366,75 @@ namespace CGAL { // get functions for dynamic properties of mutable Surface_mesh template typename boost::property_map, dynamic_vertex_property_t >::type -get(dynamic_vertex_property_t, Surface_mesh& sm) -{ +get(dynamic_vertex_property_t, Surface_mesh& sm) { typedef typename boost::property_map, dynamic_vertex_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_vertex_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Vertex_index, T>(std::string()).first)); + return DPM(sm, new SMPM( + sm.template add_property_map::Vertex_index, T>(std::string()).first)); } template typename boost::property_map, dynamic_face_property_t >::type -get(dynamic_face_property_t, Surface_mesh& sm) -{ +get(dynamic_face_property_t, Surface_mesh& sm) { typedef typename boost::property_map, dynamic_face_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_face_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Face_index, T>(std::string()).first)); + return DPM(sm, + new SMPM(sm.template add_property_map::Face_index, T>(std::string()).first)); } template typename boost::property_map, dynamic_edge_property_t >::type -get(dynamic_edge_property_t, Surface_mesh& sm) -{ +get(dynamic_edge_property_t, Surface_mesh& sm) { typedef typename boost::property_map, dynamic_edge_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_edge_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Edge_index, T>(std::string()).first)); + return DPM(sm, + new SMPM(sm.template add_property_map::Edge_index, T>(std::string()).first)); } template typename boost::property_map, dynamic_halfedge_property_t >::type -get(dynamic_halfedge_property_t, Surface_mesh& sm) -{ +get(dynamic_halfedge_property_t, Surface_mesh& sm) { typedef typename boost::property_map, dynamic_halfedge_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_halfedge_property_t >::type DPM; - return DPM(sm, new SMPM(sm.template add_property_map::Halfedge_index, T>(std::string()).first)); + return DPM(sm, new SMPM( + sm.template add_property_map::Halfedge_index, T>(std::string()).first)); } // get functions for dynamic properties of const Surface_mesh template typename boost::property_map, dynamic_vertex_property_t >::const_type -get(dynamic_vertex_property_t, const Surface_mesh& sm) -{ +get(dynamic_vertex_property_t, const Surface_mesh& sm) { return CGAL::internal::Dynamic_with_index::Vertex_index, T>(num_vertices(sm)); } template typename boost::property_map, dynamic_face_property_t >::const_type -get(dynamic_face_property_t, const Surface_mesh& sm) -{ +get(dynamic_face_property_t, const Surface_mesh& sm) { return CGAL::internal::Dynamic_with_index::Face_index, T>(num_faces(sm)); } template typename boost::property_map, dynamic_halfedge_property_t >::const_type -get(dynamic_halfedge_property_t, const Surface_mesh& sm) -{ +get(dynamic_halfedge_property_t, const Surface_mesh& sm) { return CGAL::internal::Dynamic_with_index::Halfedge_index, T>(num_halfedges(sm)); } template typename boost::property_map, dynamic_edge_property_t >::const_type -get(dynamic_edge_property_t, const Surface_mesh& sm) -{ +get(dynamic_edge_property_t, const Surface_mesh& sm) { return CGAL::internal::Dynamic_with_index::Edge_index, T>(num_edges(sm)); } // implementation detail: required by Dynamic_property_map_deleter -template +template void -remove_property(Pmap pm, CGAL::Surface_mesh

& sm) -{ +remove_property(Pmap pm, CGAL::Surface_mesh

& sm) { return sm.remove_property_map(pm); } template struct Get_pmap_of_surface_mesh { - typedef typename boost::property_map, Property_tag >::type type; + typedef typename boost::property_map, Property_tag>::type type; }; diff --git a/Surface_mesh/test/Surface_mesh/sm_join_test.cpp b/Surface_mesh/test/Surface_mesh/sm_join_test.cpp index 4a71243b8e4e..65b83c9fef6a 100644 --- a/Surface_mesh/test/Surface_mesh/sm_join_test.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_join_test.cpp @@ -19,35 +19,24 @@ typedef boost::graph_traits::face_descriptor face_descriptor; void freelist(const Sm& sm, int vc, int fc, int ec) { + // vc should be the number of in-active vertex indices std::cout << "vertex freelist" << std::endl; - vertex_descriptor vd = sm.vertex_freelist(); - while(vd != sm.null_vertex()){ - --vc; + auto unused_vertices = sm.vertex_freelist(); + for (auto vd: unused_vertices) std::cout << vd << std::endl; - halfedge_descriptor hd = halfedge(vd,sm); - vd = vertex_descriptor((Sm::size_type)hd); - } - assert(vc == 0); + assert(vc == unused_vertices.size()); std::cout << "face freelist" << std::endl; - face_descriptor fd = sm.face_freelist(); - while(fd != sm.null_face()){ - --fc; + auto unused_faces = sm.face_freelist(); + for (auto fd: unused_faces) std::cout << fd << std::endl; - halfedge_descriptor hd = halfedge(fd,sm); - fd = face_descriptor((Sm::size_type)hd); - } - assert(fc == 0); + assert(fc == unused_faces.size()); std::cout << "edge freelist" << std::endl; - edge_descriptor ed = sm.edge_freelist(); - while(ed != sm.null_edge()){ - --ec; + auto unused_edges = sm.edge_freelist(); + for (auto ed: unused_edges) std::cout << ed << std::endl; - halfedge_descriptor hd = next(halfedge(ed,sm),sm); - ed = edge(hd,sm); - } - assert(ec == 0); + assert(ec == unused_edges.size()); } diff --git a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp index 2a925c7c325c..40da40f5826e 100644 --- a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp @@ -33,28 +33,30 @@ void OpenOFF(int i) assert(in && !surface_mesh.is_empty()); - SMesh::Property_map fcolors = - surface_mesh.property_map("f:color").first; - - SMesh::Property_map vcolors = - surface_mesh.property_map("v:color").first; - CGAL::IO::Color c = fcolors[*(surface_mesh.faces().begin())]; - assert(c== CGAL::IO::Color(229,0,0)); - c = fcolors[*(--surface_mesh.faces().end())]; - assert(c== CGAL::IO::Color(0,0,229)); - - c = vcolors[*(surface_mesh.vertices().begin())]; - assert((c== CGAL::IO::Color(229,0,0))); - c = vcolors[*(--surface_mesh.vertices().end())]; - assert((c== CGAL::IO::Color(0,0,229))); + auto [fcolors, created_fcolors] = surface_mesh.property_map("f:color"); + auto [vcolors, created_vcolors] = surface_mesh.property_map("v:color"); + + // Both color maps should have already existed, because they were loaded from the file + assert(!created_fcolors); + assert(!created_vcolors); + + auto first_fcolor = fcolors[*(surface_mesh.faces().begin())]; + assert(first_fcolor == CGAL::IO::Color(229, 0, 0)); + auto last_fcolor = fcolors[*(--surface_mesh.faces().end())]; + assert(last_fcolor == CGAL::IO::Color(0, 0, 229)); + + auto first_vcolor = vcolors[*(surface_mesh.vertices().begin())]; + assert((first_vcolor == CGAL::IO::Color(229, 0, 0))); + auto last_vcolor = vcolors[*(--surface_mesh.vertices().end())]; + assert((last_vcolor == CGAL::IO::Color(0, 0, 229))); } int main() { OpenOFF(1); - OpenOFF(2); - OpenOFF(3); +// OpenOFF(2); +// OpenOFF(3); std::cerr << "done" << std::endl; return 0; } diff --git a/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp b/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp index 16fb699982c5..1496c35ace5f 100644 --- a/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp +++ b/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp @@ -219,15 +219,11 @@ void point_position_accessor () void properties () { Surface_fixture f; - - Sm::Property_map prop; - bool created = false; - - boost::tie(prop,created) = f.m.add_property_map("illuminatiproperty", 23); + auto [prop, created] = f.m.add_property_map("illuminatiproperty", 23); assert(created == true); - boost::tie(prop, created)= f.m.add_property_map("illuminatiproperty"); - assert(created == false); + auto [_, created_again] = f.m.add_property_map("illuminatiproperty"); + assert(created_again == false); } void move () { From 02b126d961a47c1d352ff1d56eeab5c7eb83e1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 17 May 2023 17:08:01 +0200 Subject: [PATCH 043/520] maps are not necessarily default constructible --- .../CGAL/boost/graph/IO/Generic_facegraph_builder.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h index 4952f5f0a5f5..a83944ac3ad9 100644 --- a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h +++ b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h @@ -105,10 +105,10 @@ class Generic_facegraph_builder // Default constructors should only be used when VNM, VCM, etc. are defined as Constant_property_maps // This fails in cases where get_parameter() succeeds // even though internal_np::Lookup_named_param_def defaulted to Constant_property_map - VNM vnm = choose_parameter(get_parameter(np, internal_np::vertex_normal_map), VNM()); - VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map), VCM()); - VTM vtm = choose_parameter(get_parameter(np, internal_np::vertex_texture_map), VTM()); - FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map), FCM()); + VNM vnm = choose_parameter(get_parameter(np, internal_np::vertex_normal_map)); + VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map)); + VTM vtm = choose_parameter(get_parameter(np, internal_np::vertex_texture_map)); + FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map)); const bool has_vertex_normals = (is_vnm_requested && !(vertex_normals.empty())); const bool has_vertex_colors = (is_vcm_requested && !(vertex_colors.empty())); From 98ae089d10cc8ccdf5ea65292ec5f66f695d191d Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 17 May 2023 21:57:51 +0100 Subject: [PATCH 044/520] Add package 2D Polygon Repair --- Documentation/doc/Documentation/packages.txt | 1 + .../include/CGAL/license/Polygon_repair_2.h | 54 ++++++++++++++++++ .../doc/Polygon_repair_2/Doxyfile.in | 4 ++ .../Polygon_repair_2/PackageDescription.txt | 40 +++++++++++++ .../doc/Polygon_repair_2/Polygon_repair_2.txt | 29 ++++++++++ .../doc/Polygon_repair_2/dependencies | 7 +++ .../doc/Polygon_repair_2/examples.txt | 3 + .../fig/Polygon_repair_2-small.png | Bin 0 -> 9467 bytes .../examples/Polygon_repair_2/CMakeLists.txt | 16 ++++++ .../Polygon_repair_2/repair_polygon_2.cpp | 19 ++++++ .../include/CGAL/Polygon_repair_2/repair.h | 25 ++++++++ .../package_info/Polygon_repair_2/copyright | 1 + .../Polygon_repair_2/description.txt | 1 + .../package_info/Polygon_repair_2/license.txt | 1 + .../Polygon_repair_2/long_description.txt | 0 .../package_info/Polygon_repair_2/maintainer | 1 + .../test/Polygon_repair_2/CMakeLists.txt | 16 ++++++ .../repair_polygon_2_test.cpp | 19 ++++++ 18 files changed, 237 insertions(+) create mode 100644 Installation/include/CGAL/license/Polygon_repair_2.h create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/dependencies create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/examples.txt create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/fig/Polygon_repair_2-small.png create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp create mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h create mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/copyright create mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/description.txt create mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/license.txt create mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/long_description.txt create mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/maintainer create mode 100644 Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp diff --git a/Documentation/doc/Documentation/packages.txt b/Documentation/doc/Documentation/packages.txt index aa061c842d8d..d89ade951422 100644 --- a/Documentation/doc/Documentation/packages.txt +++ b/Documentation/doc/Documentation/packages.txt @@ -31,6 +31,7 @@ \cgalPackageSection{PartPolygons,Polygons} \package_listing{Polygon} +\package_listing{Polygon_repair_2} \package_listing{Boolean_set_operations_2} \package_listing{Nef_2} \package_listing{Nef_S2} diff --git a/Installation/include/CGAL/license/Polygon_repair_2.h b/Installation/include/CGAL/license/Polygon_repair_2.h new file mode 100644 index 000000000000..11c0f94e3c5a --- /dev/null +++ b/Installation/include/CGAL/license/Polygon_repair_2.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 GeometryFactory SARL (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Andreas Fabri +// +// Warning: this file is generated, see include/CGAL/license/README.md + +#ifndef CGAL_LICENSE_POLYGON_REPAIR_2_H +#define CGAL_LICENSE_POLYGON_REPAIR_2_H + +#include +#include + +#ifdef CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE + +# if CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +# if defined(CGAL_LICENSE_WARNING) + + CGAL_pragma_warning("Your commercial license for CGAL does not cover " + "this release of the 2D Polygon Repair package.") +# endif + +# ifdef CGAL_LICENSE_ERROR +# error "Your commercial license for CGAL does not cover this release \ + of the 2D Polygon Repair package. \ + You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +# endif // CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +#else // no CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE + +# if defined(CGAL_LICENSE_WARNING) + CGAL_pragma_warning("\nThe macro CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE is not defined." + "\nYou use the CGAL 2D Polygon Repair package under " + "the terms of the GPLv3+.") +# endif // CGAL_LICENSE_WARNING + +# ifdef CGAL_LICENSE_ERROR +# error "The macro CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE is not defined.\ + You use the CGAL 2D Polygon Repair package under the terms of \ + the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +#endif // no CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE + +#endif // CGAL_LICENSE_POLYGON_REPAIR_2_H diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in b/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in new file mode 100644 index 000000000000..eac877a4f2bc --- /dev/null +++ b/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in @@ -0,0 +1,4 @@ +@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 2D Polygon Repair" +INPUT = ${CMAKE_SOURCE_DIR}/Polygon_repair_2/doc/Polygon_repair_2/ \ + ${CMAKE_SOURCE_DIR}/Polygon_repair_2/include diff --git a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt new file mode 100644 index 000000000000..2d02aeae88a7 --- /dev/null +++ b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt @@ -0,0 +1,40 @@ +// PRETTY PACKAGE NAME should equal the project title in Doxyfile.in + +/// \defgroup PkgPolygonRepair2 2D Polygon Repair Reference +/// \defgroup PkgPolygonRepair2Concepts Concepts +/// \ingroup PkgPolygonRepair2 + +/// \defgroup PkgPolygonRepair2AlgorithmClasses Algorithm Classes +/// \ingroup PkgPolygonRepair2 + +/// \defgroup PkgPolygonRepair2TraitsClasses Traits Classes +/// \ingroup PkgPolygonRepair2 + +/// \defgroup PkgPolygonRepair2Miscellaneous Miscellaneous +/// \ingroup PkgPolygonRepair2 + +/*! +\addtogroup PkgPolygonRepair2 +\todo check generated documentation + +\cgalPkgDescriptionBegin{2D Polygon Repair,PkgPolygonRepair2} +\cgalPkgPicture{Polygon_repair_2-small.png} + +\cgalPkgSummaryBegin +\cgalPkgAuthors{Ken Arroyo Ohori} +\cgalPkgDesc{The package provides algorithms to repair 2D polygons. } +\cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepair2} +\cgalPkgSummaryEnd + +\cgalPkgShortInfoBegin +\cgalPkgSince{6.0} +\cgalPkgDependsOn{\ref PkgDEPENDENCY} +\cgalPkgBib{cgal:x-x} +\cgalPkgLicense{\ref licensesGPL "GPL"} +\cgalPkgDemo{DEMO 1,demo1.zip} +\cgalPkgDemo{DEMO 2,demo2.zip} +\cgalPkgShortInfoEnd + +\cgalPkgDescriptionEnd + +*/ diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt new file mode 100644 index 000000000000..723a7d05053d --- /dev/null +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -0,0 +1,29 @@ +namespace CGAL { +/*! + +\mainpage User Manual +\anchor Chapter_2D_Polygon_repair + +\cgalAutoToc +\author Ken Arroyo Ohori + +This chapter describes the ... + +\section SectionPolygonRepair2_Definitions Definitions + +Section on definitions here ... + +\section SectionPolygonRepair2_Examples Examples + +\subsection SubsectionPolygonRepair2_FirstExample First Example + +The following example shows ... + +\cgalExample{Polygon_repair_2/repair_polygon_2.cpp} + +\cgalFigureBegin{figPck,bench.png} +Left: ... +\cgalFigureEnd + +*/ +} /* namespace CGAL */ diff --git a/Polygon_repair_2/doc/Polygon_repair_2/dependencies b/Polygon_repair_2/doc/Polygon_repair_2/dependencies new file mode 100644 index 000000000000..6df3ace8d07a --- /dev/null +++ b/Polygon_repair_2/doc/Polygon_repair_2/dependencies @@ -0,0 +1,7 @@ +Manual +Kernel_23 +STL_Extension +Algebraic_foundations +Circulator +Stream_support +Polygon \ No newline at end of file diff --git a/Polygon_repair_2/doc/Polygon_repair_2/examples.txt b/Polygon_repair_2/doc/Polygon_repair_2/examples.txt new file mode 100644 index 000000000000..c3d2195f5d89 --- /dev/null +++ b/Polygon_repair_2/doc/Polygon_repair_2/examples.txt @@ -0,0 +1,3 @@ +/*! +\example Polygon_repair_2/repair_polygon_2.cpp +*/ diff --git a/Polygon_repair_2/doc/Polygon_repair_2/fig/Polygon_repair_2-small.png b/Polygon_repair_2/doc/Polygon_repair_2/fig/Polygon_repair_2-small.png new file mode 100644 index 0000000000000000000000000000000000000000..5609b037587a48c46b483d2c0768c7ae88b40c72 GIT binary patch literal 9467 zcmZ`QsjDeqppv3OAP@}2H?o@G)$`?vj0nC9ne$JA7erHK1zGS4zV2k21HcQ4 z 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 literal 0 HcmV?d00001 diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt new file mode 100644 index 000000000000..7e634d7d73e5 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt @@ -0,0 +1,16 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.1...3.23) +project(Polygon_repair_2_Examples) + +find_package(CGAL REQUIRED) + +# create a target per cppfile +file( + GLOB cppfiles + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +foreach(cppfile ${cppfiles}) + create_single_source_cgal_program("${cppfile}") +endforeach() diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp new file mode 100644 index 000000000000..20b2c9694a81 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include +#include +#include +#include +#include + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Polygon_2 Polygon_2; +typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + +int main(int argc, char* argv[]) +{ + std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); + return 0; +} diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h new file mode 100644 index 000000000000..eae44162a9b7 --- /dev/null +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h @@ -0,0 +1,25 @@ +// Copyright (c) 2023 GeometryFactory. All rights reserved. +// 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) : Ken Arroyo Ohori +// +#ifndef CGAL_POLYGON_REPAIR_2_REPAIR_H +#define CGAL_POLYGON_REPAIR_2_REPAIR_H + +#include + + +namespace CGAL { + +namespace Polygon_repair_2 { + +} // namespace Polygon_repair_2 +} // namespace CGAL + +#endif diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/copyright b/Polygon_repair_2/package_info/Polygon_repair_2/copyright new file mode 100644 index 000000000000..b9a65603a2ee --- /dev/null +++ b/Polygon_repair_2/package_info/Polygon_repair_2/copyright @@ -0,0 +1 @@ +GeometryFactory (France) diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/description.txt b/Polygon_repair_2/package_info/Polygon_repair_2/description.txt new file mode 100644 index 000000000000..4fee1c792710 --- /dev/null +++ b/Polygon_repair_2/package_info/Polygon_repair_2/description.txt @@ -0,0 +1 @@ +Package Polygon_repair_2 provides functions to repair polygons. diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/license.txt b/Polygon_repair_2/package_info/Polygon_repair_2/license.txt new file mode 100644 index 000000000000..8bb8efcb72b0 --- /dev/null +++ b/Polygon_repair_2/package_info/Polygon_repair_2/license.txt @@ -0,0 +1 @@ +GPL (v3 or later) diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/long_description.txt b/Polygon_repair_2/package_info/Polygon_repair_2/long_description.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/maintainer b/Polygon_repair_2/package_info/Polygon_repair_2/maintainer new file mode 100644 index 000000000000..2427333ef578 --- /dev/null +++ b/Polygon_repair_2/package_info/Polygon_repair_2/maintainer @@ -0,0 +1 @@ +GeometryFactory diff --git a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt new file mode 100644 index 000000000000..7e634d7d73e5 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt @@ -0,0 +1,16 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.1...3.23) +project(Polygon_repair_2_Examples) + +find_package(CGAL REQUIRED) + +# create a target per cppfile +file( + GLOB cppfiles + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +foreach(cppfile ${cppfiles}) + create_single_source_cgal_program("${cppfile}") +endforeach() diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp new file mode 100644 index 000000000000..20b2c9694a81 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -0,0 +1,19 @@ +#include +#include + +#include +#include +#include +#include +#include + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Polygon_2 Polygon_2; +typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + +int main(int argc, char* argv[]) +{ + std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); + return 0; +} From c066a18fe27b3fc59b574254341c191c7732d97a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 18 May 2023 11:36:06 +0200 Subject: [PATCH 045/520] Fix issues with the simpler unit tests --- .../graph/IO/Generic_facegraph_builder.h | 3 - Property_map/include/CGAL/Properties.h | 45 +++++- .../include/CGAL/Surface_mesh/Surface_mesh.h | 136 +++++++----------- .../test/Surface_mesh/sm_open_colored_off.cpp | 6 +- Surface_mesh/test/Surface_mesh/sm_remove.cpp | 53 +++---- 5 files changed, 113 insertions(+), 130 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h index a83944ac3ad9..0149990e305d 100644 --- a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h +++ b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h @@ -102,9 +102,6 @@ class Generic_facegraph_builder // Construct the graph VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_property_map(CGAL::vertex_point, g)); - // Default constructors should only be used when VNM, VCM, etc. are defined as Constant_property_maps - // This fails in cases where get_parameter() succeeds - // even though internal_np::Lookup_named_param_def defaulted to Constant_property_map VNM vnm = choose_parameter(get_parameter(np, internal_np::vertex_normal_map)); VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map)); VTM vtm = choose_parameter(get_parameter(np, internal_np::vertex_texture_map)); diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 445ebfcbd5dd..47c0c6041fd2 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -35,6 +35,8 @@ class Property_array_base { virtual void reset(Index i) = 0; + virtual const std::type_info& type() = 0; + }; /*! @@ -68,7 +70,7 @@ class Property_array : public Property_array_base { return new_array; } - virtual void copy(const Property_array_base& other_base) { + virtual void copy(const Property_array_base& other_base) override { auto& other = dynamic_cast&>(other_base); m_data = other.m_data; CGAL_precondition(m_active_indices.size() == m_data.size()); @@ -96,10 +98,13 @@ class Property_array : public Property_array_base { m_data[std::size_t(i)] = m_default_value; }; - std::size_t capacity() const { return m_data.size(); } + virtual const std::type_info& type() override { return typeid(T); }; public: + // todo: there's not really a good reason to use this, maybe it should be removed + std::size_t capacity() const { return m_data.size(); } + const_reference operator[](Index i) const { CGAL_precondition(std::size_t(i) < m_data.size()); return m_data[std::size_t(i)]; @@ -141,9 +146,9 @@ class Property_array_handle { reference operator[](Index i) { return m_array[i]; } - bool operator==(const Property_array& other) const { return &other.m_array == m_array; } + bool operator==(const Property_array_handle& other) const { return other.m_array == m_array; } - bool operator!=(const Property_array& other) const { return !operator==(other); } + bool operator!=(const Property_array_handle& other) const { return !operator==(other); } inline friend reference get(Property_array_handle p, const Index& i) { return p[i]; } @@ -267,7 +272,33 @@ class Property_container { */ bool remove_property(const std::string& name) { return m_property_arrays.erase(name) == 1; } - std::size_t num_properties() { return m_property_arrays.size(); } + void remove_all_properties_except(const std::vector& preserved_names) { + // todo: if this is used often, it should take a parameter pack instead of a vector + // A fold expression could then be used in place of std::find for better performance + for (auto it = m_property_arrays.begin(); it != m_property_arrays.end();) { + auto const& [name, array] = *it; + if (std::find(preserved_names.begin(), preserved_names.end(), name) == preserved_names.end()) + it = m_property_arrays.erase(it); + else + it++; + } + } + + std::vector properties() const { + std::vector property_names{}; + for (auto const&[name, _] : m_property_arrays) + property_names.emplace_back(name); + return property_names; + } + + std::size_t num_properties() const{ return m_property_arrays.size(); } + + const std::type_info& property_type(const std::string& name) const { + if (auto it = m_property_arrays.find(name); it != m_property_arrays.end()) + return it->second->type(); + else + return typeid(void); + } public: @@ -286,7 +317,7 @@ class Property_container { // Expand the storage and return the last element reserve(capacity() + 1); m_active_indices.back() = true; - Index first_new_index{capacity() - 1}; + auto first_new_index = Index(capacity() - 1); reset(first_new_index); return first_new_index; } @@ -395,7 +426,7 @@ class Property_container { /*! * Adds the elements of the other container to this container for each property which is present in this container. * - * Gaps are preserved, and all elements of the other container are guaranteed + * Gaps in both containers are preserved, and all elements of the other container are guaranteed * to appear after the elements of this container. * Properties in this container which don't appear in the other container are extended with default values. * Properties in the other container which don't appear in this one are not included. diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 5a45933e06b2..e49684a81295 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -239,9 +239,10 @@ namespace CGAL { class SM_Edge_index { public: + // todo: why does Edge_index use a different size type from other indices? typedef std::size_t size_type; - SM_Edge_index() : halfedge_((std::numeric_limits::max)()) { } + SM_Edge_index() = default; explicit SM_Edge_index(size_type idx) : halfedge_(idx * 2) { } @@ -310,7 +311,7 @@ namespace CGAL { } private: - SM_Halfedge_index halfedge_; + SM_Halfedge_index halfedge_{}; }; #endif @@ -891,12 +892,6 @@ class Surface_mesh hconn_(hprops_.add_property("h:connectivity")), fconn_(fprops_.add_property("f:connectivity")), vpoint_(vprops_.add_property("v:point")), - // todo: the following shouldn't be necessary - vremoved_(vprops_.add_property("v:removed", false)), - eremoved_(eprops_.add_property("e:removed", false)), - fremoved_(fprops_.add_property("f:removed", false)), - removed_vertices_(0), removed_edges_(0), removed_faces_(0), - garbage_(false), anonymous_property_(0) {} /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. @@ -909,12 +904,6 @@ class Surface_mesh vpoint_(vprops_.get_property("v:point")), hconn_(hprops_.get_property("h:connectivity")), fconn_(fprops_.get_property("f:connectivity")), - // todo: the following shouldn't be necessary - vremoved_(vprops_.get_property("v:removed")), - eremoved_(eprops_.get_property("e:removed")), - fremoved_(fprops_.get_property("f:removed")), - removed_vertices_(0), removed_edges_(0), removed_faces_(0), - garbage_(false), anonymous_property_(0) {} /// Move constructor. @@ -927,12 +916,6 @@ class Surface_mesh vpoint_(vprops_.get_property("v:point")), hconn_(hprops_.get_property("h:connectivity")), fconn_(fprops_.get_property("f:connectivity")), - // todo: the following shouldn't be necessary - vremoved_(vprops_.get_property("v:removed")), - eremoved_(eprops_.get_property("e:removed")), - fremoved_(fprops_.get_property("f:removed")), - removed_vertices_(0), removed_edges_(0), removed_faces_(0), - garbage_(false), anonymous_property_(0) {} /// assigns `rhs` to `*this`. Performs a deep copy of all properties. @@ -959,7 +942,10 @@ class Surface_mesh /// adds a new vertex, and resizes vertex properties if necessary. Vertex_index add_vertex() { - return vprops_.emplace(); + if (recycle_) + return vprops_.emplace(); + else + return vprops_.emplace_back(); } /// adds a new vertex, resizes vertex properties if necessary, @@ -978,11 +964,17 @@ class Surface_mesh Halfedge_index add_edge() { // Add properties for a new edge - eprops_.emplace(); + if (recycle_) + eprops_.emplace(); + else + eprops_.emplace_back(); // Add properties for a pair of new half-edges // The new half-edges are placed adjacently, and we return the index of the first - return hprops_.emplace_group(2); + if (recycle_) + return hprops_.emplace_group(2); + else + return hprops_.emplace_group_back(2); } /// adds two opposite halfedges, and resizes edge and halfedge properties if necessary. @@ -1003,7 +995,10 @@ class Surface_mesh /// adds a new face, and resizes face properties if necessary. Face_index add_face() { - return fprops_.emplace(); + if (recycle_) + return fprops_.emplace(); + else + return fprops_.emplace_back(); } /// if possible, adds a new face with vertices from a range with value type `Vertex_index`. @@ -1110,7 +1105,18 @@ class Surface_mesh /// /// After calling this method, the object is the same as a newly constructed object. The additional property maps are also removed and must thus be re-added if needed. void clear() { - // todo + clear_without_removing_property_maps(); + vprops_.remove_all_properties_except({"v:connectivity", "v:point"}); + hprops_.remove_all_properties_except({"h:connectivity"}); + fprops_.remove_all_properties_except({"f:connectivity"}); + eprops_.remove_all_properties_except({}); + } + + void clear_without_removing_property_maps() { + vprops_.reserve(0); + hprops_.reserve(0); + eprops_.reserve(0); + fprops_.reserve(0); } @@ -1243,7 +1249,12 @@ class Surface_mesh /// checks if any vertices, halfedges, edges, or faces are marked as removed. /// \sa collect_garbage // todo: remove - bool has_garbage() const { return false; } + bool has_garbage() const { + return number_of_removed_vertices() != 0 || + number_of_removed_edges() != 0 || + number_of_removed_halfedges() != 0 || + number_of_removed_faces() != 0; + } /// really removes vertices, halfedges, edges, and faces which are marked removed. /// \sa `has_garbage()` @@ -1251,7 +1262,9 @@ class Surface_mesh /// In case you store indices in an auxiliary data structure /// or in a property these indices are potentially no longer /// referring to the right elements. - void collect_garbage(); + void collect_garbage() { + // todo: this should compress the array + } // //undocumented convenience function that allows to get old-index->new-index information // template @@ -1261,10 +1274,10 @@ class Surface_mesh /// upon addition of new elements. /// When set to `true` (default value), new elements are first picked in the garbage (if any) /// while if set to `false` only new elements are created. - void set_recycle_garbage(bool b); + void set_recycle_garbage(bool b) { recycle_ = b; } /// Getter - bool does_recycle_garbage() const; + bool does_recycle_garbage() const { return recycle_; } /// @cond CGAL_DOCUMENT_INTERNALS /// removes unused memory from vectors. This shrinks the storage @@ -1856,25 +1869,10 @@ class Surface_mesh template void remove_property_map(Property_map p) { // todo: this is never used, but it should probably still work + // Maybe this could be replaced with removal by name? } -// /// removes all property maps for index type `I` added by a call to `add_property_map()`. -// /// The memory allocated for those property maps is freed. -// template -// void remove_property_maps() { -// Property_selector(this).resize_property_array(); -// } - -// /// removes all property maps for all index types added by a call to `add_property_map()`. -// /// The memory allocated for those property maps is freed. -// void remove_all_property_maps() { -// remove_property_maps(); -// remove_property_maps(); -// remove_property_maps(); -// remove_property_maps(); -// } - /// @cond CGAL_DOCUMENT_INTERNALS /// returns the std::type_info of the value type of the /// property identified by `name`. `typeid(void)` if `name` @@ -1884,7 +1882,7 @@ class Surface_mesh template const std::type_info& property_type(const std::string& name) { - return Property_selector(this)().get_type(name); + return get_property_container().property_type(name); } /// @endcond @@ -1892,7 +1890,7 @@ class Surface_mesh /// @tparam I The key type of the properties. template std::vector properties() const { - return Property_selector(const_cast(this))().properties(); + return get_property_container().properties(); } /// returns the property for the string "v:point". @@ -1981,19 +1979,10 @@ class Surface_mesh Property_array& vpoint_; - Property_array& vremoved_; - Property_array& eremoved_; - Property_array& fremoved_; - - size_type removed_vertices_; - size_type removed_edges_; - size_type removed_faces_; - size_type vertices_freelist_; size_type edges_freelist_; size_type faces_freelist_; - bool garbage_; - bool recycle_; + bool recycle_ = true; size_type anonymous_property_; }; @@ -2003,13 +1992,13 @@ class Surface_mesh * @{ */ - /// \relates Surface_mesh - /// Inserts `other` into `sm`. - /// Shifts the indices of vertices of `other` by `sm.number_of_vertices() + sm.number_of_removed_vertices()` - /// and analogously for halfedges, edges, and faces. - /// Copies entries of all property maps which have the same name in `sm` and `other`. - /// that is, property maps which are only in `other` are ignored. - /// Also copies elements which are marked as removed, and concatenates the freelists of `sm` and `other`. +/// \relates Surface_mesh +/// Inserts `other` into `sm`. +/// Shifts the indices of vertices of `other` by `sm.number_of_vertices() + sm.number_of_removed_vertices()` +/// and analogously for halfedges, edges, and faces. +/// Copies entries of all property maps which have the same name in `sm` and `other`. +/// that is, property maps which are only in `other` are ignored. +/// Also copies elements which are marked as removed, and concatenates the freelists of `sm` and `other`. template Surface_mesh

& operator+=(Surface_mesh

& sm, const Surface_mesh

& other) { @@ -2058,13 +2047,9 @@ operator=(const Surface_mesh

& rhs) { // how many elements are removed? - removed_vertices_ = rhs.removed_vertices_; - removed_edges_ = rhs.removed_edges_; - removed_faces_ = rhs.removed_faces_; vertices_freelist_ = rhs.vertices_freelist_; edges_freelist_ = rhs.edges_freelist_; faces_freelist_ = rhs.faces_freelist_; - garbage_ = rhs.garbage_; recycle_ = rhs.recycle_; anonymous_property_ = rhs.anonymous_property_; } @@ -2203,21 +2188,6 @@ degree(Face_index f) const { return count; } -template -void -Surface_mesh

:: -set_recycle_garbage(bool b) { - recycle_ = b; -} - - -template -bool -Surface_mesh

:: -does_recycle_garbage() const { - return recycle_; -} - namespace internal { namespace handle { diff --git a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp index 40da40f5826e..0c9987634202 100644 --- a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp @@ -55,8 +55,8 @@ void OpenOFF(int i) int main() { OpenOFF(1); -// OpenOFF(2); -// OpenOFF(3); - std::cerr << "done" << std::endl; + OpenOFF(2); + OpenOFF(3); + std::cout << "done" << std::endl; return 0; } diff --git a/Surface_mesh/test/Surface_mesh/sm_remove.cpp b/Surface_mesh/test/Surface_mesh/sm_remove.cpp index 26713c930e18..48e1301433fc 100644 --- a/Surface_mesh/test/Surface_mesh/sm_remove.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_remove.cpp @@ -19,13 +19,13 @@ int main() Sm m; Sm::vertex_index u; - assert(m.num_vertices() == 0); + assert(m.number_of_vertices() == 0); assert(m.number_of_removed_vertices() == 0); for(int i=0; i < 10; i++){ u = m.add_vertex(Point_3(0,0,0)); m.remove_vertex(u); } - assert(m.num_vertices() == 1); + assert(m.number_of_vertices() == 0); assert(m.number_of_removed_vertices() == 1); @@ -34,16 +34,16 @@ int main() assert(! m.does_recycle_garbage()); m.add_vertex(Point_3(0,0,0)); - assert(m.num_vertices() == 2); + assert(m.number_of_vertices() == 1); assert(m.number_of_removed_vertices() == 1); m.set_recycle_garbage(true); m.add_vertex(Point_3(0,0,0)); - assert(m.num_vertices() == 2); + assert(m.number_of_vertices() == 2); assert(m.number_of_removed_vertices() == 0); - std::cout << m.num_vertices() << " " << m.number_of_removed_vertices() << std::endl; + std::cout << m.number_of_vertices() << " " << m.number_of_removed_vertices() << std::endl; // make sure all is OK when clearing the mesh @@ -51,9 +51,6 @@ int main() auto hconn = m.property_map("h:connectivity").first; auto fconn = m.property_map("f:connectivity").first; auto vpoint = m.property_map("v:point").first; - auto vremoved = m.property_map("v:removed").first; - auto eremoved = m.property_map("e:removed").first; - auto fremoved = m.property_map("f:removed").first; // first call to squat the first available position m.add_property_map("vprop_dummy"); @@ -78,21 +75,15 @@ int main() auto l_hconn = m.property_map("h:connectivity").first; auto l_fconn = m.property_map("f:connectivity").first; auto l_vpoint = m.property_map("v:point").first; - auto l_vremoved = m.property_map("v:removed").first; - auto l_eremoved = m.property_map("e:removed").first; - auto l_fremoved = m.property_map("f:removed").first; - - assert( &vconn.array() == &l_vconn.array() ); - assert( &hconn.array() == &l_hconn.array() ); - assert( &fconn.array() == &l_fconn.array() ); - assert( &vpoint.array() == &l_vpoint.array() ); - assert( &vremoved.array() == &l_vremoved.array() ); - assert( &eremoved.array() == &l_eremoved.array() ); - assert( &fremoved.array() == &l_fremoved.array() ); - assert( &vprop.array() == &l_vprop.array() ); - assert( &hprop.array() == &l_hprop.array() ); - assert( &fprop.array() == &l_fprop.array() ); - assert( &eprop.array() == &l_eprop.array() ); + + assert( vconn == l_vconn ); + assert( hconn == l_hconn ); + assert( fconn == l_fconn ); + assert( vpoint == l_vpoint ); + assert( vprop == l_vprop ); + assert( hprop == l_hprop ); + assert( fprop == l_fprop ); + assert( eprop == l_eprop ); } { @@ -102,17 +93,11 @@ int main() auto l_hconn = m.property_map("h:connectivity").first; auto l_fconn = m.property_map("f:connectivity").first; auto l_vpoint = m.property_map("v:point").first; - auto l_vremoved = m.property_map("v:removed").first; - auto l_eremoved = m.property_map("e:removed").first; - auto l_fremoved = m.property_map("f:removed").first; - - assert( &vconn.array() == &l_vconn.array() ); - assert( &hconn.array() == &l_hconn.array() ); - assert( &fconn.array() == &l_fconn.array() ); - assert( &vpoint.array() == &l_vpoint.array() ); - assert( &vremoved.array() == &l_vremoved.array() ); - assert( &eremoved.array() == &l_eremoved.array() ); - assert( &fremoved.array() == &l_fremoved.array() ); + + assert( vconn == l_vconn ); + assert( hconn == l_hconn ); + assert( fconn == l_fconn ); + assert( vpoint == l_vpoint ); } return 0; From 3d0fc6065e213903ee556c33bdb9481ea35db354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 May 2023 18:35:50 +0200 Subject: [PATCH 046/520] fix minimal doc --- Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in | 3 +-- .../doc/Polygon_repair_2/PackageDescription.txt | 12 +++++++++++- .../include/CGAL/Polygon_repair_2/repair.h | 5 +++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in b/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in index eac877a4f2bc..2ef1e12b3c0d 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in +++ b/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in @@ -1,4 +1,3 @@ @INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 2D Polygon Repair" -INPUT = ${CMAKE_SOURCE_DIR}/Polygon_repair_2/doc/Polygon_repair_2/ \ - ${CMAKE_SOURCE_DIR}/Polygon_repair_2/include + diff --git a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt index 2d02aeae88a7..8f02d4c00b41 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt @@ -10,6 +10,9 @@ /// \defgroup PkgPolygonRepair2TraitsClasses Traits Classes /// \ingroup PkgPolygonRepair2 +/// \defgroup PkgPolygonRepair2Functions Functions +/// \ingroup PkgPolygonRepair2 + /// \defgroup PkgPolygonRepair2Miscellaneous Miscellaneous /// \ingroup PkgPolygonRepair2 @@ -28,7 +31,7 @@ \cgalPkgShortInfoBegin \cgalPkgSince{6.0} -\cgalPkgDependsOn{\ref PkgDEPENDENCY} +\cgalPkgDependsOn{\ref PkgPolygon2} \cgalPkgBib{cgal:x-x} \cgalPkgLicense{\ref licensesGPL "GPL"} \cgalPkgDemo{DEMO 1,demo1.zip} @@ -37,4 +40,11 @@ \cgalPkgDescriptionEnd +\cgalClassifedRefPages + +\cgalCRPSection{Concepts} +- ... +\cgalCRPSection{Functions} +- `CGAL::Polygon_repair_2::repair()` + */ diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h index eae44162a9b7..bd0bb93439c7 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h @@ -19,6 +19,11 @@ namespace CGAL { namespace Polygon_repair_2 { +/// \ingroup PkgPolygonRepair2Functions +/// This is a place holder for a function +template +bool repair(Polygon& p); + } // namespace Polygon_repair_2 } // namespace CGAL From 6dc9415e9fd3b4ddd8604f39a994954346fec57c Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 25 May 2023 11:08:07 +0200 Subject: [PATCH 047/520] Avoid default-construction of pmaps in write_OFF --- .../include/CGAL/Surface_mesh/IO/OFF.h | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h index 0ebeba94af60..583a25e3eb39 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h @@ -338,11 +338,9 @@ bool write_OFF_with_or_without_fcolors(std::ostream& os, const bool has_fcolors = !(is_default_parameter::value); - typename Mesh::template Property_map fcolors; - bool has_internal_fcolors; - std::tie(fcolors, has_internal_fcolors) = sm.template property_map("f:color"); + auto [fcolors, has_internal_fcolors] = sm.template property_map("f:color"); - if(!has_fcolors && has_internal_fcolors && std::distance(fcolors.begin(), fcolors.end()) > 0) + if(!has_fcolors && has_internal_fcolors && fcolors.size() > 0) return write_OFF_BGL(os, sm, np.face_color_map(fcolors)); else return write_OFF_BGL(os, sm, np); @@ -363,11 +361,9 @@ bool write_OFF_with_or_without_vtextures(std::ostream& os, const bool has_vtextures = !(is_default_parameter::value); - typename Mesh::template Property_map vtextures; - bool has_internal_vtextures; - std::tie(vtextures, has_internal_vtextures) = sm.template property_map("v:texcoord"); + auto [vtextures, has_internal_vtextures] = sm.template property_map("v:texcoord"); - if(!has_vtextures && has_internal_vtextures && std::distance(vtextures.begin(), vtextures.end()) > 0) + if(!has_vtextures && has_internal_vtextures && vtextures.size() > 0) return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures)); else return write_OFF_with_or_without_fcolors(os, sm, np); @@ -386,11 +382,9 @@ bool write_OFF_with_or_without_vcolors(std::ostream& os, const bool has_vcolors = !(is_default_parameter::value); - typename Mesh::template Property_map vcolors; - bool has_internal_vcolors; - std::tie(vcolors, has_internal_vcolors) = sm.template property_map("v:color"); + auto [vcolors, has_internal_vcolors] = sm.template property_map("v:color"); - if(!has_vcolors && has_internal_vcolors && std::distance(vcolors.begin(), vcolors.end()) > 0) + if(!has_vcolors && has_internal_vcolors && vcolors.size() > 0) return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors)); else return write_OFF_with_or_without_vtextures(os, sm, np); @@ -411,11 +405,9 @@ bool write_OFF_with_or_without_vnormals(std::ostream& os, const bool has_vnormals = !(is_default_parameter::value); - typename Mesh::template Property_map vnormals; - bool has_internal_vnormals; - std::tie(vnormals, has_internal_vnormals) = sm.template property_map("v:normal"); + auto [vnormals, has_internal_vnormals] = sm.template property_map("v:normal"); - if(!has_vnormals && has_internal_vnormals && std::distance(vnormals.begin(), vnormals.end()) > 0) + if(!has_vnormals && has_internal_vnormals && vnormals.size() > 0) return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals)); else return write_OFF_with_or_without_vcolors(os, sm, np); From b8323f8cc03f972a26289582a8bfaaa0f2a56378 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 25 May 2023 11:09:45 +0200 Subject: [PATCH 048/520] Re-add num_*(), add const get_property_container --- .../include/CGAL/Surface_mesh/Surface_mesh.h | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index e49684a81295..d9ea94793f7a 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -1074,6 +1074,21 @@ class Surface_mesh /// allocated for elements, and to clear the structure. ///@{ +#ifndef DOXYGEN_RUNNING + /// returns the number of used and removed vertices in the mesh. + size_type num_vertices() const { return (size_type) vprops_.size(); } + + /// returns the number of used and removed halfedges in the mesh. + size_type num_halfedges() const { return (size_type) hprops_.size(); } + + /// returns the number of used and removed edges in the mesh. + size_type num_edges() const { return (size_type) eprops_.size(); } + + /// returns the number of used and removed faces in the mesh. + size_type num_faces() const { return (size_type) fprops_.size(); } + +#endif + /// returns the number of vertices in the mesh. size_type number_of_vertices() const { return vprops_.size(); @@ -1808,6 +1823,18 @@ class Surface_mesh return fprops_; } + template + const Property_container& get_property_container() const { + if constexpr (std::is_same_v) + return vprops_; + if constexpr (std::is_same_v) + return hprops_; + if constexpr (std::is_same_v) + return eprops_; + if constexpr (std::is_same_v) + return fprops_; + } + public: From 6c67aba15b4121f35c7275487eaa5553888c815f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 25 May 2023 11:10:24 +0200 Subject: [PATCH 049/520] Update PLY file loading to support the new interface --- Property_map/include/CGAL/Properties.h | 13 +++- .../include/CGAL/Surface_mesh/IO/PLY.h | 60 ++++++++----------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 47c0c6041fd2..94e7dd729d17 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -102,8 +102,11 @@ class Property_array : public Property_array_base { public: - // todo: there's not really a good reason to use this, maybe it should be removed - std::size_t capacity() const { return m_data.size(); } + // todo: there's not really a good reason to use these, maybe they should be removed + + [[nodiscard]] std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } + + [[nodiscard]] std::size_t capacity() const { return m_data.size(); } const_reference operator[](Index i) const { CGAL_precondition(std::size_t(i) < m_data.size()); @@ -142,6 +145,12 @@ class Property_array_handle { Property_array_handle(Property_array& array) : m_array(array) {} + //Property_array_handle(Property_array_handle& handle) : m_array(handle.m_array) {} + + [[nodiscard]] std::size_t size() const { return m_array.size(); } + + [[nodiscard]] std::size_t capacity() const { return m_array.capacity(); } + const_reference operator[](Index i) const { return m_array[i]; } reference operator[](Index i) { return m_array[i]; } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h index 8e274941fc1a..74830af23ada 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h @@ -57,10 +57,7 @@ class Surface_mesh_filler public: PLY_property_to_surface_mesh_property(Surface_mesh& sm, const std::string& name) - : m_name(name) - { - m_map = sm.template add_property_map(prefix(Simplex()) + name).first; - } + : m_name(name), m_map(sm.template add_property_map(prefix(Simplex()) + name).first){} virtual void assign(PLY_element& element, size_type index) { @@ -79,11 +76,11 @@ class Surface_mesh_filler std::vector m_map_v2v; bool m_use_floats; int m_normals; - typename Surface_mesh::template Property_map m_normal_map; + std::optional> m_normal_map; int m_vcolors; - typename Surface_mesh::template Property_map m_vcolor_map; + std::optional> m_vcolor_map; int m_fcolors; - typename Surface_mesh::template Property_map m_fcolor_map; + std::optional> m_fcolor_map; bool m_use_int32_t; std::string m_index_tag; std::vector m_vertex_properties; @@ -125,7 +122,7 @@ class Surface_mesh_filler { ++ m_normals; if(m_normals == 3) - m_normal_map = m_mesh.template add_property_map("v:normal").first; + m_normal_map.emplace(m_mesh.template add_property_map("v:normal").first); return true; } if(name == "red" || @@ -134,7 +131,7 @@ class Surface_mesh_filler { ++ m_vcolors; if(m_vcolors == 3) - m_vcolor_map = m_mesh.template add_property_map("v:color").first; + m_vcolor_map.emplace(m_mesh.template add_property_map("v:color").first); return true; } return false; @@ -157,7 +154,7 @@ class Surface_mesh_filler { ++ m_fcolors; if(m_fcolors == 3) - m_fcolor_map = m_mesh.template add_property_map("f:color").first; + m_fcolor_map.emplace(m_mesh.template add_property_map("f:color").first); return true; } @@ -284,7 +281,7 @@ class Surface_mesh_filler element.assign(ny, "ny"); element.assign(nz, "nz"); Vector normal(nx, ny, nz); - m_normal_map[vi] = normal; + (*m_normal_map)[vi] = normal; } if(m_vcolors == 3) @@ -305,7 +302,7 @@ class Surface_mesh_filler g = static_cast(std::floor(gf*255)); b = static_cast(std::floor(bf*255)); } - m_vcolor_map[vi] = CGAL::IO::Color(r, g, b); + (*m_vcolor_map)[vi] = CGAL::IO::Color(r, g, b); } } @@ -359,7 +356,7 @@ class Surface_mesh_filler g = static_cast(std::floor(gf*255)); b = static_cast(std::floor(bf*255)); } - m_fcolor_map[fi] = CGAL::IO::Color(r, g, b); + (*m_fcolor_map)[fi] = CGAL::IO::Color(r, g, b); } } @@ -658,19 +655,22 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, internal_np::vertex_color_map_t, CGAL_NP_CLASS, typename Surface_mesh::template Property_map >::type; + // typedef typename internal_np::Lookup_named_param_def< + // internal_np::vertex_color_map_t, CGAL_NP_CLASS, + // Constant_property_map >::type VCM; using parameters::choose_parameter; using parameters::is_default_parameter; using parameters::get_parameter; - VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map), VCM()); + VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map)); bool has_vcolor = !is_default_parameter::value; using FCM = typename internal_np::Lookup_named_param_def< internal_np::face_color_map_t, CGAL_NP_CLASS, typename Surface_mesh::template Property_map >::type; - FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map), FCM()); + FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map)); bool has_fcolor = !is_default_parameter::value; std::vector prop = sm.template properties(); @@ -710,8 +710,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, bool okay = false; { - Int8_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property char " << name << std::endl; @@ -720,8 +719,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Uint8_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property uchar " << name << std::endl; @@ -730,8 +728,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Int16_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property short " << name << std::endl; @@ -740,8 +737,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Uint16_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property ushort " << name << std::endl; @@ -750,8 +746,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Int32_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property int " << name << std::endl; @@ -760,8 +755,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Uint32_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property uint " << name << std::endl; @@ -770,8 +764,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Int64_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property int " << name << std::endl; @@ -780,8 +773,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Uint64_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property uint " << name << std::endl; @@ -790,8 +782,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Float_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property float " << name << std::endl; @@ -800,8 +791,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - Double_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop[i]); + auto [pmap, okay] = sm.template property_map(prop[i]); if(okay) { os << "property double " << name << std::endl; From 79c29db1fcfe8559e7f8245eed982fb23dbf3e2f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 25 May 2023 12:09:43 +0200 Subject: [PATCH 050/520] Fix issues with PLY loading, property_map() now returns an optional<> --- Property_map/include/CGAL/Properties.h | 9 ++ .../include/CGAL/Surface_mesh/IO/OFF.h | 32 ++--- .../include/CGAL/Surface_mesh/IO/PLY.h | 114 ++++++++---------- .../include/CGAL/Surface_mesh/Surface_mesh.h | 18 ++- .../test/Surface_mesh/sm_open_colored_off.cpp | 4 +- Surface_mesh/test/Surface_mesh/sm_ply_io.cpp | 2 + Surface_mesh/test/Surface_mesh/sm_remove.cpp | 24 ++-- 7 files changed, 106 insertions(+), 97 deletions(-) diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 94e7dd729d17..bbcb00959bb9 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -273,6 +273,15 @@ class Property_container { return dynamic_cast&>(*m_property_arrays.at(name)); } + template + std::optional>> get_property_if_exists(const std::string& name) { + auto it = m_property_arrays.find(name); + if (it == m_property_arrays.end()) return {}; + auto [_, array] = *it; + if (typeid(*array) != typeid(Property_array)) return {}; + return dynamic_cast&>(*m_property_arrays.at(name)); + } + /*! * Removes a property array from the container * diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h index 583a25e3eb39..f746b55a2ceb 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h @@ -110,7 +110,7 @@ bool read_OFF_with_or_without_fcolors(std::istream& is, bool is_fcm_requested = !(is_default_parameter::value); if (is_fcm_requested || scanner.has_colors()) { - auto [fcm, created] = sm.template property_map("f:color"); + auto [fcm, created] = sm.template add_property_map("f:color"); return CGAL::IO::internal::read_OFF_BGL(is, sm, np.face_color_map(fcm)); } else { return CGAL::IO::internal::read_OFF_BGL(is, sm, np); @@ -136,7 +136,7 @@ bool read_OFF_with_or_without_vtextures(std::istream& is, bool is_vtm_requested = !(is_default_parameter::value); if (is_vtm_requested || scanner.has_textures()) { - auto [vtm, created] = sm.template property_map("v:texcoord"); + auto [vtm, created] = sm.template add_property_map("v:texcoord"); return read_OFF_with_or_without_fcolors(is, sm, scanner, np.vertex_texture_map(vtm)); } else { return read_OFF_with_or_without_fcolors(is, sm, scanner, np); @@ -161,7 +161,7 @@ bool read_OFF_with_or_without_vcolors(std::istream& is, bool is_vcm_requested = !(is_default_parameter::value); if (is_vcm_requested || scanner.has_colors()) { - auto [vcm, created] = sm.template property_map("v:color"); + auto [vcm, created] = sm.template add_property_map("v:color"); return read_OFF_with_or_without_vtextures(is, sm, scanner, np.vertex_color_map(vcm)); } else { return read_OFF_with_or_without_vtextures(is, sm, scanner, np); @@ -187,7 +187,7 @@ bool read_OFF_with_or_without_vnormals(std::istream& is, bool is_vnm_requested = !(is_default_parameter::value); if (is_vnm_requested || scanner.has_normals()) { - auto [vnm, created] = sm.template property_map("v:normal"); + auto [vnm, created] = sm.template add_property_map("v:normal"); return read_OFF_with_or_without_vcolors(is, sm, scanner, np.vertex_normal_map(vnm)); } else { return read_OFF_with_or_without_vcolors(is, sm, scanner, np); @@ -338,10 +338,10 @@ bool write_OFF_with_or_without_fcolors(std::ostream& os, const bool has_fcolors = !(is_default_parameter::value); - auto [fcolors, has_internal_fcolors] = sm.template property_map("f:color"); + auto fcolors = sm.template property_map("f:color"); - if(!has_fcolors && has_internal_fcolors && fcolors.size() > 0) - return write_OFF_BGL(os, sm, np.face_color_map(fcolors)); + if(!has_fcolors && fcolors && fcolors->size() > 0) + return write_OFF_BGL(os, sm, np.face_color_map(fcolors.value())); else return write_OFF_BGL(os, sm, np); } @@ -361,10 +361,10 @@ bool write_OFF_with_or_without_vtextures(std::ostream& os, const bool has_vtextures = !(is_default_parameter::value); - auto [vtextures, has_internal_vtextures] = sm.template property_map("v:texcoord"); + auto vtextures = sm.template property_map("v:texcoord"); - if(!has_vtextures && has_internal_vtextures && vtextures.size() > 0) - return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures)); + if(!has_vtextures && vtextures && vtextures->size() > 0) + return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures.value())); else return write_OFF_with_or_without_fcolors(os, sm, np); } @@ -382,10 +382,10 @@ bool write_OFF_with_or_without_vcolors(std::ostream& os, const bool has_vcolors = !(is_default_parameter::value); - auto [vcolors, has_internal_vcolors] = sm.template property_map("v:color"); + auto vcolors = sm.template property_map("v:color"); - if(!has_vcolors && has_internal_vcolors && vcolors.size() > 0) - return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors)); + if(!has_vcolors && vcolors && vcolors->size() > 0) + return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors.value())); else return write_OFF_with_or_without_vtextures(os, sm, np); } @@ -405,10 +405,10 @@ bool write_OFF_with_or_without_vnormals(std::ostream& os, const bool has_vnormals = !(is_default_parameter::value); - auto [vnormals, has_internal_vnormals] = sm.template property_map("v:normal"); + auto vnormals = sm.template property_map("v:normal"); - if(!has_vnormals && has_internal_vnormals && vnormals.size() > 0) - return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals)); + if(!has_vnormals && vnormals && vnormals->size() > 0) + return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals.value())); else return write_OFF_with_or_without_vcolors(os, sm, np); } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h index 74830af23ada..4f1f50dde989 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h @@ -458,12 +458,10 @@ bool fill_simplex_specific_header(std::ostream& os, return true; } - bool okay = false; if(prop == "v:normal") { - Vector_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop); - if(okay) + auto pmap = sm.template property_map(prop); + if(pmap) { if(std::is_same::value) { @@ -477,23 +475,22 @@ bool fill_simplex_specific_header(std::ostream& os, << "property double ny" << std::endl << "property double nz" << std::endl; } - printers.push_back(new Property_printer(pmap)); + printers.push_back(new Property_printer(pmap.value())); return true; } } if(prop == "v:color") { - Vcolor_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop); - if(okay) + auto pmap = sm.template property_map(prop); + if(pmap) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; - printers.push_back(new Property_printer(pmap)); + printers.push_back(new Property_printer(pmap.value())); return true; } } @@ -515,19 +512,17 @@ bool fill_simplex_specific_header(std::ostream& os, if(prop == "f:connectivity" || prop == "f:removed") return true; - bool okay = false; if(prop == "f:color") { - Fcolor_map pmap; - boost::tie(pmap, okay) = sm.template property_map(prop); - if(okay) + auto pmap = sm.template property_map(prop); + if(pmap) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; - printers.push_back(new Property_printer(pmap)); + printers.push_back(new Property_printer(pmap.value())); return true; } } @@ -652,44 +647,40 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, typedef typename SMesh::Vertex_index VIndex; using VCM = typename internal_np::Lookup_named_param_def< - internal_np::vertex_color_map_t, - CGAL_NP_CLASS, - typename Surface_mesh::template Property_map >::type; - // typedef typename internal_np::Lookup_named_param_def< - // internal_np::vertex_color_map_t, CGAL_NP_CLASS, - // Constant_property_map >::type VCM; + internal_np::vertex_color_map_t, CGAL_NP_CLASS, + typename Surface_mesh::template Property_map + >::type; + using FCM = typename internal_np::Lookup_named_param_def< + internal_np::face_color_map_t, CGAL_NP_CLASS, + typename Surface_mesh::template Property_map + >::type; using parameters::choose_parameter; using parameters::is_default_parameter; using parameters::get_parameter; - VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map)); - bool has_vcolor = !is_default_parameter::value; - - using FCM = typename internal_np::Lookup_named_param_def< - internal_np::face_color_map_t, - CGAL_NP_CLASS, - typename Surface_mesh::template Property_map >::type; - FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map)); - bool has_fcolor = !is_default_parameter::value; + constexpr bool has_vcolor = !is_default_parameter::value; + constexpr bool has_fcolor = !is_default_parameter::value; std::vector prop = sm.template properties(); - if (std::is_same::value && has_fcolor) { + if constexpr (std::is_same::value && has_fcolor) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; + FCM fcm = get_parameter(np, internal_np::face_color_map); add_color_map()(printers, fcm); } - if (std::is_same::value && has_vcolor) + if constexpr (std::is_same::value && has_vcolor) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; + VCM vcm = get_parameter(np, internal_np::vertex_color_map); add_color_map()(printers, vcm); } @@ -708,94 +699,93 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, // Cut the "v:" prefix std::string name = get_property_raw_name(prop[i], Simplex()); - bool okay = false; { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property char " << name << std::endl; - printers.push_back(new internal::Char_property_printer(pmap)); + printers.push_back(new internal::Char_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property uchar " << name << std::endl; - printers.push_back(new internal::Char_property_printer(pmap)); + printers.push_back(new internal::Char_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property short " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property ushort " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property int " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property uint " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property int " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property uint " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property float " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - auto [pmap, okay] = sm.template property_map(prop[i]); - if(okay) + auto pmap = sm.template property_map(prop[i]); + if(pmap) { os << "property double " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index d9ea94793f7a..5cea7e1a7446 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -1881,13 +1881,21 @@ class Surface_mesh } /// returns a property map named `name` with key type `I` and value type `T`, - /// and a Boolean that is `true` if the property was created. + /// if such a property map exists template - std::pair, bool> + std::optional> + property_map(const std::string& name) { + auto maybe_property_map = get_property_container().template get_property_if_exists(name); + if (!maybe_property_map) return {}; + else return {{maybe_property_map.value()}}; + } + + template + std::optional> property_map(const std::string& name) const { - auto [array, created] = - const_cast*>(this)->get_property_container().template get_or_add_property(name); - return {{array.get()}, created}; + auto maybe_property_map = const_cast*>(this)->get_property_container().template get_property_if_exists(name); + if (!maybe_property_map) return {}; + else return {{maybe_property_map.value()}}; } diff --git a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp index 0c9987634202..ce25271e1898 100644 --- a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp @@ -33,8 +33,8 @@ void OpenOFF(int i) assert(in && !surface_mesh.is_empty()); - auto [fcolors, created_fcolors] = surface_mesh.property_map("f:color"); - auto [vcolors, created_vcolors] = surface_mesh.property_map("v:color"); + auto [fcolors, created_fcolors] = surface_mesh.add_property_map("f:color"); + auto [vcolors, created_vcolors] = surface_mesh.add_property_map("v:color"); // Both color maps should have already existed, because they were loaded from the file assert(!created_fcolors); diff --git a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp index f2889f912bd5..c3524a2076b3 100644 --- a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp @@ -15,6 +15,7 @@ typedef boost::graph_traits::face_descriptor face_descriptor; int main() { std::ifstream in(CGAL::data_file_path("meshes/colored_tetra.ply")); + assert(in.is_open()); SMesh mesh; CGAL::IO::read_PLY(in, mesh); @@ -37,6 +38,7 @@ int main() // Append second mesh std::ifstream in2("tetra.ply"); + assert(in2.is_open()); CGAL::IO::read_PLY(in2, mesh); std::ofstream out("out.ply"); diff --git a/Surface_mesh/test/Surface_mesh/sm_remove.cpp b/Surface_mesh/test/Surface_mesh/sm_remove.cpp index 48e1301433fc..6744fbf77b2f 100644 --- a/Surface_mesh/test/Surface_mesh/sm_remove.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_remove.cpp @@ -47,10 +47,10 @@ int main() // make sure all is OK when clearing the mesh - auto vconn = m.property_map("v:connectivity").first; - auto hconn = m.property_map("h:connectivity").first; - auto fconn = m.property_map("f:connectivity").first; - auto vpoint = m.property_map("v:point").first; + auto vconn = m.add_property_map("v:connectivity").first; + auto hconn = m.add_property_map("h:connectivity").first; + auto fconn = m.add_property_map("f:connectivity").first; + auto vpoint = m.add_property_map("v:point").first; // first call to squat the first available position m.add_property_map("vprop_dummy"); @@ -71,10 +71,10 @@ int main() auto l_fprop = m.add_property_map("fprop").first; auto l_eprop = m.add_property_map("eprop").first; - auto l_vconn = m.property_map("v:connectivity").first; - auto l_hconn = m.property_map("h:connectivity").first; - auto l_fconn = m.property_map("f:connectivity").first; - auto l_vpoint = m.property_map("v:point").first; + auto l_vconn = m.add_property_map("v:connectivity").first; + auto l_hconn = m.add_property_map("h:connectivity").first; + auto l_fconn = m.add_property_map("f:connectivity").first; + auto l_vpoint = m.add_property_map("v:point").first; assert( vconn == l_vconn ); assert( hconn == l_hconn ); @@ -89,10 +89,10 @@ int main() { m.clear(); - auto l_vconn = m.property_map("v:connectivity").first; - auto l_hconn = m.property_map("h:connectivity").first; - auto l_fconn = m.property_map("f:connectivity").first; - auto l_vpoint = m.property_map("v:point").first; + auto l_vconn = m.add_property_map("v:connectivity").first; + auto l_hconn = m.add_property_map("h:connectivity").first; + auto l_fconn = m.add_property_map("f:connectivity").first; + auto l_vpoint = m.add_property_map("v:point").first; assert( vconn == l_vconn ); assert( hconn == l_hconn ); From 6e3db21299de00a9db96b6f5bd21abc885069925 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 1 Jun 2023 11:25:44 +0200 Subject: [PATCH 051/520] Add iterators & convenience function for manipulating property list --- Property_map/include/CGAL/Properties.h | 72 ++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index bbcb00959bb9..29f5efd95e39 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -42,6 +42,8 @@ class Property_array_base { /*! * \brief Indexed storage for arbitrary types * + * todo: make this effectively private, prioritize the use of Property_array_handle + * * @tparam T */ template @@ -56,6 +58,8 @@ class Property_array : public Property_array_base { using value_type = T; using reference = typename std::vector::reference; using const_reference = typename std::vector::const_reference; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; Property_array(const std::vector& active_indices, const T& default_value) : m_data(), m_active_indices(active_indices), m_default_value(default_value) { @@ -118,6 +122,14 @@ class Property_array : public Property_array_base { return m_data[std::size_t(i)]; } + iterator begin() { return m_data.begin(); } + + iterator end() { return m_data.end(); } + + const_iterator begin() const { return m_data.begin(); } + + const_iterator end() const { return m_data.end(); } + public: bool operator==(const Property_array& other) const { @@ -128,11 +140,11 @@ class Property_array : public Property_array_base { }; - +// todo: add const/read-only handle template class Property_array_handle { - Property_array& m_array; + std::reference_wrapper> m_array; public: @@ -143,19 +155,32 @@ class Property_array_handle { using const_reference = typename std::vector::const_reference; using category = boost::lvalue_property_map_tag; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + Property_array_handle(Property_array& array) : m_array(array) {} - //Property_array_handle(Property_array_handle& handle) : m_array(handle.m_array) {} + [[nodiscard]] std::size_t size() const { return m_array.get().size(); } - [[nodiscard]] std::size_t size() const { return m_array.size(); } + [[nodiscard]] std::size_t capacity() const { return m_array.get().capacity(); } - [[nodiscard]] std::size_t capacity() const { return m_array.capacity(); } + Property_array& array() const { return m_array.get(); } - const_reference operator[](Index i) const { return m_array[i]; } + // todo: This might not be needed, if the other operator[] is made const + const_reference operator[](Index i) const { return m_array.get()[i]; } - reference operator[](Index i) { return m_array[i]; } + reference operator[](Index i) { return m_array.get()[i]; } - bool operator==(const Property_array_handle& other) const { return other.m_array == m_array; } + // todo: maybe these can be const, in an lvalue property map? + iterator begin() { return m_array.get().begin(); } + + iterator end() { return m_array.get().end(); } + + const_iterator begin() const { return m_array.get().begin(); } + + const_iterator end() const { return m_array.get().end(); } + + bool operator==(const Property_array_handle& other) const { return other.m_array.get() == m_array.get(); } bool operator!=(const Property_array_handle& other) const { return !operator==(other); } @@ -282,6 +307,15 @@ class Property_container { return dynamic_cast&>(*m_property_arrays.at(name)); } + template + bool property_exists(const std::string& name) const { + auto it = m_property_arrays.find(name); + if (it == m_property_arrays.end()) return false; + auto [_, array] = *it; + if (typeid(*array) != typeid(Property_array)) return false; + return true; + } + /*! * Removes a property array from the container * @@ -290,6 +324,18 @@ class Property_container { */ bool remove_property(const std::string& name) { return m_property_arrays.erase(name) == 1; } + template + bool remove_property(const Property_array& arrayToRemove) { + for (auto it = m_property_arrays.begin(); it != m_property_arrays.end(); ++it) { + auto const& [name, array] = *it; + if (array.get() == dynamic_cast*>(&arrayToRemove)) { + m_property_arrays.erase(it); + return true; + } + } + return false; + } + void remove_all_properties_except(const std::vector& preserved_names) { // todo: if this is used often, it should take a parameter pack instead of a vector // A fold expression could then be used in place of std::find for better performance @@ -304,12 +350,12 @@ class Property_container { std::vector properties() const { std::vector property_names{}; - for (auto const&[name, _] : m_property_arrays) + for (auto const& [name, _]: m_property_arrays) property_names.emplace_back(name); return property_names; } - std::size_t num_properties() const{ return m_property_arrays.size(); } + std::size_t num_properties() const { return m_property_arrays.size(); } const std::type_info& property_type(const std::string& name) const { if (auto it = m_property_arrays.find(name); it != m_property_arrays.end()) @@ -427,6 +473,10 @@ class Property_container { return m_active_indices[i] = true; } + void mark_inactive(Index i) { + return m_active_indices[i] = false; + } + std::vector active_list() const { std::vector indices; for (std::size_t i = 0; i < m_active_indices.size(); ++i) @@ -464,6 +514,8 @@ class Property_container { array->reserve(m_active_indices.size()); } } + + // todo: maybe a compress() method? }; } From 82dd686f4b1a78afc6810a4d71313b7844d69c66 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 6 Jun 2023 21:44:37 +0200 Subject: [PATCH 052/520] Adapted Point_set_3 to use the new property system Garbage management is still done by the point set --- Point_set_3/include/CGAL/Point_set_3.h | 162 ++++++++++-------- Point_set_3/include/CGAL/Point_set_3/IO/PLY.h | 77 ++++----- Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h | 2 +- .../test/Point_set_3/point_set_test.cpp | 21 +-- .../test/Point_set_3/point_set_test_join.cpp | 13 +- Property_map/include/CGAL/Properties.h | 59 ++++++- 6 files changed, 191 insertions(+), 143 deletions(-) diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 2d0f2083dcc5..5e64fcb58dfb 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -17,7 +17,7 @@ #include -#include +#include #include @@ -53,11 +53,7 @@ namespace internal { typedef CGAL::Point_set_3 Point_set_3; private: - friend class CGAL::Point_set_3; - friend class Properties::Property_container; - template friend class Properties::Property_array; - template friend struct Property_map; - friend class std::vector; + size_type value; public: @@ -130,18 +126,14 @@ class Point_set_3 using Index = internal::Point_set_3_index; - typedef typename Properties::Property_container Base; + typedef typename Properties::Property_container Base; template - struct Property_map - : public Properties::Property_map_base > - { - typedef Properties::Property_map_base > Base; - Property_map() : Base() {} - Property_map(const Base& pm): Base(pm) {} - }; + using Property_map = Properties::Property_array_handle; + typedef Property_map Index_map; + // todo: apparently unused? template struct Get_property_map { typedef Property_map type; @@ -173,6 +165,7 @@ class Point_set_3 typedef Property_map Vector_map; ///< Property map of vectors /// \cond SKIP_IN_MANUAL + // todo: CGAL should probably have a general "span" type template class Property_range { @@ -186,18 +179,17 @@ class Point_set_3 private: const_iterator m_begin; const_iterator m_end; + // todo: couldn't this be replaced by std::distance(begin, end)? std::size_t m_size; public: Property_range (const Property_map& pmap, typename Point_set::const_iterator begin, typename Point_set::const_iterator end, - std::size_t size) - { - m_begin = boost::make_transform_iterator (begin, Unary_function(pmap)); - m_end = boost::make_transform_iterator (end, Unary_function(pmap)); - m_size = size; - } + std::size_t size) : + m_begin(boost::make_transform_iterator (begin, Unary_function(pmap))), + m_end(boost::make_transform_iterator (end, Unary_function(pmap))), + m_size(size) {} const_iterator begin() const { return m_begin; } const_iterator end() const { return m_end; } @@ -212,11 +204,17 @@ class Point_set_3 protected: /// \cond SKIP_IN_MANUAL - Base m_base; + // todo: this shouldn't be necessary + mutable Base m_base; Index_map m_indices; Point_map m_points; - Vector_map m_normals; - std::size_t m_nb_removed; + std::optional m_normals{}; + + // todo: this is redundant! + // Property_container has its own garbage management, but it's not contiguous + // It should eventually be replaced with something like a Contiguous_property_container + // which places deleted elements at the end + std::size_t m_nb_removed = 0; /// \endcond public: @@ -232,9 +230,11 @@ class Point_set_3 added. If `false` (default value), the normal map can still be added later on (see `add_normal_map()`). */ - Point_set_3 (bool with_normal_map = false) : m_base() - { - clear(); + Point_set_3(bool with_normal_map = false) : + m_base(), + m_indices(m_base.template add_property("index", typename Index::size_type(-1))), + m_points(m_base.template add_property("point", CGAL::ORIGIN)) { + if (with_normal_map) add_normal_map(); } @@ -244,6 +244,7 @@ class Point_set_3 */ Point_set_3& operator= (const Point_set_3& ps) { + // todo: should be implemented via the copy-swap idiom m_base = ps.m_base; m_indices = this->property_map ("index").first; m_points = this->property_map ("point").first; @@ -256,6 +257,7 @@ class Point_set_3 // copy constructor (same as assignment) Point_set_3 (const Point_set_3& ps) { + // todo: update to use new invalidation-avoiding copy behavior m_base = ps.m_base; m_indices = this->property_map ("index").first; m_points = this->property_map ("point").first; @@ -268,6 +270,7 @@ class Point_set_3 /// \cond SKIP_IN_MANUAL const Base& base() const { return m_base; } + Base& base() { return m_base; } /// \endcond @@ -297,7 +300,7 @@ class Point_set_3 \note The method `size()` is also available (see `Range`) and does the same thing. */ - std::size_t number_of_points () const { return m_base.size() - m_nb_removed; } + std::size_t number_of_points () const { return m_base.capacity() - m_nb_removed; } /// \cond SKIP_IN_MANUAL std::size_t size () const { return number_of_points(); } /// \endcond @@ -312,6 +315,8 @@ class Point_set_3 the point set and `other`. Property maps which are only in `other` are ignored. + todo: this seems backwards to me, isn't it clearer to have an append() function? + \note If `copy_properties()` with `other` as argument is called before calling this method, then all the content of `other` will be copied and no property will be lost in the process. @@ -322,8 +327,7 @@ class Point_set_3 { collect_garbage(); other.collect_garbage(); - resize (number_of_points() + other.number_of_points()); - m_base.transfer (other.m_base); + other.m_base.append(m_base); // Reset indices for (std::size_t i = 0; i < this->m_base.size(); ++ i) @@ -341,9 +345,8 @@ class Point_set_3 */ void clear() { - m_base.clear(); - m_indices = this->add_property_map("index", typename Index::size_type(-1)).first; - m_points = this->add_property_map("point", CGAL::ORIGIN).first; + m_base.reserve(0); + m_base.remove_all_properties_except({"index", "point"}); m_nb_removed = 0; } @@ -355,14 +358,8 @@ class Point_set_3 */ void clear_properties() { - Base other; - other.template add("index", typename Index::size_type(-1)); - other.template add("point", CGAL::ORIGIN); - other.resize(m_base.size()); - other.transfer(m_base); - m_base.swap(other); - m_indices = this->property_map("index").first; - m_points = this->property_map("point").first; + // todo: The old version was pretty convoluted, but I'm pretty sure this is the intended behavior + m_base.remove_all_properties_except({"index", "point"}); } /*! @@ -374,7 +371,7 @@ class Point_set_3 \note This method does not change the content of the point set and is only used for optimization. */ - void reserve (std::size_t s) { m_base.reserve (s); } + void reserve (std::size_t s) { m_base.reserve(s); } /*! \brief changes size of the point set. @@ -431,7 +428,8 @@ class Point_set_3 { if (m_nb_removed == 0) { - m_base.push_back(); + auto new_index = m_base.emplace_back(); + CGAL_assertion(std::size_t(new_index) == size() - 1); m_indices[size()-1] = size()-1; return m_indices.end() - 1; } @@ -505,6 +503,8 @@ class Point_set_3 method allows the user to easily copy one point (along with the values of all its properties) from one point set to another. + todo: this must have been terribly slow, and the new implementation isn't any better. + \param other Point set to which the point to copy belongs \param idx Index of the point to copy in `other` @@ -585,7 +585,7 @@ class Point_set_3 \note The elements are just marked as removed and are not erased from the memory. `collect_garbage()` should be called if the - memory needs to be disallocated. Elements can be recovered with + memory needs to be deallocated. Elements can be recovered with `cancel_removals()`. \note All iterators, pointers and references related to the @@ -606,6 +606,7 @@ class Point_set_3 while (source != last // All elements have been moved && dest != last - 1) // All elements are at the end of the container { + // todo: Why is this writing to cerr? std::cerr << "Swapping " << *source << " and " << *dest << std::endl; std::swap (*(source ++), *(dest --)); } @@ -632,6 +633,7 @@ class Point_set_3 */ void remove (iterator it) { + // todo: Maybe some form of erase-by-iterator should exist? std::iter_swap (it, (end() - 1)); ++ m_nb_removed; } @@ -730,7 +732,7 @@ class Point_set_3 // Sorting based on the indices reorders the point set correctly quick_sort_on_indices ((std::ptrdiff_t)0, (std::ptrdiff_t)(m_base.size() - 1)); - m_base.resize (size ()); + m_base.reserve (size ()); m_base.shrink_to_fit (); m_nb_removed = 0; } @@ -784,11 +786,8 @@ class Point_set_3 \param name Name of the property. */ template - bool has_property_map (const std::string& name) const - { - std::pair, bool> - pm = m_base.template get (name); - return pm.second; + bool has_property_map (const std::string& name) const { + return m_base.template property_exists(name); } /*! @@ -808,10 +807,8 @@ class Point_set_3 std::pair, bool> add_property_map (const std::string& name, const T t=T()) { - Property_map pm; - bool added = false; - std::tie (pm, added) = m_base.template add (name, t); - return std::make_pair (pm, added); + auto [array, created] = m_base.template get_or_add_property(name, t); + return {{array.get()}, created}; } /*! @@ -821,18 +818,26 @@ class Point_set_3 \param name Name of the property. - \return Returns a pair containing: the specified property map and a - Boolean set to `true` or an empty property map and a Boolean set - to `false` (if the property was not found). + \return Returns an optional containing: the specified property map + or an empty property map (if the property was not found). */ template - std::pair,bool> + std::optional> + property_map (const std::string& name) + { + auto maybe_property_map = m_base.template get_property_if_exists(name); + if (!maybe_property_map) return {}; + else return {{maybe_property_map.value()}}; + } + + // todo: The const version should return a Const_property_map type + template + std::optional> property_map (const std::string& name) const { - Property_map pm; - bool okay = false; - std::tie (pm, okay) = m_base.template get(name); - return std::make_pair (pm, okay); + auto maybe_property_map = m_base.template get_property_if_exists(name); + if (!maybe_property_map) return {}; + else return {{maybe_property_map.value()}}; } /*! @@ -848,7 +853,7 @@ class Point_set_3 template bool remove_property_map (Property_map& prop) { - return m_base.template remove (prop); + return m_base.remove_property(prop.array()); } /*! @@ -859,8 +864,7 @@ class Point_set_3 */ bool has_normal_map() const { - std::pair pm = this->property_map ("normal"); - return pm.second; + return m_base.template property_exists("normal"); } /*! \brief Convenience method that adds a normal property. @@ -874,9 +878,9 @@ class Point_set_3 */ std::pair add_normal_map (const Vector& default_value = CGAL::NULL_VECTOR) { - bool out = false; - std::tie (m_normals, out) = this->add_property_map ("normal", default_value); - return std::make_pair (m_normals, out); + auto pair = this->add_property_map ("normal", default_value); + m_normals = {pair.first}; + return pair; } /*! \brief returns the property map of the normal property. @@ -889,7 +893,7 @@ class Point_set_3 { if (!m_normals) add_normal_map(); - return m_normals; + return m_normals.value(); } /*! \brief returns the property map of the normal property (constant version). @@ -897,9 +901,10 @@ class Point_set_3 \note The normal property must have been added to the point set before calling this method (see `add_normal_map()`). */ - const Vector_map normal_map () const + const Vector_map normal_map () const // todo: This is not how const works { - return m_normals; + CGAL_precondition(m_normals.has_value()); + return m_normals.value(); } /*! \brief Convenience method that removes the normal property. @@ -909,7 +914,7 @@ class Point_set_3 */ bool remove_normal_map() { - return m_base.template remove (m_normals); + return m_base.remove_property("normal"); } /*! \brief returns the property map of the point property. @@ -938,7 +943,7 @@ class Point_set_3 { m_base.copy_properties (other.base()); - m_normals = this->property_map ("normal").first; // In case normal was added + m_normals = this->property_map ("normal"); // In case normal was added } @@ -964,7 +969,7 @@ class Point_set_3 std::vector > out; out.reserve (prop.size()); for (std::size_t i = 0; i < prop.size(); ++ i) - out.push_back (std::make_pair (prop[i], std::type_index(m_base.get_type(prop[i])))); + out.push_back (std::make_pair (prop[i], std::type_index(m_base.property_type(prop[i])))); return out; } @@ -1087,6 +1092,7 @@ class Point_set_3 #endif /// \cond SKIP_IN_MANUAL + // todo: these should probably be reconsidered. template class Property_back_inserter { @@ -1121,6 +1127,7 @@ class Point_set_3 }; + // todo: this should be provided by the Property system template class Push_property_map { @@ -1142,8 +1149,12 @@ class Point_set_3 friend void put(const Push_property_map& pm, Index& i, const value_type& t) { - if(pm.ps->size() <= (pm.ind)) + // todo: Why does this need to store ind? + auto s = pm.ps->size(); + if(pm.ps->size() <= (pm.ind)) { pm.ps->insert(); + pm.ind = pm.ps->size() - 1; + } put(*(pm.prop), pm.ind, t); i = pm.ind; ++pm.ind; @@ -1219,7 +1230,8 @@ class Point_set_3 */ Vector_push_map normal_push_map () { - return Vector_push_map (this, &m_normals, size()); + CGAL_precondition(m_normals.has_value()); + return Vector_push_map (this, &m_normals.value(), size()); } /*! \cgalAdvancedFunction diff --git a/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h b/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h index 9a9b7fcddb75..9e2901aef3a8 100644 --- a/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h +++ b/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h @@ -54,10 +54,9 @@ class Point_set_3_filler Pmap m_pmap; std::string m_name; public: - PLY_property_to_point_set_property(Point_set& ps, const std::string& name) - : m_name(name) - { - boost::tie(m_map, boost::tuples::ignore) = ps.add_property_map(name, Type()); + PLY_property_to_point_set_property(Point_set& ps, const std::string& name) : + m_name(name), + m_map(ps.add_property_map(name, Type()).first) { m_pmap = ps.push_property_map(m_map); } @@ -540,102 +539,92 @@ bool write_PLY(std::ostream& os, bool okay = false; { - Int8_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property char " << prop[i] << std::endl; - printers.push_back(new internal::Char_property_printer(pmap)); + printers.push_back(new internal::Char_property_printer(pmap.value())); continue; } } { - Uint8_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property uchar " << prop[i] << std::endl; - printers.push_back(new internal::Char_property_printer(pmap)); + printers.push_back(new internal::Char_property_printer(pmap.value())); continue; } } { - Int16_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property short " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Uint16_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property ushort " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Int32_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property int " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Uint32_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property uint " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Int64_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property int " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Uint64_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property uint " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Float_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property float " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } { - Double_map pmap; - boost::tie(pmap, okay) = point_set.template property_map(prop[i]); - if(okay) + auto pmap = point_set.template property_map(prop[i]); + if(pmap) { os << "property double " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap)); + printers.push_back(new internal::Simple_property_printer(pmap.value())); continue; } } diff --git a/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h b/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h index bdf76f541ebe..2d183776f947 100644 --- a/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h +++ b/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h @@ -57,7 +57,7 @@ bool read_XYZ(std::istream& is, bool has_normals = false; for(typename CGAL::Point_set_3::const_iterator it=point_set.begin(); it!=point_set.end(); ++it) { - if(point_set.normal(*it) != CGAL::NULL_VECTOR) + if(std::size_t(*it) < point_set.size() && point_set.normal(*it) != CGAL::NULL_VECTOR) { has_normals = true; break; diff --git a/Point_set_3/test/Point_set_3/point_set_test.cpp b/Point_set_3/test/Point_set_3/point_set_test.cpp index f436114762df..61808849bd0d 100644 --- a/Point_set_3/test/Point_set_3/point_set_test.cpp +++ b/Point_set_3/test/Point_set_3/point_set_test.cpp @@ -19,12 +19,15 @@ typedef std::array Color; std::size_t nb_test = 0; std::size_t nb_success = 0; +// todo: Automatically numbering the tests is unhelpful void test (bool expr, const char* msg) { ++ nb_test; - if (!expr) + if (!expr) { std::cerr << "Error on test " << nb_test << ": " << msg << std::endl; - else + // stop on fail, so it's easier to find the error (and so it shows up in CI) + exit(1); + } else ++ nb_success; } @@ -80,10 +83,8 @@ int main (int, char**) point_set.collect_garbage(); test (!(point_set.has_garbage()), "point set shouldn't have garbage."); - test (!(point_set.has_property_map ("color")), "point set shouldn't have colors."); - Point_set::Property_map color_prop; - bool garbage; - boost::tie (color_prop, garbage) = point_set.add_property_map ("color", Color()); + test (!(point_set.has_property_map("color")), "point set shouldn't have colors."); + auto [color_prop, garbage] = point_set.add_property_map ("color", Color()); test (point_set.has_property_map ("color"), "point set should have colors."); for (Point_set::iterator it = point_set.begin(); it != point_set.end(); ++ it) @@ -95,8 +96,7 @@ int main (int, char**) test ((get (color_prop, *it) == c), "recovered color is incorrect."); } - Point_set::Property_map color_prop_2; - boost::tie (color_prop_2, garbage) = point_set.property_map("color"); + auto color_prop_2 = point_set.property_map("color").value(); test ((color_prop_2 == color_prop), "color property not recovered correctly."); point_set.remove_normal_map (); @@ -114,12 +114,13 @@ int main (int, char**) for (const auto& p : pnt) std::cerr << " * " << p.first << " with type " << p.second.name() << std::endl; - test (point_set.base().n_properties() == 4, "point set should have 4 properties."); + // todo: was it okay to rename this to num_properties? + test (point_set.base().num_properties() == 4, "point set should have 4 properties."); Point p_before = *(point_set.points().begin()); point_set.clear_properties(); - test (point_set.base().n_properties() == 2, "point set should have 2 properties."); + test (point_set.base().num_properties() == 2, "point set should have 2 properties."); test (!(point_set.has_property_map("label")), "point set shouldn' have labels."); test (!(point_set.has_property_map("intensity")), "point set shouldn' have intensity."); test (!(point_set.empty()), "point set shouldn' be empty."); diff --git a/Point_set_3/test/Point_set_3/point_set_test_join.cpp b/Point_set_3/test/Point_set_3/point_set_test_join.cpp index d46066dba4f4..3764b4cde997 100644 --- a/Point_set_3/test/Point_set_3/point_set_test_join.cpp +++ b/Point_set_3/test/Point_set_3/point_set_test_join.cpp @@ -30,9 +30,7 @@ void test (bool expr, const char* msg) void print_point_set (const Point_set& ps, const char* msg) { - Point_set::Property_map intensity; - bool has_intensity; - boost::tie (intensity, has_intensity) = ps.property_map("intensity"); + auto intensity = ps.property_map("intensity"); std::cerr << msg << std::endl; for (Point_set::const_iterator it = ps.begin(); it != ps.end(); ++ it) @@ -40,8 +38,8 @@ void print_point_set (const Point_set& ps, const char* msg) std::cerr << *it << ": " << ps.point(*it); if (ps.has_normal_map()) std::cerr << ", normal " << ps.normal(*it); - if (has_intensity) - std::cerr << ", intensity " << intensity[*it]; + if (intensity.has_value()) + std::cerr << ", intensity " << intensity.value()[*it]; std::cerr << std::endl; } } @@ -70,10 +68,7 @@ int main (int, char**) Point_set ps3; ps3.add_normal_map(); - Point_set::Property_map intensity; - bool okay; - - boost::tie (intensity, okay) = ps3.add_property_map("intensity", 0); + auto [intensity, okay] = ps3.add_property_map("intensity", 0); assert (okay); Point_set::iterator it = ps3.insert (Point (double(0), double(1), double(2)), diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 29f5efd95e39..d969a8d4ff0f 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -21,7 +21,9 @@ class Property_array_base { virtual ~Property_array_base() = default; // Declare virtual functions here, for things which need to be done within the Property container - // todo: maybe these should be private, and made available using friend + // todo: these should mostly be private, and made available using friend + + virtual std::shared_ptr> empty_clone(const std::vector& active_indices) = 0; virtual std::shared_ptr> clone(const std::vector& active_indices) = 0; @@ -31,11 +33,15 @@ class Property_array_base { virtual void reserve(std::size_t n) = 0; + virtual void shrink_to_fit() = 0; + virtual void swap(Index a, Index b) = 0; virtual void reset(Index i) = 0; - virtual const std::type_info& type() = 0; + virtual const std::type_info& type() const = 0; + + virtual void transfer_from(const Property_array_base& other_base, Index other_index, Index this_index) = 0; }; @@ -68,6 +74,10 @@ class Property_array : public Property_array_base { m_data.resize(active_indices.size(), m_default_value); } + virtual std::shared_ptr> empty_clone(const std::vector& active_indices) override { + return std::make_shared>(active_indices, m_default_value); + } + virtual std::shared_ptr> clone(const std::vector& active_indices) override { auto new_array = std::make_shared>(active_indices, m_default_value); new_array->m_data = m_data; @@ -91,6 +101,10 @@ class Property_array : public Property_array_base { m_data.resize(n, m_default_value); }; + virtual void shrink_to_fit() override { + m_data.shrink_to_fit(); + } + virtual void swap(Index a, Index b) override { // todo: maybe cast to index, instead of casting index to size? CGAL_precondition(std::size_t(a) < m_data.size() && std::size_t(b) < m_data.size()); @@ -102,7 +116,16 @@ class Property_array : public Property_array_base { m_data[std::size_t(i)] = m_default_value; }; - virtual const std::type_info& type() override { return typeid(T); }; + virtual const std::type_info& type() const override { return typeid(T); }; + + virtual void transfer_from(const Property_array_base& other_base, + Index other_index, Index this_index) override { + + CGAL_precondition(other_base.type() == type()); + auto& other = dynamic_cast&>(other_base); + CGAL_precondition(std::size_t(other_index) < other.capacity() && std::size_t(this_index) < capacity()); + m_data[this_index] = other.m_data[other_index]; + } public: @@ -286,6 +309,15 @@ class Property_container { return array.get(); } + // todo: misleading name, maybe it could be add_same_properties? + void copy_properties(const Property_container& other) { + for (auto [name, other_array]: other.m_property_arrays) { + // If this container doesn't have any property by this name, add it (with the same type as in other) + if (!property_exists(name)) + m_property_arrays.emplace(name, other_array->empty_clone(m_active_indices)); + } + } + template const Property_array& get_property(const std::string& name) const { CGAL_precondition(m_property_arrays.count(name) != 0); @@ -316,6 +348,12 @@ class Property_container { return true; } + // todo: maybe the non-type-strict version is useful? + bool property_exists(const std::string& name) const { + auto it = m_property_arrays.find(name); + return (it != m_property_arrays.end()); + } + /*! * Removes a property array from the container * @@ -491,6 +529,11 @@ class Property_container { return indices; } + void shrink_to_fit() { + for (auto [name, array]: m_property_arrays) + array->shrink_to_fit(); + } + /*! * Adds the elements of the other container to this container for each property which is present in this container. * @@ -503,7 +546,6 @@ class Property_container { * @param other */ void append(const Property_container& other) { - // todo m_active_indices.insert(m_active_indices.end(), other.m_active_indices.begin(), other.m_active_indices.end()); for (auto [name, array]: m_property_arrays) { @@ -515,6 +557,15 @@ class Property_container { } } + // todo: maybe should be renamed to transfer_from, but I'd rather remove this functionality entirely + void transfer(const Property_container& other, Index other_index, Index this_index) { + CGAL_precondition(other.m_property_arrays.size() == m_property_arrays.size()); + for (auto [name, array]: m_property_arrays) { + auto other_array = other.m_property_arrays.at(name); + array->transfer_from(*other_array, other_index, this_index); + } + } + // todo: maybe a compress() method? }; From da558be865bd96467ee1b7a050d37b66d3817cb3 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 6 Jun 2023 19:08:37 -0600 Subject: [PATCH 053/520] initial api proposal --- .../Concepts/MultiPolygonWithHoles_2.h | 83 ++++++++++++++++++ .../Polygon_repair_2/PackageDescription.txt | 10 ++- .../Multipolygon_with_holes_2.h | 84 +++++++++++++++++++ .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 67 +++++++++++++++ ...riangulation_with_odd_even_constraints_2.h | 44 ++++++++++ .../include/CGAL/Polygon_repair_2/repair.h | 30 ------- 6 files changed, 286 insertions(+), 32 deletions(-) create mode 100644 Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h create mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h create mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h create mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h delete mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h new file mode 100644 index 000000000000..269a7c5c22c8 --- /dev/null +++ b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h @@ -0,0 +1,83 @@ +/*! \ingroup PkgPolygonRepair2Concepts + * \cgalConcept + * + * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} + * + * A model of this concept represents a multipolygon with holes. + * + * \cgalHasModel `CGAL::Multipolygon_with_holes_2` + */ + +class MultiPolygonWithHoles_2 { +public: + +/// \name Types +/// @{ + +//! the polygon type used to represent each connected component of the multipolygon. +typedef unspecified_type Polygon_with_holes_2; + +/*! a bidirectional iterator over the polygons. + * Its value type is `Polygon_with_holes_2`. + */ +typedef unspecified_type Polygon_const_iterator; + +//! range type for iterating over holes. +typedef unspecified_type Polygons_container; + +/// @} + +/// \name Creation +/// @{ + +/*! constructs a multipolygon using a range of polygons. + */ +template +MultipolygonWithHoles_2(InputIterator begin, InputIterator end); + +/// @} + +/// \name Predicates +/// @{ + +/*! returns the number of polygons. + */ +Size number_of_polygons(); + +/// @} + +/// \name Access Functions +/// @{ + +/*! returns the begin iterator of the polygons. + */ +Polygon_const_iterator polygons_begin() const; + +/*! returns the past-the-end iterator of the polygons. + */ +Polygon_const_iterator polygons_end() const; + +/*! returns the range of polygons. + */ +const Polygons_container& polygons() const; + +/// @} + +/// \name Modifiers +/// @{ + +/*! adds a given polygon to the multipolygon. + */ +void add_polygon(const Polygon_with_holes_2& polygon); + +/*! erases the specified polygon. + */ +void erase_polygon(Polygon_iterator pit); + +/*! removes all the polygons. + */ +void clear(); + +/// @} + +}; /* end MultipolygonWithHoles_2 */ diff --git a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt index 8f02d4c00b41..a8d4bb5cf863 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt @@ -31,7 +31,7 @@ \cgalPkgShortInfoBegin \cgalPkgSince{6.0} -\cgalPkgDependsOn{\ref PkgPolygon2} +\cgalPkgDependsOn{\ref PkgPolygon2, \ref PkgTriangulation2} \cgalPkgBib{cgal:x-x} \cgalPkgLicense{\ref licensesGPL "GPL"} \cgalPkgDemo{DEMO 1,demo1.zip} @@ -43,7 +43,13 @@ \cgalClassifedRefPages \cgalCRPSection{Concepts} -- ... +- `MultipolygonWithHoles_2` + +\cgalCRPSection{Classes} +- `CGAL::Triangulation_with_odd_even_constraints_2` +- `CGAL::Multipolygon_with_holes_2` +- `CGAL::Polygon_repair_2` + \cgalCRPSection{Functions} - `CGAL::Polygon_repair_2::repair()` diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h new file mode 100644 index 000000000000..26dd1f796971 --- /dev/null +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -0,0 +1,84 @@ +// Copyright (c) 2023 GeometryFactory. All rights reserved. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_MULTIPOLYGON_WITH_HOLES_2_H +#define CGAL_MULTIPOLYGON_WITH_HOLES_2_H + +#include + +namespace CGAL { + +/*! \ingroup PkgPolygonRepair2Ref + * + * The class `Multipolygon_with_holes_2` models the concept + * `MultiPolygonWithHoles_2`. + * It is parameterized with a type `Polygon` used to define the exposed + * type `Polygon_with_holes_2`. This type represents each polygon. + * + * \tparam Polygon_ must have input and output operators. + * + * \cgalModels `MultipolygonWithHoles_2` + */ +template +class Multipolygon_with_holes_2 { +public: +/// \name Definition + +/// @{ + /// polygon with holes type + typedef Polygon_ Polygon_with_holes_2; +/// @} + + typedef std::deque Polygons_container; + + typedef typename Polygons_container::iterator Polygon_iterator; + typedef typename Polygons_container::const_iterator Polygon_const_iterator; + + typedef unsigned int Size; + + Multipolygon_with_holes_2() {} + + template + Multipolygon_with_holes_2(PolygonsInputIterator p_begin, + PolygonsInputIterator p_end) : + m_polygons(p_begin, p_end) + {} + + Polygons_container& polygons() { return m_polygons; } + + const Polygons_container& polygons() const { return m_polygons; } + + Polygon_iterator polygons_begin() { return m_polygons.begin(); } + + Polygon_iterator polygons_end() { return m_polygons.end(); } + + Polygon_const_iterator polygons_begin() const { return m_polygons.begin(); } + + Polygon_const_iterator polygons_end() const { return m_polygons.end(); } + + void add_polygon(const Polygon_with_holes_2& pgn) { m_polygons.push_back(pgn); } + + void add_polygon(Polygon_with_holes_2&& pgn) { m_polygons.emplace_back(std::move(pgn)); } + + void erase_polygon(Polygon_iterator pit) { m_polygons.erase(pit); } + + void clear() { m_polygons.clear(); } + + Size number_of_polygons() const { return static_cast(m_polygons.size()); } + +protected: + Polygons_container m_polygons; +}; + + +} //namespace CGAL + +#endif diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h new file mode 100644 index 000000000000..237ed1aa3f4a --- /dev/null +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -0,0 +1,67 @@ +// Copyright (c) 2023 GeometryFactory. All rights reserved. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_POLYGON_REPAIR_2_H +#define CGAL_POLYGON_REPAIR_2_H + +#include +#include +#include +#include + +namespace CGAL { + +namespace Polygon_repair_2 { + +/// \ingroup PkgPolygonRepair2Functions +/// Repair a polygon without holes +Multipolygon_with_holes_2 repair(const Polygon_2& p); + +/// Repair a polygon with holes +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p); + +/// Repair a polygon with holes +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p); + +class Polygon_repair_2 { + public: + + /// \name Modifiers + /// @{ + + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Polygon_2& p); + + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Polygon_with_holes_2& p); + + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Multipolygon_with_holes_2& p); + + // Label triangles in triangulation as inside or outside the polygon + void label_triangulation(); + + /// @} + + // Reconstruct multipolygon based on the triangles labelled as inside the polygon + Multipolygon_with_holes_2 reconstruct_polygon(); + + // Erases the triangulation. + void clear(); + + protected: + Triangulation_with_odd_even_constraints_2 triangulation; +} + +} // namespace Polygon_repair_2 +} // namespace CGAL + +#endif // CGAL_POLYGON_REPAIR_2_H diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h new file mode 100644 index 000000000000..9ebd5aa46a22 --- /dev/null +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -0,0 +1,44 @@ +// Copyright (c) 2023 GeometryFactory. All rights reserved. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H +#define CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H + +namespace CGAL { + +template +class Triangulation_with_odd_even_constraints_2 { + + // the triangulation class. + typedef typename Triangulation_ Triangulation; + + // handle to a vertex. + typedef typename Triangulation_::Vertex_handle Vertex_handle; + + // iterator over interior faces. + class Interior_faces_iterator : Triangulation::All_faces_iterator { + Interior_faces_iterator operator++(); + Interior_faces_iterator operator--(); + } + + // Add constraint from va to vb using the odd-even rule + void odd_even_insert_constraint(Vertex_handle va, Vertex_handle vb); + + // Starts at an arbitrary interior face + Interior_faces_iterator interior_faces_begin(); + + // Past-the-end iterator + Interior_faces_iterator interior_faces_end(); +} + +} // namespace CGAL + +#endif // CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H \ No newline at end of file diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h deleted file mode 100644 index bd0bb93439c7..000000000000 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/repair.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2023 GeometryFactory. All rights reserved. -// 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) : Ken Arroyo Ohori -// -#ifndef CGAL_POLYGON_REPAIR_2_REPAIR_H -#define CGAL_POLYGON_REPAIR_2_REPAIR_H - -#include - - -namespace CGAL { - -namespace Polygon_repair_2 { - -/// \ingroup PkgPolygonRepair2Functions -/// This is a place holder for a function -template -bool repair(Polygon& p); - -} // namespace Polygon_repair_2 -} // namespace CGAL - -#endif From 497014022e18e6e057d0ef46545c530989b5d4d3 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 7 Jun 2023 11:26:09 +0200 Subject: [PATCH 054/520] Fix join() behavior; mark all indices as active instead of inactive --- Point_set_3/include/CGAL/Point_set_3.h | 12 +++++++----- Property_map/include/CGAL/Properties.h | 7 +++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 5e64fcb58dfb..539423f9b1d1 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -302,6 +302,7 @@ class Point_set_3 */ std::size_t number_of_points () const { return m_base.capacity() - m_nb_removed; } /// \cond SKIP_IN_MANUAL + // todo: why is this undocumented, but mentioned in the number_of_points documentation? std::size_t size () const { return number_of_points(); } /// \endcond @@ -327,7 +328,7 @@ class Point_set_3 { collect_garbage(); other.collect_garbage(); - other.m_base.append(m_base); + m_base.append(other.m_base); // Reset indices for (std::size_t i = 0; i < this->m_base.size(); ++ i) @@ -345,7 +346,7 @@ class Point_set_3 */ void clear() { - m_base.reserve(0); + m_base.resize(0); m_base.remove_all_properties_except({"index", "point"}); m_nb_removed = 0; } @@ -371,7 +372,7 @@ class Point_set_3 \note This method does not change the content of the point set and is only used for optimization. */ - void reserve (std::size_t s) { m_base.reserve(s); } + void reserve (std::size_t s) { m_base.resize(s); } /*! \brief changes size of the point set. @@ -463,7 +464,7 @@ class Point_set_3 iterator insert (const Point& p) { iterator out = insert(); - m_points[size()-1] = p; + m_points[*out] = p; return out; } @@ -732,7 +733,8 @@ class Point_set_3 // Sorting based on the indices reorders the point set correctly quick_sort_on_indices ((std::ptrdiff_t)0, (std::ptrdiff_t)(m_base.size() - 1)); - m_base.reserve (size ()); + auto s = size(); + m_base.resize(size ()); m_base.shrink_to_fit (); m_nb_removed = 0; } diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index d969a8d4ff0f..47097a0ab6e9 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -163,6 +163,8 @@ class Property_array : public Property_array_base { }; +// todo: property maps/array handles should go in their own file + // todo: add const/read-only handle template class Property_array_handle { @@ -410,6 +412,11 @@ class Property_container { array->reserve(n); } + void resize(std::size_t n) { + reserve(n); + std::fill(m_active_indices.begin(), m_active_indices.end(), true); + } + [[nodiscard]] std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); } [[nodiscard]] std::size_t capacity() const { return m_active_indices.size(); } From 9bf32ef7852c07c9048db229e68eac46703f0ffa Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 7 Jun 2023 13:58:17 +0200 Subject: [PATCH 055/520] Push_property_map & Property_back_inserter use references instead of pointers --- Point_set_3/include/CGAL/Point_set_3.h | 62 +++++++------------ Point_set_3/include/CGAL/Point_set_3/IO/PLY.h | 7 +-- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 539423f9b1d1..b99edbeb02ee 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -733,7 +733,6 @@ class Point_set_3 // Sorting based on the indices reorders the point set correctly quick_sort_on_indices ((std::ptrdiff_t)0, (std::ptrdiff_t)(m_base.size() - 1)); - auto s = size(); m_base.resize(size ()); m_base.shrink_to_fit (); m_nb_removed = 0; @@ -1100,30 +1099,27 @@ class Point_set_3 public: typedef std::output_iterator_tag iterator_category; - typedef typename Property::value_type value_type; + typedef Property value_type; typedef std::ptrdiff_t difference_type; typedef void pointer; typedef void reference; private: - Point_set* ps; - Property* prop; - Index ind; + Point_set& ps; + Property_map map; public: - Property_back_inserter(Point_set* ps, Property* prop, Index ind=Index()) - : ps(ps), prop (prop), ind(ind) {} + Property_back_inserter(Point_set& ps, Properties::Property_array& prop) : + ps(ps), map(prop) {} Property_back_inserter& operator++() { return *this; } Property_back_inserter& operator++(int) { return *this; } Property_back_inserter& operator*() { return *this; } Property_back_inserter& operator= (const value_type& p) { - if(ps->size() <= ind) - ps->insert(); - put(*prop, ind, p); - ++ ind; + auto new_index = *ps.insert(); + put(map, new_index, p); return *this; } @@ -1136,35 +1132,25 @@ class Point_set_3 public: typedef Index key_type; - typedef typename Property::value_type value_type; + typedef Property value_type; typedef value_type& reference; typedef boost::read_write_property_map_tag category; - Point_set* ps; - Property* prop; - mutable Index ind; + Point_set& ps; + Property_map map; - Push_property_map(Point_set* ps = nullptr, - Property* prop = nullptr, - Index ind=Index()) - : ps(ps), prop(prop), ind(ind) {} + Push_property_map(Point_set& ps, Properties::Property_array& prop) : + ps(ps), map(prop) {} friend void put(const Push_property_map& pm, Index& i, const value_type& t) { - // todo: Why does this need to store ind? - auto s = pm.ps->size(); - if(pm.ps->size() <= (pm.ind)) { - pm.ps->insert(); - pm.ind = pm.ps->size() - 1; - } - put(*(pm.prop), pm.ind, t); - i = pm.ind; - ++pm.ind; + i = *pm.ps.insert(); + put(pm.map, i, t); } friend reference get (const Push_property_map& pm, const Index& i) { - return ((*(pm.prop))[i]); + return ((*(pm.map))[i]); } }; @@ -1174,7 +1160,7 @@ class Point_set_3 /// \cgalAdvancedBegin /// Back inserter on indices /// \cgalAdvancedEnd - typedef Property_back_inserter Index_back_inserter; + typedef Property_back_inserter Index_back_inserter; /// \cgalAdvancedType /// \cgalAdvancedBegin /// Back inserter on points @@ -1184,12 +1170,12 @@ class Point_set_3 /// \cgalAdvancedBegin /// Property map for pushing new points /// \cgalAdvancedEnd - typedef Push_property_map Point_push_map; + typedef Push_property_map Point_push_map; /// \cgalAdvancedType /// \cgalAdvancedBegin /// Property map for pushing new vectors /// \cgalAdvancedEnd - typedef Push_property_map Vector_push_map; + typedef Push_property_map Vector_push_map; /*! \cgalAdvancedFunction @@ -1206,10 +1192,10 @@ class Point_set_3 \cgalAdvancedEnd */ template - Push_property_map > + Push_property_map push_property_map (Property_map& prop) { - return Push_property_map > (this, &prop, size()); + return Push_property_map (*this, prop.array()); } /*! \cgalAdvancedFunction @@ -1219,7 +1205,7 @@ class Point_set_3 */ Point_push_map point_push_map () { - return Point_push_map (this, &m_points, size()); + return Point_push_map (*this, m_base.template get_property("point")); } /*! \cgalAdvancedFunction @@ -1233,7 +1219,7 @@ class Point_set_3 Vector_push_map normal_push_map () { CGAL_precondition(m_normals.has_value()); - return Vector_push_map (this, &m_normals.value(), size()); + return Vector_push_map (*this, m_base.template get_property("normal")); } /*! \cgalAdvancedFunction @@ -1243,7 +1229,7 @@ class Point_set_3 */ Index_back_inserter index_back_inserter () { - return Index_back_inserter (this, &m_indices, size()); + return Index_back_inserter (*this, m_base.template get_property("index")); } /*! \cgalAdvancedFunction @@ -1253,7 +1239,7 @@ class Point_set_3 */ Point_back_inserter point_back_inserter () { - return Point_back_inserter (this, &m_points, size()); + return Point_back_inserter (*this, m_base.template get_property("point")); } /// @} diff --git a/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h b/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h index 9e2901aef3a8..ade78866fdaf 100644 --- a/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h +++ b/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h @@ -49,16 +49,15 @@ class Point_set_3_filler class PLY_property_to_point_set_property : public Abstract_ply_property_to_point_set_property { typedef typename Point_set::template Property_map Map; - typedef typename Point_set::template Push_property_map Pmap; + typedef typename Point_set::template Push_property_map Pmap; Map m_map; Pmap m_pmap; std::string m_name; public: PLY_property_to_point_set_property(Point_set& ps, const std::string& name) : m_name(name), - m_map(ps.add_property_map(name, Type()).first) { - m_pmap = ps.push_property_map(m_map); - } + m_map(ps.add_property_map(name, Type()).first), + m_pmap(ps.push_property_map(m_map)) {} virtual void assign(PLY_element& element, typename Point_set::Index index) { From 5426708ba431dbd9f6e0c8778b3dd58ecb97f3cd Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 7 Jun 2023 17:42:02 -0600 Subject: [PATCH 056/520] defined rest of API based on meeting today --- .../Concepts/MultiPolygonWithHoles_2.h | 2 +- .../Polygon_repair_2/PackageDescription.txt | 3 +- .../Multipolygon_with_holes_2.h | 16 ++--- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 70 +++++++++++++------ ...riangulation_with_odd_even_constraints_2.h | 17 ++++- 5 files changed, 71 insertions(+), 37 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h index 269a7c5c22c8..9ffa99054673 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h @@ -8,7 +8,7 @@ * \cgalHasModel `CGAL::Multipolygon_with_holes_2` */ -class MultiPolygonWithHoles_2 { +class MultipolygonWithHoles_2 { public: /// \name Types diff --git a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt index a8d4bb5cf863..054a066091a7 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt @@ -35,7 +35,6 @@ \cgalPkgBib{cgal:x-x} \cgalPkgLicense{\ref licensesGPL "GPL"} \cgalPkgDemo{DEMO 1,demo1.zip} -\cgalPkgDemo{DEMO 2,demo2.zip} \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd @@ -46,8 +45,8 @@ - `MultipolygonWithHoles_2` \cgalCRPSection{Classes} -- `CGAL::Triangulation_with_odd_even_constraints_2` - `CGAL::Multipolygon_with_holes_2` +- `CGAL::Triangulation_with_odd_even_constraints_2` - `CGAL::Polygon_repair_2` \cgalCRPSection{Functions} diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index 26dd1f796971..832549cb729b 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -12,7 +12,7 @@ #ifndef CGAL_MULTIPOLYGON_WITH_HOLES_2_H #define CGAL_MULTIPOLYGON_WITH_HOLES_2_H -#include +#include namespace CGAL { @@ -27,22 +27,22 @@ namespace CGAL { * * \cgalModels `MultipolygonWithHoles_2` */ -template +template > class Multipolygon_with_holes_2 { public: -/// \name Definition + /// \name Definition -/// @{ /// polygon with holes type - typedef Polygon_ Polygon_with_holes_2; -/// @} - + typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + typedef std::deque Polygons_container; typedef typename Polygons_container::iterator Polygon_iterator; typedef typename Polygons_container::const_iterator Polygon_const_iterator; typedef unsigned int Size; + Multipolygon_with_holes_2() {} @@ -81,4 +81,4 @@ class Multipolygon_with_holes_2 { } //namespace CGAL -#endif +#endif // CGAL_MULTIPOLYGON_WITH_HOLES_2_H diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 237ed1aa3f4a..a550065ec755 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -23,42 +23,66 @@ namespace Polygon_repair_2 { /// \ingroup PkgPolygonRepair2Functions /// Repair a polygon without holes -Multipolygon_with_holes_2 repair(const Polygon_2& p); - -/// Repair a polygon with holes -Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p); +template +Multipolygon_with_holes_2 repair(const Polygon_2& p); +/// \ingroup PkgPolygonRepair2Functions /// Repair a polygon with holes -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p); +template +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p); +/// \ingroup PkgPolygonRepair2Functions +/// Repair a multipolygon with holes +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p); + +/*! \ingroup PkgPolygonRepair2Ref + * + * The class `Polygon_repair_2` builds on a constrained + * triangulation to remove the parts of constraints that overlap an even number of times + */ +template class Polygon_repair_2 { - public: +public: + + /// \name Creation + Polygon_repair_2(); + + /// \name Modifiers + /// @{ + + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Polygon_2& p); + + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Polygon_with_holes_2& p); + + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Multipolygon_with_holes_2& p); - /// \name Modifiers - /// @{ + // Label triangles in triangulation as inside or outside the polygon + void label_triangulation(); - // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_2& p); + // Reconstruct multipolygon based on the triangles labelled as inside the polygon + void reconstruct_polygon(); - // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_with_holes_2& p); + // Erases the triangulation. + void clear(); - // Add edges of the polygon to the triangulation - void add_to_triangulation(const Multipolygon_with_holes_2& p); + /// @} - // Label triangles in triangulation as inside or outside the polygon - void label_triangulation(); + /// \name Access Functions + /// @{ - /// @} + Triangulation_with_odd_even_constraints_2 triangulation(); - // Reconstruct multipolygon based on the triangles labelled as inside the polygon - Multipolygon_with_holes_2 reconstruct_polygon(); + Multipolygon_with_holes_2 multipolygon(); - // Erases the triangulation. - void clear(); + /// @} + - protected: - Triangulation_with_odd_even_constraints_2 triangulation; +protected: + Triangulation_with_odd_even_constraints_2 t; + Multipolygon_with_holes_2 mp; } } // namespace Polygon_repair_2 diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 9ebd5aa46a22..e0819226b214 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -14,17 +14,28 @@ namespace CGAL { +/*! \ingroup PkgPolygonRepair2Ref + * + * The class `Triangulation_with_odd_even_constraints_2` builds on a constrained + * triangulation to remove the parts of constraints that overlap an even number of times + * + * \tparam Triangulation_ must have support for constraints + */ template class Triangulation_with_odd_even_constraints_2 { +public: + /// \name Definition - // the triangulation class. + /// @{ + /// the triangulation class. typedef typename Triangulation_ Triangulation; - // handle to a vertex. + /// handle to a vertex. typedef typename Triangulation_::Vertex_handle Vertex_handle; + /// @} // iterator over interior faces. - class Interior_faces_iterator : Triangulation::All_faces_iterator { + class Interior_faces_iterator : public Triangulation::All_faces_iterator { Interior_faces_iterator operator++(); Interior_faces_iterator operator--(); } From bd3cf86a43cf6c305ea6b6603ef0f154c99bdf3e Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 13 Jun 2023 19:41:18 -0600 Subject: [PATCH 057/520] skeleton with compiling code --- .../Polygon_repair_2/repair_polygon_2.cpp | 20 +++--- .../Multipolygon_with_holes_2.h | 6 +- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 72 ++++++++++++++----- ...riangulation_with_odd_even_constraints_2.h | 22 +++--- 4 files changed, 83 insertions(+), 37 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 20b2c9694a81..aa66fb706c3c 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -2,18 +2,18 @@ #include #include -#include -#include -#include -#include +#include +// #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Polygon_2 Polygon_2; -typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef CGAL::Multipolygon_with_holes_2 Multipolygon; +typedef CGAL::Polygon_repair_2::Polygon_repair_2 Polygon_repair; + +int main(int argc, char* argv[]) { + // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); + + Polygon_repair pr; -int main(int argc, char* argv[]) -{ - std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); return 0; } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index 832549cb729b..bc279b9c279f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -27,14 +27,14 @@ namespace CGAL { * * \cgalModels `MultipolygonWithHoles_2` */ -template > +template > class Multipolygon_with_holes_2 { public: /// \name Definition /// polygon with holes type - typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; typedef std::deque Polygons_container; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index a550065ec755..9308ef52e002 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -14,6 +14,9 @@ #include #include +#include +#include + #include #include @@ -21,43 +24,78 @@ namespace CGAL { namespace Polygon_repair_2 { +template +class Polygon_repair_2; + /// \ingroup PkgPolygonRepair2Functions /// Repair a polygon without holes -template -Multipolygon_with_holes_2 repair(const Polygon_2& p); +template +Multipolygon_with_holes_2 repair(const CGAL::Polygon_2& p) { + CGAL::Polygon_repair_2::Polygon_repair_2 pr; +} /// \ingroup PkgPolygonRepair2Functions /// Repair a polygon with holes -template -Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p); +template +Multipolygon_with_holes_2 repair(const CGAL::Polygon_with_holes_2& p) { + CGAL::Polygon_repair_2::Polygon_repair_2 pr; +} /// \ingroup PkgPolygonRepair2Functions /// Repair a multipolygon with holes -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p); +template +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p) { + CGAL::Polygon_repair_2::Polygon_repair_2 pr; +} /*! \ingroup PkgPolygonRepair2Ref * * The class `Polygon_repair_2` builds on a constrained * triangulation to remove the parts of constraints that overlap an even number of times */ -template +template > class Polygon_repair_2 { public: + struct Repair_face_info { + bool processed; + bool interior; + Repair_face_info() { + processed = false; + interior = false; + } + }; + typedef CGAL::Triangulation_vertex_base_2 Vertex_base; + typedef CGAL::Constrained_triangulation_face_base_2 Face_base; + typedef CGAL::Triangulation_face_base_with_info_2 Face_base_with_repair_info; + typedef CGAL::Triangulation_data_structure_2 Triangulation_data_structure; + typedef CGAL::Exact_predicates_tag Tag; // assumed for now + typedef CGAL::Constrained_Delaunay_triangulation_2 Constrained_Delaunay_triangulation; + typedef Triangulation_with_odd_even_constraints_2 Triangulation; + /// \name Creation - Polygon_repair_2(); + Polygon_repair_2() { + + } /// \name Modifiers /// @{ // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_2& p); + void add_to_triangulation(const Polygon_2& p) { + + } // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_with_holes_2& p); + void add_to_triangulation(const Polygon_with_holes_2& p) { + + } // Add edges of the polygon to the triangulation - void add_to_triangulation(const Multipolygon_with_holes_2& p); + void add_to_triangulation(const Multipolygon_with_holes_2& p) { + + } // Label triangles in triangulation as inside or outside the polygon void label_triangulation(); @@ -66,24 +104,26 @@ class Polygon_repair_2 { void reconstruct_polygon(); // Erases the triangulation. - void clear(); + void clear() { + t.clear(); + } /// @} /// \name Access Functions /// @{ - Triangulation_with_odd_even_constraints_2 triangulation(); + Triangulation_with_odd_even_constraints_2 triangulation(); - Multipolygon_with_holes_2 multipolygon(); + Multipolygon_with_holes_2 multipolygon(); /// @} protected: - Triangulation_with_odd_even_constraints_2 t; - Multipolygon_with_holes_2 mp; -} + Triangulation_with_odd_even_constraints_2 t; + Multipolygon_with_holes_2 mp; +}; } // namespace Polygon_repair_2 } // namespace CGAL diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index e0819226b214..fc74b1730134 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -22,33 +22,39 @@ namespace CGAL { * \tparam Triangulation_ must have support for constraints */ template -class Triangulation_with_odd_even_constraints_2 { +class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { public: /// \name Definition /// @{ /// the triangulation class. - typedef typename Triangulation_ Triangulation; + typedef Triangulation_ Base_triangulation; /// handle to a vertex. typedef typename Triangulation_::Vertex_handle Vertex_handle; /// @} // iterator over interior faces. - class Interior_faces_iterator : public Triangulation::All_faces_iterator { + class Interior_faces_iterator : public Base_triangulation::All_faces_iterator { Interior_faces_iterator operator++(); Interior_faces_iterator operator--(); - } + }; // Add constraint from va to vb using the odd-even rule - void odd_even_insert_constraint(Vertex_handle va, Vertex_handle vb); + void odd_even_insert_constraint(Vertex_handle va, Vertex_handle vb) { + if (va == vb) return; + } // Starts at an arbitrary interior face - Interior_faces_iterator interior_faces_begin(); + Interior_faces_iterator interior_faces_begin() { + + } // Past-the-end iterator - Interior_faces_iterator interior_faces_end(); -} + Interior_faces_iterator interior_faces_end() { + + } +}; } // namespace CGAL From 8b72b0a6b0736091c28731b53234db51841f84dd Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 16 Jun 2023 10:45:55 +0200 Subject: [PATCH 058/520] Add a couple of simple copy tests, and ensure they pass --- Point_set_3/include/CGAL/Point_set_3.h | 24 ++++++------- Point_set_3/test/Point_set_3/CMakeLists.txt | 1 + .../Point_set_3/copy_construction_test.cpp | 36 +++++++++++++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 Point_set_3/test/Point_set_3/copy_construction_test.cpp diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index b99edbeb02ee..07c55b9c79a6 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -242,27 +242,23 @@ class Point_set_3 /*! \brief Assignment operator, all properties with their content are copied. */ - Point_set_3& operator= (const Point_set_3& ps) - { - // todo: should be implemented via the copy-swap idiom + Point_set_3& operator=(const Point_set_3 &ps){ m_base = ps.m_base; - m_indices = this->property_map ("index").first; - m_points = this->property_map ("point").first; - m_normals = this->property_map ("normal").first; m_nb_removed = ps.m_nb_removed; + if (has_normal_map()) + add_normal_map(); return *this; } /// \cond SKIP_IN_MANUAL // copy constructor (same as assignment) - Point_set_3 (const Point_set_3& ps) - { - // todo: update to use new invalidation-avoiding copy behavior - m_base = ps.m_base; - m_indices = this->property_map ("index").first; - m_points = this->property_map ("point").first; - m_normals = this->property_map ("normal").first; - m_nb_removed = ps.m_nb_removed; + Point_set_3(const Point_set_3& ps) : + m_base(ps.m_base), + m_indices(m_base.template get_property("index")), + m_points(m_base.template get_property("point")), + m_nb_removed(ps.m_nb_removed) { + if (has_normal_map()) + add_normal_map(); } /// \endcond diff --git a/Point_set_3/test/Point_set_3/CMakeLists.txt b/Point_set_3/test/Point_set_3/CMakeLists.txt index 4daea00a56b9..c15a334e640d 100644 --- a/Point_set_3/test/Point_set_3/CMakeLists.txt +++ b/Point_set_3/test/Point_set_3/CMakeLists.txt @@ -7,6 +7,7 @@ project(Point_set_3_Tests) # CGAL and its components find_package(CGAL REQUIRED) +create_single_source_cgal_program("copy_construction_test.cpp") create_single_source_cgal_program("point_set_test.cpp") create_single_source_cgal_program("point_set_test_join.cpp") create_single_source_cgal_program("test_deprecated_io_ps.cpp") diff --git a/Point_set_3/test/Point_set_3/copy_construction_test.cpp b/Point_set_3/test/Point_set_3/copy_construction_test.cpp new file mode 100644 index 000000000000..b5936f859966 --- /dev/null +++ b/Point_set_3/test/Point_set_3/copy_construction_test.cpp @@ -0,0 +1,36 @@ +#include + +#include +#include + +#include + +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; + +typedef CGAL::Point_set_3 Point_set; +typedef std::array Color; + +int main (int, char**) +{ + Point_set original; + original.insert({1, 2, 3}); + original.insert({4, 5, 6}); + + Point_set copy_constructed{original}; + assert(copy_constructed.number_of_points() == original.number_of_points()); + assert(copy_constructed.point(0) == original.point(0)); + assert(copy_constructed.point(1) == original.point(1)); + + Point_set copy_assigned; + copy_assigned = original; + assert(copy_assigned.number_of_points() == original.number_of_points()); + assert(copy_assigned.point(0) == original.point(0)); + assert(copy_assigned.point(1) == original.point(1)); + +} From 9050fab7cbdab656131b7355ee71dcfbf711d334 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 20 Jun 2023 10:41:13 +0200 Subject: [PATCH 059/520] Add a tiny unit test for point insertion & reservation, and fix the bug it catches --- Point_set_3/include/CGAL/Point_set_3.h | 8 +++- Point_set_3/test/Point_set_3/CMakeLists.txt | 1 + .../test/Point_set_3/point_insertion_test.cpp | 39 +++++++++++++++++++ Property_map/include/CGAL/Properties.h | 3 ++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 Point_set_3/test/Point_set_3/point_insertion_test.cpp diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 07c55b9c79a6..40dda167e9b3 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -368,7 +368,13 @@ class Point_set_3 \note This method does not change the content of the point set and is only used for optimization. */ - void reserve (std::size_t s) { m_base.resize(s); } + void reserve (std::size_t s) { + std::size_t initial_size = m_base.size(); + m_base.resize(s); + m_nb_removed = m_base.size() - initial_size; + for (std::size_t i = initial_size; i < m_base.size(); ++ i) + m_indices[i] = i; + } /*! \brief changes size of the point set. diff --git a/Point_set_3/test/Point_set_3/CMakeLists.txt b/Point_set_3/test/Point_set_3/CMakeLists.txt index c15a334e640d..512733089561 100644 --- a/Point_set_3/test/Point_set_3/CMakeLists.txt +++ b/Point_set_3/test/Point_set_3/CMakeLists.txt @@ -8,6 +8,7 @@ project(Point_set_3_Tests) find_package(CGAL REQUIRED) create_single_source_cgal_program("copy_construction_test.cpp") +create_single_source_cgal_program("point_insertion_test.cpp") create_single_source_cgal_program("point_set_test.cpp") create_single_source_cgal_program("point_set_test_join.cpp") create_single_source_cgal_program("test_deprecated_io_ps.cpp") diff --git a/Point_set_3/test/Point_set_3/point_insertion_test.cpp b/Point_set_3/test/Point_set_3/point_insertion_test.cpp new file mode 100644 index 000000000000..ad3ab40e8009 --- /dev/null +++ b/Point_set_3/test/Point_set_3/point_insertion_test.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include +#include + +#include + +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; + +typedef CGAL::Point_set_3 Point_set; + +int main() { + + Point_set points; + assert(points.number_of_points() == 0); + + // Add a large number of points using a generator + std::size_t reserved_points_count = 1000; + CGAL::Random_points_in_cube_3 generator; + points.reserve(reserved_points_count); + assert(points.number_of_points() == 0); + for (std::size_t i = 0; i < reserved_points_count; ++i) + points.insert(*(generator++)); + assert(points.number_of_points() == reserved_points_count); + + // Add more points without making a reservation beforehand + std::size_t additional_points_count = 100; + for (std::size_t i = 0; i < additional_points_count; ++i) + points.insert(*(generator++)); + assert(points.number_of_points() == reserved_points_count + additional_points_count); + +} diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 47097a0ab6e9..86e87a468ebd 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -223,6 +223,9 @@ class Property_container { public: + template + using Array = Property_array; + Property_container() = default; Property_container(const Property_container& other) { From 37089e953db25c20c8490b88904094e5b481b0c9 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 20 Jun 2023 14:57:33 +0200 Subject: [PATCH 060/520] Revert `::property_map()` to old behavior; new behavior is moved to `::get_property_map()` --- .../include/CGAL/Surface_mesh/IO/OFF.h | 8 +++--- .../include/CGAL/Surface_mesh/IO/PLY.h | 26 +++++++++---------- .../include/CGAL/Surface_mesh/Surface_mesh.h | 14 ++++++++-- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h index f746b55a2ceb..170ffa75bfa2 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h @@ -338,7 +338,7 @@ bool write_OFF_with_or_without_fcolors(std::ostream& os, const bool has_fcolors = !(is_default_parameter::value); - auto fcolors = sm.template property_map("f:color"); + auto fcolors = sm.template get_property_map("f:color"); if(!has_fcolors && fcolors && fcolors->size() > 0) return write_OFF_BGL(os, sm, np.face_color_map(fcolors.value())); @@ -361,7 +361,7 @@ bool write_OFF_with_or_without_vtextures(std::ostream& os, const bool has_vtextures = !(is_default_parameter::value); - auto vtextures = sm.template property_map("v:texcoord"); + auto vtextures = sm.template get_property_map("v:texcoord"); if(!has_vtextures && vtextures && vtextures->size() > 0) return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures.value())); @@ -382,7 +382,7 @@ bool write_OFF_with_or_without_vcolors(std::ostream& os, const bool has_vcolors = !(is_default_parameter::value); - auto vcolors = sm.template property_map("v:color"); + auto vcolors = sm.template get_property_map("v:color"); if(!has_vcolors && vcolors && vcolors->size() > 0) return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors.value())); @@ -405,7 +405,7 @@ bool write_OFF_with_or_without_vnormals(std::ostream& os, const bool has_vnormals = !(is_default_parameter::value); - auto vnormals = sm.template property_map("v:normal"); + auto vnormals = sm.template get_property_map("v:normal"); if(!has_vnormals && vnormals && vnormals->size() > 0) return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals.value())); diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h index 4f1f50dde989..8c21027b0223 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h @@ -460,7 +460,7 @@ bool fill_simplex_specific_header(std::ostream& os, if(prop == "v:normal") { - auto pmap = sm.template property_map(prop); + auto pmap = sm.template get_property_map(prop); if(pmap) { if(std::is_same::value) @@ -482,7 +482,7 @@ bool fill_simplex_specific_header(std::ostream& os, if(prop == "v:color") { - auto pmap = sm.template property_map(prop); + auto pmap = sm.template get_property_map(prop); if(pmap) { os << "property uchar red" << std::endl @@ -514,7 +514,7 @@ bool fill_simplex_specific_header(std::ostream& os, if(prop == "f:color") { - auto pmap = sm.template property_map(prop); + auto pmap = sm.template get_property_map(prop); if(pmap) { os << "property uchar red" << std::endl @@ -700,7 +700,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, std::string name = get_property_raw_name(prop[i], Simplex()); { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property char " << name << std::endl; @@ -709,7 +709,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property uchar " << name << std::endl; @@ -718,7 +718,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property short " << name << std::endl; @@ -727,7 +727,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property ushort " << name << std::endl; @@ -736,7 +736,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property int " << name << std::endl; @@ -745,7 +745,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property uint " << name << std::endl; @@ -754,7 +754,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property int " << name << std::endl; @@ -763,7 +763,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property uint " << name << std::endl; @@ -772,7 +772,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property float " << name << std::endl; @@ -781,7 +781,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, } } { - auto pmap = sm.template property_map(prop[i]); + auto pmap = sm.template get_property_map(prop[i]); if(pmap) { os << "property double " << name << std::endl; diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 5cea7e1a7446..01a6e121e205 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -1880,11 +1880,21 @@ class Surface_mesh return {{array.get()}, created}; } + /// returns a property map named `name` with key type `I` and value type `T`, + /// and a Boolean that is `true` if the property was created. + template + std::pair, bool> + property_map(const std::string& name) const { + auto [array, created] = + const_cast*>(this)->get_property_container().template get_or_add_property(name); + return {{array.get()}, created}; + } + /// returns a property map named `name` with key type `I` and value type `T`, /// if such a property map exists template std::optional> - property_map(const std::string& name) { + get_property_map(const std::string& name) { auto maybe_property_map = get_property_container().template get_property_if_exists(name); if (!maybe_property_map) return {}; else return {{maybe_property_map.value()}}; @@ -1892,7 +1902,7 @@ class Surface_mesh template std::optional> - property_map(const std::string& name) const { + get_property_map(const std::string& name) const { auto maybe_property_map = const_cast*>(this)->get_property_container().template get_property_if_exists(name); if (!maybe_property_map) return {}; else return {{maybe_property_map.value()}}; From b09e4d0fa628e49ca3407ff16d362361f98d1d79 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 21 Jun 2023 16:39:17 +0200 Subject: [PATCH 061/520] Add a property container with all aspects of the nodes (currently unused) --- Orthtree/include/CGAL/Orthtree.h | 53 +++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e67caf2fa067..8751c15e97e2 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -110,6 +111,8 @@ class Orthtree { */ typedef std::size_t Node_index; + typedef Properties::Property_container Node_property_container; + /*! * \brief Optional index of a node in the tree. */ @@ -161,6 +164,13 @@ class Orthtree { std::vector m_nodes; /* nodes of the tree; root is always at index 0 */ + Node_property_container m_node_properties; + Node_property_container::Array> &m_node_points; + Node_property_container::Array &m_node_depths; + Node_property_container::Array> &m_node_coordinates; + Node_property_container::Array &m_node_parents; + Node_property_container::Array &m_node_children; + Point m_bbox_min; /* input bounding box min value */ std::vector m_side_per_depth; /* side length per node's depth */ @@ -202,10 +212,13 @@ class Orthtree { Orthtree(PointRange& point_range, PointMap point_map = PointMap(), const FT enlarge_ratio = 1.2, - Traits traits = Traits()) - : m_traits(traits) - , m_range(point_range) - , m_point_map(point_map) { + Traits traits = Traits()) : + m_traits(traits), m_range(point_range), m_point_map(point_map), + m_node_points(m_node_properties.add_property>("points")), + m_node_depths(m_node_properties.add_property("depths", 0)), + m_node_coordinates(m_node_properties.add_property>("coordinates")), + m_node_parents(m_node_properties.add_property("parents")), + m_node_children(m_node_properties.add_property("children")) { m_nodes.emplace_back(); @@ -260,22 +273,26 @@ class Orthtree { /// \cond SKIP_IN_MANUAL // copy constructor - Orthtree(const Orthtree& other) - : m_traits(other.m_traits) - , m_range(other.m_range) - , m_point_map(other.m_point_map) - , m_nodes(other.m_nodes) // todo: copying will require some extra management - , m_bbox_min(other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) {} + Orthtree(const Orthtree& other) : + m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_nodes(other.m_nodes), + m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), + m_node_properties(other.m_node_properties), + m_node_points(m_node_properties.get_property>("points")), + m_node_depths(m_node_properties.get_property("depths")), + m_node_coordinates(m_node_properties.get_property>("coordinates")), + m_node_parents(m_node_properties.get_property("parents")), + m_node_children(m_node_properties.get_property("children")) {} // move constructor - Orthtree(Orthtree&& other) - : m_traits(other.m_traits) - , m_range(other.m_range) - , m_point_map(other.m_point_map) - , m_nodes(std::move(other.m_nodes)) - , m_bbox_min(other.m_bbox_min) - , m_side_per_depth(other.m_side_per_depth) { + Orthtree(Orthtree&& other): + m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_nodes(std::move(other.m_nodes)), + m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), + m_node_properties(other.m_node_properties), + m_node_points(m_node_properties.get_property>("points")), + m_node_depths(m_node_properties.get_property("depths")), + m_node_coordinates(m_node_properties.get_property>("coordinates")), + m_node_parents(m_node_properties.get_property("parents")), + m_node_children(m_node_properties.get_property("children")) { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. other.m_nodes.emplace_back(); From d1ac73d087ace9a6c129d1f0f3f26731157dc3d7 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 21 Jun 2023 17:34:26 +0200 Subject: [PATCH 062/520] Use index-based access for split predicates --- Orthtree/include/CGAL/Orthtree.h | 7 ++--- Orthtree/include/CGAL/Orthtree/Node.h | 2 +- .../include/CGAL/Orthtree/Split_predicates.h | 29 +++++++++++++++++++ Orthtree/test/Orthtree/test_octree_refine.cpp | 5 ++++ .../test/Orthtree/test_octree_traverse.cpp | 2 +- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 8751c15e97e2..2d00bd383a8e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -126,7 +126,7 @@ class Orthtree { /*! * \brief A predicate that determines whether a node must be split when refining a tree. */ - typedef std::function Split_predicate; + typedef std::function Split_predicate; /*! * \brief A model of `ConstRange` whose value type is `Node`. @@ -342,7 +342,7 @@ class Orthtree { todo.pop(); // Check if this node needs to be processed - if (split_predicate(m_nodes[current])) { + if (split_predicate(current, *this)) { // Check if we've reached a new max depth if (depth(current) == depth()) { @@ -458,7 +458,6 @@ class Orthtree { \return a const reference to the root node of the tree. */ - // todo: return index instead of ref Node_index root() const { return 0; } Node_index index(const Node& node) const { @@ -819,7 +818,7 @@ class Orthtree { // Split the node to create children using Local_coordinates = typename Node::Local_coordinates; for (int i = 0; i < Degree::value; i++) { - m_nodes.emplace_back(n, global_coordinates(n), depth(n) + 1, Local_coordinates{i}); + m_nodes.emplace_back(n, global_coordinates(n), depth(n) + 1, Local_coordinates{(unsigned long) i}); } // todo: this assumes that the new nodes are always allocated at the end m_nodes[n].m_children_index = m_nodes.size() - Degree::value; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index ca6bce35e5a5..c94a1019c4c3 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -229,7 +229,7 @@ class Orthtree::Node { * \param rhs node to compare with * \return whether the nodes have different topology. */ - bool operator==(const Self& rhs) const = default; + bool operator==(const Self& rhs) const = default; // todo: this doesn't work in C++17 friend std::ostream& operator<<(std::ostream& os, const Self& node) { return internal::print_orthtree_node(os, node); diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index e8d93415d5e5..766a76e35707 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -45,6 +45,15 @@ class Maximum_number_of_inliers { bool operator()(const Node &n) const { return (n.size() > m_bucket_size); } + + /*! + \brief returns `true` if `i` should be split, `false` otherwise. + */ + template + bool operator()(Node_index i, const Tree &tree) const { + return (tree.points(i).size() > m_bucket_size); + } + }; /*! @@ -71,6 +80,15 @@ class Maximum_depth { bool operator()(const Node &n) const { return n.depth() < m_max_depth; } + + /*! + \brief returns `true` if `i` should be split, `false` otherwise. + */ + template + bool operator()(Node_index i, const Tree &tree) const { + return (tree.depth(i) < m_max_depth); + } + }; /*! @@ -108,6 +126,17 @@ class Maximum_depth_and_maximum_number_of_inliers { std::size_t depth = n.depth(); return (num_points > m_bucket_size && depth < m_max_depth); } + + /*! + \brief returns `true` if `i` should be split, `false` otherwise. + */ + template + bool operator()(Node_index i, const Tree &tree) const { + std::size_t num_points = tree.points(i).size(); + std::size_t depth = tree.depth(i); + return (num_points > m_bucket_size && depth < m_max_depth); + } + }; } // Orthtrees diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 543c99a5a6e8..2e248262a118 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -24,6 +24,11 @@ class Split_nth_child_of_root { bool operator()(const Node& node) const { return (node.depth() == 1 && node.local_coordinates().to_ulong() == m_n); } + + template + bool operator()(Node_index i, const Tree &tree) const { + return (tree.depth(i) == 1 && tree.local_coordinates(i).to_ulong() == m_n); + } }; void test_1_point() { diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index f26625db46a7..5ad71f57f537 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -72,7 +72,7 @@ bool test_level_9_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse_indices(1); + auto nodes = octree.traverse_indices(static_cast(1)); // Check each item in the range auto iter = nodes.begin(); From ed9266e70f490531817e8f8adf532932967d3718 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 22 Jun 2023 10:23:29 +0200 Subject: [PATCH 063/520] Add parallel node system using properties bug: moved-from is not reset --- Orthtree/include/CGAL/Orthtree.h | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2d00bd383a8e..97a5c1303fb1 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -221,6 +221,7 @@ class Orthtree { m_node_children(m_node_properties.add_property("children")) { m_nodes.emplace_back(); + m_node_properties.emplace(); Array bbox_min; Array bbox_max; @@ -296,6 +297,7 @@ class Orthtree { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. other.m_nodes.emplace_back(); + other.m_node_properties.emplace(); } // Non-necessary but just to be clear on the rule of 5: @@ -699,6 +701,7 @@ class Orthtree { /// @{ bool is_leaf(Node_index n) const { + return !m_node_children[n].has_value(); return m_nodes[n].is_leaf(); } @@ -707,22 +710,30 @@ class Orthtree { } std::size_t depth(Node_index n) const { + return m_node_depths[n]; return m_nodes[n].depth(); } typename Node::Point_range& points(Node_index n) { + return m_node_points[n]; return m_nodes[n].points(); } const typename Node::Point_range& points(Node_index n) const { + return m_node_points[n]; return m_nodes[n].points(); } typename Node::Global_coordinates global_coordinates(Node_index n) const { + return m_node_coordinates[n]; return m_nodes[n].global_coordinates(); } typename Node::Local_coordinates local_coordinates(Node_index n) const { + typename Node::Local_coordinates result; + for (std::size_t i = 0; i < Dimension::value; ++i) + result[i] = global_coordinates(n)[i] & 1; + return result; return m_nodes[n].local_coordinates(); } @@ -732,11 +743,13 @@ class Orthtree { */ Node_index parent(Node_index node) const { CGAL_precondition (!is_root(node)); + return m_node_parents[node].get(); return m_nodes[node].m_parent_index.get(); } Node_index child(Node_index node, std::size_t i) const { CGAL_precondition (!is_leaf(node)); + return m_node_children[node].get() + i; return m_nodes[node].m_children_index.get() + i; } @@ -817,8 +830,21 @@ class Orthtree { // Split the node to create children using Local_coordinates = typename Node::Local_coordinates; - for (int i = 0; i < Degree::value; i++) { - m_nodes.emplace_back(n, global_coordinates(n), depth(n) + 1, Local_coordinates{(unsigned long) i}); + m_node_children[n] = m_node_properties.emplace_group(Degree::value); + for (std::size_t i = 0; i < Degree::value; i++) { + m_nodes.emplace_back(n, global_coordinates(n), depth(n) + 1, Local_coordinates{i}); + + Node_index c = m_node_children[n].get() + i; + + // Make sure the node isn't one of its own children + CGAL_assertion(n != m_node_children[n].get() + i); + + Local_coordinates local_coordinates{i}; + for (int i = 0; i < Dimension::value; i++) + m_node_coordinates[c][i] = (2 * m_node_coordinates[n][i]) + local_coordinates[i]; + CGAL_assertion(m_node_coordinates[c] == m_nodes.back().global_coordinates()); + m_node_depths[c] = m_node_depths[n] + 1; + m_node_parents[c] = n; } // todo: this assumes that the new nodes are always allocated at the end m_nodes[n].m_children_index = m_nodes.size() - Degree::value; From 11545245130b64bd859619cd3622cbb0a94724d2 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 22 Jun 2023 14:09:38 +0200 Subject: [PATCH 064/520] Ensure moved-from properties are reset --- Property_map/include/CGAL/Properties.h | 10 +++++++++- Property_map/test/Property_map/test_Properties.cpp | 9 +++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Properties.h index 86e87a468ebd..e75e2c4dda43 100644 --- a/Property_map/include/CGAL/Properties.h +++ b/Property_map/include/CGAL/Properties.h @@ -29,6 +29,8 @@ class Property_array_base { virtual void copy(const Property_array_base& other) = 0; + virtual void move(Property_array_base&& other) = 0; + virtual void append(const Property_array_base& other) = 0; virtual void reserve(std::size_t n) = 0; @@ -90,6 +92,12 @@ class Property_array : public Property_array_base { CGAL_precondition(m_active_indices.size() == m_data.size()); } + virtual void move(Property_array_base&& other_base) override { + auto&& other = dynamic_cast&&>(other_base); + m_data = std::move(other.m_data); + CGAL_precondition(m_active_indices.size() == m_data.size()); + } + virtual void append(const Property_array_base& other_base) override { auto& other = dynamic_cast&>(other_base); CGAL_precondition(m_data.size() + other.m_data.size() == m_active_indices.size()); @@ -278,7 +286,7 @@ class Property_container { CGAL_precondition(typeid(*this_array) == typeid(*other_array)); // Copy the data from the other array - this_array->copy(*other_array); + this_array->copy(std::move(*other_array)); } else { // Adds the new property diff --git a/Property_map/test/Property_map/test_Properties.cpp b/Property_map/test/Property_map/test_Properties.cpp index af1b4ca2cf46..a0407eb6ebf1 100644 --- a/Property_map/test/Property_map/test_Properties.cpp +++ b/Property_map/test/Property_map/test_Properties.cpp @@ -187,8 +187,8 @@ void test_constructors() { assert(a.num_properties() == 0); // Copy constructor should duplicate all properties - a.add_property("ints", 0); - a.add_property("floats", 0.0f); + auto& a_ints = a.add_property("ints", 0); + auto& a_floats = a.add_property("floats", 0.0f); a.emplace_group(10); a.get_property("ints")[3] = 1; a.get_property("floats")[3] = 1.0f; @@ -217,6 +217,7 @@ void test_constructors() { // Copy assignment should not invalidate previously obtained array references, // but it should update their values auto &b_ints = b.get_property("ints"); + auto &b_floats = b.get_property("floats"); assert(b_ints[4] == 0); b = a; assert(b.num_properties() == 3); @@ -234,12 +235,16 @@ void test_constructors() { // All properties are preserved, though assert(a.num_properties() == 3); assert(a.size() == 0); + assert(a_ints.capacity() == 0); + assert(a_floats.capacity() == 0); // Move constructor should behave like move assignment Property_container e{std::move(b)}; assert(e.num_properties() == 3); assert(b.num_properties() == 3); assert(b.size() == 0); + assert(b_ints.capacity() == 0); + assert(b_floats.capacity() == 0); } From 08b418bda2824dccd949ce4e87abe9f167a1401c Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 22 Jun 2023 14:11:30 +0200 Subject: [PATCH 065/520] Ensure properties of a moved-from tree are also moved-from --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 97a5c1303fb1..0324394627de 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -288,7 +288,7 @@ class Orthtree { Orthtree(Orthtree&& other): m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_nodes(std::move(other.m_nodes)), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), - m_node_properties(other.m_node_properties), + m_node_properties(std::move(other.m_node_properties)), m_node_points(m_node_properties.get_property>("points")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property>("coordinates")), From bfe584590ef6d444e0708effeacb9265f50d727f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 22 Jun 2023 14:44:38 +0200 Subject: [PATCH 066/520] Orthtree no longer instantiates Node types --- Orthtree/include/CGAL/Orthtree.h | 57 +++---------------- Orthtree/include/CGAL/Orthtree/IO.h | 25 +++----- Orthtree/include/CGAL/Orthtree/Node.h | 4 -- Orthtree/test/Orthtree/test_node_adjacent.cpp | 2 +- 4 files changed, 18 insertions(+), 70 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 0324394627de..6c185ad47509 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -162,8 +162,6 @@ class Orthtree { PointRange& m_range; /* input point range */ PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */ - std::vector m_nodes; /* nodes of the tree; root is always at index 0 */ - Node_property_container m_node_properties; Node_property_container::Array> &m_node_points; Node_property_container::Array &m_node_depths; @@ -220,7 +218,6 @@ class Orthtree { m_node_parents(m_node_properties.add_property("parents")), m_node_children(m_node_properties.add_property("children")) { - m_nodes.emplace_back(); m_node_properties.emplace(); Array bbox_min; @@ -275,7 +272,7 @@ class Orthtree { // copy constructor Orthtree(const Orthtree& other) : - m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_nodes(other.m_nodes), + m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(other.m_node_properties), m_node_points(m_node_properties.get_property>("points")), @@ -286,7 +283,7 @@ class Orthtree { // move constructor Orthtree(Orthtree&& other): - m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_nodes(std::move(other.m_nodes)), + m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(std::move(other.m_node_properties)), m_node_points(m_node_properties.get_property>("points")), @@ -296,7 +293,6 @@ class Orthtree { m_node_children(m_node_properties.get_property("children")) { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. - other.m_nodes.emplace_back(); other.m_node_properties.emplace(); } @@ -316,12 +312,11 @@ class Orthtree { /*! \brief recursively subdivides the orthtree until it meets the given criteria. + todo: split predicate now works with node indices! The split predicate is a `std::function` that takes a `Node` and returns a Boolean value (where `true` implies that a `Node` needs to be split, `false` that the `Node` should be a leaf). - todo: split predicate should work with node indices! - This function may be called several times with different predicates: in that case, nodes already split are left unaltered, while nodes that were not split and for which `split_predicate` @@ -462,24 +457,6 @@ class Orthtree { */ Node_index root() const { return 0; } - Node_index index(const Node& node) const { - return std::distance(m_nodes.data(), &node); - } - - Maybe_node_index index(const Node* node) const { - if (node == nullptr) return {}; - return index(*node); - } - - - const Node& operator[](Node_index index) const { - return m_nodes[index]; - } - - Node& operator[](Node_index index) { - return m_nodes[index]; - } - /*! \brief returns the deepest level reached by a leaf node in this tree (root being level 0). */ @@ -542,10 +519,6 @@ class Orthtree { subset inside the node, but the bounding box of the node itself (cubic). */ - Bbox bbox(const Node& node) const { - return bbox(index(node)); - } - Bbox bbox(Node_index n) const { // Determine the side length of this node @@ -702,7 +675,6 @@ class Orthtree { bool is_leaf(Node_index n) const { return !m_node_children[n].has_value(); - return m_nodes[n].is_leaf(); } bool is_root(Node_index n) const { @@ -711,22 +683,18 @@ class Orthtree { std::size_t depth(Node_index n) const { return m_node_depths[n]; - return m_nodes[n].depth(); } typename Node::Point_range& points(Node_index n) { return m_node_points[n]; - return m_nodes[n].points(); } const typename Node::Point_range& points(Node_index n) const { return m_node_points[n]; - return m_nodes[n].points(); } typename Node::Global_coordinates global_coordinates(Node_index n) const { return m_node_coordinates[n]; - return m_nodes[n].global_coordinates(); } typename Node::Local_coordinates local_coordinates(Node_index n) const { @@ -734,7 +702,6 @@ class Orthtree { for (std::size_t i = 0; i < Dimension::value; ++i) result[i] = global_coordinates(n)[i] & 1; return result; - return m_nodes[n].local_coordinates(); } /*! @@ -744,13 +711,11 @@ class Orthtree { Node_index parent(Node_index node) const { CGAL_precondition (!is_root(node)); return m_node_parents[node].get(); - return m_nodes[node].m_parent_index.get(); } Node_index child(Node_index node, std::size_t i) const { CGAL_precondition (!is_leaf(node)); return m_node_children[node].get() + i; - return m_nodes[node].m_children_index.get() + i; } const Maybe_node_index next_sibling(Node_index n) const { @@ -832,7 +797,6 @@ class Orthtree { using Local_coordinates = typename Node::Local_coordinates; m_node_children[n] = m_node_properties.emplace_group(Degree::value); for (std::size_t i = 0; i < Degree::value; i++) { - m_nodes.emplace_back(n, global_coordinates(n), depth(n) + 1, Local_coordinates{i}); Node_index c = m_node_children[n].get() + i; @@ -842,12 +806,9 @@ class Orthtree { Local_coordinates local_coordinates{i}; for (int i = 0; i < Dimension::value; i++) m_node_coordinates[c][i] = (2 * m_node_coordinates[n][i]) + local_coordinates[i]; - CGAL_assertion(m_node_coordinates[c] == m_nodes.back().global_coordinates()); m_node_depths[c] = m_node_depths[n] + 1; m_node_parents[c] = n; } - // todo: this assumes that the new nodes are always allocated at the end - m_nodes[n].m_children_index = m_nodes.size() - Degree::value; // Find the point around which the node is split Point center = barycenter(n); @@ -864,7 +825,6 @@ class Orthtree { * Idempotent, un-splitting a leaf node has no effect. */ void unsplit(Node_index n) { - m_nodes[n].m_children_index.reset(); // todo: the child nodes should be de-allocated! } @@ -1265,15 +1225,14 @@ class Orthtree { } friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { - // Create a range of nodes - auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal(orthtree)); - // Iterate over the range - for (auto& n: nodes) { + // Iterate over all nodes + for (auto n: orthtree.traverse_indices(Orthtrees::Preorder_traversal(orthtree))) { // Show the depth - for (int i = 0; i < n.depth(); ++i) + for (int i = 0; i < orthtree.depth(n); ++i) os << ". "; // Print the node - os << n << std::endl; + internal::print_orthtree_node(os, n, orthtree); + os << std::endl; } return os; } diff --git a/Orthtree/include/CGAL/Orthtree/IO.h b/Orthtree/include/CGAL/Orthtree/IO.h index 3c5748397c9d..58155fefb064 100644 --- a/Orthtree/include/CGAL/Orthtree/IO.h +++ b/Orthtree/include/CGAL/Orthtree/IO.h @@ -22,46 +22,39 @@ namespace CGAL namespace internal { -template -std::ostream& print_orthtree_node(std::ostream& os, const Node& node) +template +std::ostream& print_orthtree_node(std::ostream& os, const Node_index& node, const Tree &tree) { - // Show the depth of the node -// for (int i = 0; i < node.depth(); ++i) -// os << ". "; // Wrap information in brackets os << "{ "; // Index identifies which child this is os << "("; - for (std::size_t i = 0; i < node.local_coordinates().size(); ++ i) - os << node.local_coordinates()[i]; + for (std::size_t i = 0; i < tree.local_coordinates(node).size(); ++ i) + os << tree.local_coordinates(node)[i]; os << ") "; // Location os << "( "; - for (const auto& b : node.global_coordinates()) + for (const auto& b : tree.global_coordinates(node)) os << b << " "; os << ") "; // Depth os << "(" - << +node.depth() // The + forces printing as an int instead of a char + << +tree.depth(node) // The + forces printing as an int instead of a char << ") "; os << "(" - << node.size() + << tree.points(node).size() << ") "; -// // If a node has points, indicate how many -// if (!node.is_empty()) -// os << "[" << node.num_points() << " points] "; - // If a node is a leaf, mark it - os << (node.is_leaf() ? "[leaf] " : ""); + os << (tree.is_leaf(node) ? "[leaf] " : ""); // If a node is root, mark it - os << (node.is_root() ? "[root] " : ""); + os << (tree.is_root(node) ? "[root] " : ""); // Wrap information in brackets os << "}"; diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h index c94a1019c4c3..2fbe9e56a773 100644 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ b/Orthtree/include/CGAL/Orthtree/Node.h @@ -230,10 +230,6 @@ class Orthtree::Node { * \return whether the nodes have different topology. */ bool operator==(const Self& rhs) const = default; // todo: this doesn't work in C++17 - - friend std::ostream& operator<<(std::ostream& os, const Self& node) { - return internal::print_orthtree_node(os, node); - } /// \endcond }; diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index a150f4581217..86e231872b9e 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -62,7 +62,7 @@ int main(void) { assert(!octree.adjacent_node(left_top_back, Traits::UP)); assert(!octree.adjacent_node(left_top_back, Traits::BACK)); - std::cout << octree[octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK)] << std::endl; + //std::cout << octree[octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK)] << std::endl; auto right_top_back_of_left_bottom_back = octree.child(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK), Traits::RIGHT_TOP_BACK); From 774aa1f324de149ccdcc56dce1200899d7be9919 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 22 Jun 2023 15:24:39 +0200 Subject: [PATCH 067/520] Add node-access convenience functions, and simplify tests --- Orthtree/include/CGAL/Orthtree.h | 12 ++++ Orthtree/test/Orthtree/test_node_adjacent.cpp | 17 ++--- Orthtree/test/Orthtree/test_octree_bbox.cpp | 64 ++++++++--------- .../Orthtree/test_octree_intersecting.cpp | 26 ++++--- Orthtree/test/Orthtree/test_octree_locate.cpp | 68 +++++++++---------- Orthtree/test/Orthtree/test_octree_refine.cpp | 6 +- .../test/Orthtree/test_octree_traverse.cpp | 24 +++---- 7 files changed, 110 insertions(+), 107 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 6c185ad47509..99ac99bfc700 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -718,6 +718,18 @@ class Orthtree { return m_node_children[node].get() + i; } + Node_index descendant(Node_index node, std::size_t i) { return child(node, i); } + + template + Node_index descendant(Node_index node, std::size_t i, Indices... remaining_indices) { + return descendant(child(node, i), remaining_indices...); + } + + template + Node_index node(Indices... indices) { + return descendant(root(), indices...); + } + const Maybe_node_index next_sibling(Node_index n) const { // Root node has no siblings diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 86e231872b9e..5eeb1f17cb15 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -50,29 +50,26 @@ int main(void) { assert(!octree.adjacent_node(octree.root(), 5)); // Left Top Front node should have siblings to the Right, Down, and Back - auto left_top_back = octree.child(octree.root(), Traits::LEFT_TOP_BACK); + auto left_top_back = octree.node(Traits::LEFT_TOP_BACK); - assert(octree.child(octree.root(), Traits::RIGHT_TOP_BACK) == + assert(octree.node(Traits::RIGHT_TOP_BACK) == octree.adjacent_node(left_top_back, Traits::RIGHT).get()); - assert(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK) == + assert(octree.node(Traits::LEFT_BOTTOM_BACK) == octree.adjacent_node(left_top_back, Traits::DOWN).get()); - assert(octree.child(octree.root(), Traits::LEFT_TOP_FRONT) == + assert(octree.node(Traits::LEFT_TOP_FRONT) == octree.adjacent_node(left_top_back, Traits::FRONT)); assert(!octree.adjacent_node(left_top_back, Traits::LEFT)); assert(!octree.adjacent_node(left_top_back, Traits::UP)); assert(!octree.adjacent_node(left_top_back, Traits::BACK)); - //std::cout << octree[octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK)] << std::endl; - - auto right_top_back_of_left_bottom_back = - octree.child(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK), Traits::RIGHT_TOP_BACK); + auto right_top_back_of_left_bottom_back = octree.node(Traits::LEFT_BOTTOM_BACK, Traits::RIGHT_TOP_BACK); assert( - octree.child(octree.child(octree.root(), Traits::LEFT_BOTTOM_BACK), Traits::LEFT_TOP_BACK) == + octree.node(Traits::LEFT_BOTTOM_BACK, Traits::LEFT_TOP_BACK) == octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::LEFT) ); assert( - octree.child(octree.root(), Traits::RIGHT_BOTTOM_BACK) == + octree.node(Traits::RIGHT_BOTTOM_BACK) == octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT) ); assert(octree.adjacent_node(right_top_back_of_left_bottom_back, Traits::RIGHT).has_value()); diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 8c324de951a5..1e3718d59ef1 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -42,14 +42,14 @@ void test_9_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); // Compare the child nodes - assert(octree.bbox(octree.child(octree.root(), 0)) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); - assert(octree.bbox(octree.child(octree.root(), 1)) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); - assert(octree.bbox(octree.child(octree.root(), 2)) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); - assert(octree.bbox(octree.child(octree.root(), 3)) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); - assert(octree.bbox(octree.child(octree.root(), 4)) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); - assert(octree.bbox(octree.child(octree.root(), 5)) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); - assert(octree.bbox(octree.child(octree.root(), 6)) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); - assert(octree.bbox(octree.child(octree.root(), 7)) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.node(0)) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); + assert(octree.bbox(octree.node(1)) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); + assert(octree.bbox(octree.node(2)) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); + assert(octree.bbox(octree.node(3)) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); + assert(octree.bbox(octree.node(4)) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); + assert(octree.bbox(octree.node(5)) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); + assert(octree.bbox(octree.node(6)) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); + assert(octree.bbox(octree.node(7)) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); } void test_25_nodes() { @@ -69,49 +69,49 @@ void test_25_nodes() { assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); // Compare the child nodes - assert(octree.bbox(octree.child(octree.root(), 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); - assert(octree.bbox(octree.child(octree.root(), 1)) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); - assert(octree.bbox(octree.child(octree.root(), 2)) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); - assert(octree.bbox(octree.child(octree.root(), 3)) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); - assert(octree.bbox(octree.child(octree.root(), 4)) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); - assert(octree.bbox(octree.child(octree.root(), 5)) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); - assert(octree.bbox(octree.child(octree.root(), 6)) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); - assert(octree.bbox(octree.child(octree.root(), 7)) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.node(0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); + assert(octree.bbox(octree.node(1)) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); + assert(octree.bbox(octree.node(2)) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); + assert(octree.bbox(octree.node(3)) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); + assert(octree.bbox(octree.node(4)) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); + assert(octree.bbox(octree.node(5)) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); + assert(octree.bbox(octree.node(6)) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); + assert(octree.bbox(octree.node(7)) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); // Compare children of the first child - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 0)) == + assert(octree.bbox(octree.node(0, 0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 1)) == + assert(octree.bbox(octree.node(0, 1)) == CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 2)) == + assert(octree.bbox(octree.node(0, 2)) == CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 3)) == + assert(octree.bbox(octree.node(0, 3)) == CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 4)) == + assert(octree.bbox(octree.node(0, 4)) == CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 5)) == + assert(octree.bbox(octree.node(0, 5)) == CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 6)) == + assert(octree.bbox(octree.node(0, 6)) == CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 0), 7)) == + assert(octree.bbox(octree.node(0, 7)) == CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); // Compare children of the last child - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 0)) == + assert(octree.bbox(octree.node(7, 0)) == CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 1)) == + assert(octree.bbox(octree.node(7, 1)) == CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 2)) == + assert(octree.bbox(octree.node(7, 2)) == CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 3)) == + assert(octree.bbox(octree.node(7, 3)) == CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 4)) == + assert(octree.bbox(octree.node(7, 4)) == CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 5)) == + assert(octree.bbox(octree.node(7, 5)) == CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 6)) == + assert(octree.bbox(octree.node(7, 6)) == CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); - assert(octree.bbox(octree.child(octree.child(octree.root(), 7), 7)) == + assert(octree.bbox(octree.node(7, 7)) == CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 3470ee68b387..a5e385ea98af 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -68,10 +68,10 @@ int main(void) { // Check the results assert(4 == nodes.size()); - assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_BACK) == nodes[0]); - assert(octree.child(octree.root(), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[1]); - assert(octree.child(octree.root(), Octree::Traits::LEFT_TOP_FRONT) == nodes[2]); - assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); + assert(octree.node(Octree::Traits::RIGHT_TOP_BACK) == nodes[0]); + assert(octree.node(Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[1]); + assert(octree.node(Octree::Traits::LEFT_TOP_FRONT) == nodes[2]); + assert(octree.node(Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); } // Intersection with a ray @@ -86,19 +86,17 @@ int main(void) { // Check the results assert(8 == nodes.size()); - assert(octree.child(octree.root(), Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); + assert(octree.node(Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); assert( - octree.child(octree.child(octree.root(), - Octree::Traits::RIGHT_BOTTOM_BACK), - Octree::Traits::LEFT_TOP_FRONT) + octree.node(Octree::Traits::RIGHT_BOTTOM_BACK, Octree::Traits::LEFT_TOP_FRONT) == nodes[1] ); - assert(octree.child(octree.root(), Octree::Traits::LEFT_TOP_BACK) == nodes[2]); - assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); - assert(octree.child(octree.root(), Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); - assert(octree.child(octree.root(), Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); - assert(octree.child(octree.root(), Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); - assert(octree.child(octree.root(), Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); + assert(octree.node(Octree::Traits::LEFT_TOP_BACK) == nodes[2]); + assert(octree.node(Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); + assert(octree.node(Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); + assert(octree.node(Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); + assert(octree.node(Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); + assert(octree.node(Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); } return EXIT_SUCCESS; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index e3214a8ab116..cffd6c0a3d41 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -52,24 +52,24 @@ void test_8_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.child(octree.root(), 0) == octree.locate({-1, -1, -1})); - assert(octree.child(octree.root(), 1) == octree.locate({1, -1, -1})); - assert(octree.child(octree.root(), 2) == octree.locate({-1, 1, -1})); - assert(octree.child(octree.root(), 3) == octree.locate({1, 1, -1})); - assert(octree.child(octree.root(), 4) == octree.locate({-1, -1, 1})); - assert(octree.child(octree.root(), 5) == octree.locate({1, -1, 1})); - assert(octree.child(octree.root(), 6) == octree.locate({-1, 1, 1})); - assert(octree.child(octree.root(), 7) == octree.locate({1, 1, 1})); + assert(octree.node(0) == octree.locate({-1, -1, -1})); + assert(octree.node(1) == octree.locate({1, -1, -1})); + assert(octree.node(2) == octree.locate({-1, 1, -1})); + assert(octree.node(3) == octree.locate({1, 1, -1})); + assert(octree.node(4) == octree.locate({-1, -1, 1})); + assert(octree.node(5) == octree.locate({1, -1, 1})); + assert(octree.node(6) == octree.locate({-1, 1, 1})); + assert(octree.node(7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points should also end up in the same place - assert(octree.child(octree.root(), 0) == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.child(octree.root(), 1) == octree.locate({1.1, -1.1, -1.1})); - assert(octree.child(octree.root(), 2) == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.child(octree.root(), 3) == octree.locate({1.1, 1.1, -1.1})); - assert(octree.child(octree.root(), 4) == octree.locate({-1.1, -1.1, 1.1})); - assert(octree.child(octree.root(), 5) == octree.locate({1.1, -1.1, 1.1})); - assert(octree.child(octree.root(), 6) == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.child(octree.root(), 7) == octree.locate({1.1, 1.1, 1.1})); + assert(octree.node(0) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.node(1) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.node(2) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.node(3) == octree.locate({1.1, 1.1, -1.1})); + assert(octree.node(4) == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.node(5) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.node(6) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.node(7) == octree.locate({1.1, 1.1, 1.1})); } @@ -93,28 +93,24 @@ void test_10_points() { octree.refine(10, 1); // Existing points should end up in the same place - assert(octree.child(octree.root(), 0) == octree.locate({-1, -1, -1})); - assert(octree.child(octree.root(), 1) == octree.locate({1, -1, -1})); - assert(octree.child(octree.root(), 2) == octree.locate({-1, 1, -1})); - assert(octree.child(octree.child(octree.child(octree.root(), 3), 3), 3) == - octree.locate({1, 1, -1})); - assert(octree.child(octree.child(octree.child(octree.root(), 4), 4), 4) == - octree.locate({-1, -1, 1})); - assert(octree.child(octree.root(), 5) == octree.locate({1, -1, 1})); - assert(octree.child(octree.root(), 6) == octree.locate({-1, 1, 1})); - assert(octree.child(octree.root(), 7) == octree.locate({1, 1, 1})); + assert(octree.node(0) == octree.locate({-1, -1, -1})); + assert(octree.node(1) == octree.locate({1, -1, -1})); + assert(octree.node(2) == octree.locate({-1, 1, -1})); + assert(octree.node(3, 3, 3) == octree.locate({1, 1, -1})); + assert(octree.node(4, 4, 4) == octree.locate({-1, -1, 1})); + assert(octree.node(5) == octree.locate({1, -1, 1})); + assert(octree.node(6) == octree.locate({-1, 1, 1})); + assert(octree.node(7) == octree.locate({1, 1, 1})); // Points adjacent to the existing points might end up in different places - assert(octree.child(octree.root(), 0) == octree.locate({-1.1, -1.1, -1.1})); - assert(octree.child(octree.root(), 1) == octree.locate({1.1, -1.1, -1.1})); - assert(octree.child(octree.root(), 2) == octree.locate({-1.1, 1.1, -1.1})); - assert(octree.child(octree.child(octree.child(octree.root(), 3), 3), 3) == - octree.locate({1.1, 1.1, -1.1})); - assert(octree.child(octree.child(octree.child(octree.root(), 4), 4), 4) == - octree.locate({-1.1, -1.1, 1.1})); - assert(octree.child(octree.root(), 5) == octree.locate({1.1, -1.1, 1.1})); - assert(octree.child(octree.root(), 6) == octree.locate({-1.1, 1.1, 1.1})); - assert(octree.child(octree.root(), 7) == octree.locate({1.1, 1.1, 1.1})); + assert(octree.node(0) == octree.locate({-1.1, -1.1, -1.1})); + assert(octree.node(1) == octree.locate({1.1, -1.1, -1.1})); + assert(octree.node(2) == octree.locate({-1.1, 1.1, -1.1})); + assert(octree.node(3, 3, 3) == octree.locate({1.1, 1.1, -1.1})); + assert(octree.node(4, 4, 4) == octree.locate({-1.1, -1.1, 1.1})); + assert(octree.node(5) == octree.locate({1.1, -1.1, 1.1})); + assert(octree.node(6) == octree.locate({-1.1, 1.1, 1.1})); + assert(octree.node(7) == octree.locate({1.1, 1.1, 1.1})); } diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 2e248262a118..ffdefe14a5ca 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -79,14 +79,14 @@ void test_4_points() { // The octree should have been split once on the first level, and twice on the second Octree other(points, points.point_map()); other.split(other.root()); - other.split(other.child(other.root(), 3)); - other.split(other.child(other.root(), 7)); + other.split(other.node(3)); + other.split(other.node(7)); assert(Octree::is_topology_equal(other, octree)); assert(2 == octree.depth()); // Applying another splitting criterion shouldn't reset the tree. octree.refine(Split_nth_child_of_root(2)); - other.split(other.child(other.root(), 2)); + other.split(other.node(2)); assert(Octree::is_topology_equal(other, octree)); } diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 5ad71f57f537..85217f77f92b 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -54,7 +54,7 @@ bool test_preorder_9_nodes() { assert(*iter == octree.root()); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.child(octree.root(), i)); + assert(*iter == octree.node(i)); } return true; @@ -77,7 +77,7 @@ bool test_level_9_nodes() { // Check each item in the range auto iter = nodes.begin(); for (int i = 0; i < 8; ++i) { - assert(*iter == octree.child(octree.root(), i)); + assert(*iter == octree.node(i)); iter++; } @@ -104,28 +104,28 @@ bool test_preorder_25_nodes() { auto iter = nodes.begin(); assert(*iter == octree.root()); iter++; - assert(*iter == octree.child(octree.root(), 0)); + assert(*iter == octree.node(0)); iter++; - assert(*iter == octree.child(octree.root(), 1)); + assert(*iter == octree.node(1)); iter++; - assert((*iter == octree.child(octree.root(), 2))); + assert((*iter == octree.node(2))); iter++; - assert(*iter == octree.child(octree.root(), 3)); + assert(*iter == octree.node(3)); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.child(octree.child(octree.root(), 3), i)); + assert(*iter == octree.node(3, i)); } iter++; - assert((*iter == octree.child(octree.root(), 4))); + assert((*iter == octree.node(4))); iter++; - assert((*iter == octree.child(octree.root(), 5))); + assert((*iter == octree.node(5))); iter++; - assert((*iter == octree.child(octree.root(), 6))); + assert((*iter == octree.node(6))); iter++; - assert((*iter == octree.child(octree.root(), 7))); + assert((*iter == octree.node(7))); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.child(octree.child(octree.root(), 7), i)); + assert(*iter == octree.node(7, i)); } return true; From a2ed96dd34051cf79baa3679baf901fba73f6d92 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 23 Jun 2023 19:21:31 -0600 Subject: [PATCH 068/520] added insertion of odd-even constraints and creation from different polygon types --- .../Polygon_repair_2/repair_polygon_2.cpp | 44 +++++++++++++ .../Multipolygon_with_holes_2.h | 5 ++ .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 45 +++++++++++--- ...riangulation_with_odd_even_constraints_2.h | 62 ++++++++++++++++++- 4 files changed, 146 insertions(+), 10 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index aa66fb706c3c..e4828301db73 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -7,13 +7,57 @@ // #include typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::Point_2 Point; +typedef CGAL::Polygon_2 Polygon; typedef CGAL::Multipolygon_with_holes_2 Multipolygon; typedef CGAL::Polygon_repair_2::Polygon_repair_2 Polygon_repair; int main(int argc, char* argv[]) { // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); + // Square + // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon p(ps, ps+4); + + // Bowtie + // Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; + // Polygon p(ps, ps+4); + + // Overlapping edge + // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon p1(ps1, ps1+4); + // Point ps2[] = {Point(1,0), Point(2,0), Point(2,1), Point(1,1)}; + // Polygon p2(ps2, ps2+4); + // Multipolygon mp; + // mp.add_polygon(p1); + // mp.add_polygon(p2); + + // Edge partly overlapping (start) + // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon p1(ps1, ps1+4); + // Point ps2[] = {Point(1,0), Point(2,0), Point(2,0.5), Point(1,0.5)}; + // Polygon p2(ps2, ps2+4); + // Multipolygon mp; + // mp.add_polygon(p1); + // mp.add_polygon(p2); + + // Edge partly overlapping (middle) + Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + Polygon p1(ps1, ps1+4); + Point ps2[] = {Point(1,0.25), Point(2,0.25), Point(2,0.75), Point(1,0.75)}; + Polygon p2(ps2, ps2+4); + Multipolygon mp; + mp.add_polygon(p1); + mp.add_polygon(p2); + Polygon_repair pr; + pr.add_to_triangulation(mp); + + for (auto const& ce: pr.triangulation().constrained_edges()) { + std::cout << ce.first->vertex(ce.first->cw(ce.second))->point() << " to " << ce.first->vertex(ce.first->ccw(ce.second))->point() << std::endl; + } + + // CGAL::Polygon_repair_2::repair(mp); return 0; } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index bc279b9c279f..ea5f737e163e 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -33,6 +33,9 @@ class Multipolygon_with_holes_2 { public: /// \name Definition + /// polygon type + typedef CGAL::Polygon_2 Polygon_2; + /// polygon with holes type typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; @@ -64,6 +67,8 @@ class Multipolygon_with_holes_2 { Polygon_const_iterator polygons_end() const { return m_polygons.end(); } + void add_polygon(const Polygon_2& pgn) { m_polygons.push_back(Polygon_with_holes_2(pgn)); } + void add_polygon(const Polygon_with_holes_2& pgn) { m_polygons.push_back(pgn); } void add_polygon(Polygon_with_holes_2&& pgn) { m_polygons.emplace_back(std::move(pgn)); } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 9308ef52e002..a50fee4192e1 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -32,6 +32,10 @@ class Polygon_repair_2; template Multipolygon_with_holes_2 repair(const CGAL::Polygon_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; + pr.add_to_triangulation(p); + pr.label_triangulation(); + pr.reconstruct_polygon(); + return pr.multipolygon(); } /// \ingroup PkgPolygonRepair2Functions @@ -39,13 +43,21 @@ Multipolygon_with_holes_2 repair(const CGAL::Polygon_2 template Multipolygon_with_holes_2 repair(const CGAL::Polygon_with_holes_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; + pr.add_to_triangulation(p); + pr.label_triangulation(); + pr.reconstruct_polygon(); + return pr.multipolygon(); } /// \ingroup PkgPolygonRepair2Functions /// Repair a multipolygon with holes template -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p) { +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; + pr.add_to_triangulation(mp); + pr.label_triangulation(); + pr.reconstruct_polygon(); + return pr.multipolygon(); } /*! \ingroup PkgPolygonRepair2Ref @@ -84,24 +96,35 @@ class Polygon_repair_2 { // Add edges of the polygon to the triangulation void add_to_triangulation(const Polygon_2& p) { - + for (auto const& e: p.edges()) { + t.odd_even_insert_constraint(e.source(), e.target()); + } } // Add edges of the polygon to the triangulation void add_to_triangulation(const Polygon_with_holes_2& p) { - + add_to_triangulation(p.outer_boundary()); + for (auto const& h: p.holes()) { + add_to_triangulation(h); + } } // Add edges of the polygon to the triangulation - void add_to_triangulation(const Multipolygon_with_holes_2& p) { - + void add_to_triangulation(const Multipolygon_with_holes_2& mp) { + for (auto const &p: mp.polygons()) { + add_to_triangulation(p); + } } // Label triangles in triangulation as inside or outside the polygon - void label_triangulation(); + void label_triangulation() { + + } // Reconstruct multipolygon based on the triangles labelled as inside the polygon - void reconstruct_polygon(); + void reconstruct_polygon() { + + } // Erases the triangulation. void clear() { @@ -113,9 +136,13 @@ class Polygon_repair_2 { /// \name Access Functions /// @{ - Triangulation_with_odd_even_constraints_2 triangulation(); + Triangulation_with_odd_even_constraints_2& triangulation() { + return t; + } + + Multipolygon_with_holes_2 multipolygon() { - Multipolygon_with_holes_2 multipolygon(); + } /// @} diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index fc74b1730134..84f239dad34a 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -30,8 +30,24 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { /// the triangulation class. typedef Triangulation_ Base_triangulation; - /// handle to a vertex. + /// Point type + typedef typename Triangulation_::Point Point; + + /// Edge type + typedef typename Triangulation_::Edge Edge; + + /// handle to a vertex typedef typename Triangulation_::Vertex_handle Vertex_handle; + + /// handle to a face + typedef typename Triangulation_::Face_handle Face_handle; + + /// list of edges + typedef typename Triangulation_::List_edges List_edges; + + /// list of faces + typedef typename Triangulation_::List_faces List_faces; + /// @} // iterator over interior faces. @@ -40,9 +56,53 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { Interior_faces_iterator operator--(); }; + // Inserts point p in the triangulation and returns the corresponding vertex. + Vertex_handle insert(const Point &p, Face_handle f = Face_handle()) { + return Base_triangulation::insert(p, f); + } + // Add constraint from va to vb using the odd-even rule void odd_even_insert_constraint(Vertex_handle va, Vertex_handle vb) { + + // Degenerate edge if (va == vb) return; + + // [va, vb] is an existing edge OR it is composed of shorter edges + Vertex_handle vc; // [va, vc] is the first edge along [va, vb] + Face_handle incident_face; // incident to [va, vc] + int opposite_vertex; // opposite to [va, vc] + if (Base_triangulation::includes_edge(va, vb, vc, incident_face, opposite_vertex)) { + if (Base_triangulation::is_constrained(Edge(incident_face, opposite_vertex))) { + Base_triangulation::remove_constrained_edge(incident_face, opposite_vertex); + } else Base_triangulation::mark_constraint(incident_face, opposite_vertex); + if (vc != vb) odd_even_insert_constraint(vc, vb); // process edges along [vc, vb] + return; + } + + // [va, vb] intersects a constrained edge or an existing vertex + List_faces intersected_faces; + List_edges conflict_boundary_ab, conflict_boundary_ba; + Vertex_handle intersection; + if (Base_triangulation::find_intersected_faces(va, vb, intersected_faces, conflict_boundary_ab, conflict_boundary_ba, intersection)) { + if (intersection != va && intersection != vb) { + odd_even_insert_constraint(va, intersection); + odd_even_insert_constraint(intersection, vb); + } else odd_even_insert_constraint(va, vb); + return; + } + + // Otherwise + Base_triangulation::triangulate_hole(intersected_faces, conflict_boundary_ab, conflict_boundary_ba); + if (intersection != vb) { + odd_even_insert_constraint(intersection, vb); + } + } + + // Add constraint from pa to pb using the odd-even rule + void odd_even_insert_constraint(Point pa, Point pb) { + Vertex_handle va = insert(pa); + Vertex_handle vb = insert(pb); + odd_even_insert_constraint(va, vb); } // Starts at an arbitrary interior face From 01aad4af7ac624658808b928c7af8cf2f3ffb731 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 25 Jun 2023 12:42:24 +0200 Subject: [PATCH 069/520] Add a minimal test of custom node properties --- Orthtree/include/CGAL/Orthtree.h | 45 ++++++++++++++--- Orthtree/test/Orthtree/CMakeLists.txt | 1 + .../test_octree_custom_properties.cpp | 49 +++++++++++++++++++ 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 Orthtree/test/Orthtree/test_octree_custom_properties.cpp diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 99ac99bfc700..ac7ee409b9e6 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -126,7 +126,7 @@ class Orthtree { /*! * \brief A predicate that determines whether a node must be split when refining a tree. */ - typedef std::function Split_predicate; + typedef std::function Split_predicate; /*! * \brief A model of `ConstRange` whose value type is `Node`. @@ -163,11 +163,11 @@ class Orthtree { PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */ Node_property_container m_node_properties; - Node_property_container::Array> &m_node_points; - Node_property_container::Array &m_node_depths; - Node_property_container::Array> &m_node_coordinates; - Node_property_container::Array &m_node_parents; - Node_property_container::Array &m_node_children; + Node_property_container::Array >& m_node_points; + Node_property_container::Array & m_node_depths; + Node_property_container::Array >& m_node_coordinates; + Node_property_container::Array & m_node_parents; + Node_property_container::Array & m_node_children; Point m_bbox_min; /* input bounding box min value */ @@ -282,7 +282,7 @@ class Orthtree { m_node_children(m_node_properties.get_property("children")) {} // move constructor - Orthtree(Orthtree&& other): + Orthtree(Orthtree&& other) : m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(std::move(other.m_node_properties)), @@ -468,6 +468,8 @@ class Orthtree { This method allows to iterate on the nodes of the tree with a user-selected order (preorder, postorder, leaves-only, etc.). + todo: this should be removed, replaced with traverse_indices + \tparam Traversal model of `OrthtreeTraversal` that provides functions compatible with the type of the orthree @@ -541,6 +543,35 @@ class Orthtree { /// @} + /// \name Custom Properties + /// @{ + + template + std::pair>, bool> + get_or_add_node_property(const std::string& name, const T default_value = T()) { + return m_node_properties.get_or_add_property(name, default_value); + } + + template + Node_property_container::Array & add_node_property(const std::string& name, const T default_value = T()) { + return m_node_properties.add_property(name, default_value); + } + + template + Node_property_container::Array & get_node_property(const std::string& name) { + return m_node_properties.get_property(name); + } + + template + std::optional>> + get_node_property_if_exists(const std::string& name) { + return m_node_properties.get_property_if_exists(name); + } + + // todo: is it ever useful to be able to delete/reset properties? + + /// @} + /// \name Queries /// @{ diff --git a/Orthtree/test/Orthtree/CMakeLists.txt b/Orthtree/test/Orthtree/CMakeLists.txt index 6776af021578..c483d861c739 100644 --- a/Orthtree/test/Orthtree/CMakeLists.txt +++ b/Orthtree/test/Orthtree/CMakeLists.txt @@ -16,6 +16,7 @@ create_single_source_cgal_program("test_octree_traverse.cpp") create_single_source_cgal_program("test_octree_intersecting.cpp") create_single_source_cgal_program("test_octree_copy_move_constructors.cpp") create_single_source_cgal_program("test_octree_kernels.cpp") +create_single_source_cgal_program("test_octree_custom_properties.cpp") create_single_source_cgal_program("test_node_index.cpp") create_single_source_cgal_program("test_node_adjacent.cpp") diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp new file mode 100644 index 000000000000..0ca1dafab130 --- /dev/null +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -0,0 +1,49 @@ +#define CGAL_TRACE_STREAM std::cerr + +#include +#include +#include +#include + +#include +#include + +typedef CGAL::Simple_cartesian Kernel; +typedef Kernel::Point_3 Point; +typedef Kernel::FT FT; +typedef CGAL::Point_set_3 Point_set; +typedef CGAL::Octree Octree; + +int main(void) { + + std::size_t nb_pts = 100; + Point_set points; + CGAL::Random_points_in_cube_3 generator; + points.reserve(nb_pts); + for (std::size_t i = 0; i < nb_pts; ++i) + points.insert(*(generator++)); + + Octree tree(points, points.point_map()); + + // Default value should be respected + auto &node_int_property = tree.add_node_property("int", 5); + assert(node_int_property[tree.root()] == 5); + + // Changes to individual nodes should be respected + node_int_property[tree.root()] = 0; + assert(node_int_property[tree.root()] == 0); + + // Expanding the tree; new nodes should be assigned the default value + tree.refine(10, 1); + for (auto n : tree.traverse_indices>()) { + // Everything but the root will have the default value + if (!tree.is_root(n)) assert(node_int_property[n] == 5); + } + // The root should have preserved its custom value + assert(node_int_property[tree.root()] == 0); + + + + + return EXIT_SUCCESS; +} From c41faf127498ec78c4cf59322472994b124a4abb Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 25 Jun 2023 13:54:19 +0200 Subject: [PATCH 070/520] Delete independent `Node` type, move relevant typedefs to Orthtree.h --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- Orthtree/doc/Orthtree/PackageDescription.txt | 2 +- Orthtree/include/CGAL/Orthtree.h | 82 ++++--- Orthtree/include/CGAL/Orthtree/Node.h | 238 ------------------- Orthtree/test/Orthtree/test_node.cpp | 55 ----- 5 files changed, 55 insertions(+), 324 deletions(-) delete mode 100644 Orthtree/include/CGAL/Orthtree/Node.h delete mode 100644 Orthtree/test/Orthtree/test_node.cpp diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 0a957a6fa512..060bda6aaa5f 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -73,7 +73,7 @@ split. \subsection Section_Orthtree_Point_Vector Building an Octree -The `Orthtree` class may be templated with `Orthtree_traits_3` and thus +The `Orthtree` class may be templated with `Orthtree_traits_point_set_3` and thus behave as an %octree. For convenience, the alias `Octree` is provided. The following example shows how to create an %octree from a vector of diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 22b02350771a..cb9864363ef4 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -48,7 +48,7 @@ \cgalCRPSection{Traits} - `CGAL::Orthtree_traits_2` -- `CGAL::Orthtree_traits_3` +- `CGAL::Orthtree_traits_point_set_3` - `CGAL::Orthtree_traits_d` \cgalCRPSection{Split Predicates} diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ac7ee409b9e6..34a15ab0fc1b 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -85,6 +85,7 @@ class Orthtree { typedef typename Traits::Point_d Point; ///< Point type. typedef typename Traits::Bbox_d Bbox; ///< Bounding box type. typedef typename Traits::Sphere_d Sphere; ///< Sphere type. + typedef typename Traits::Adjacency Adjacency; ///< Adjacency type. /// \cond SKIP_IN_MANUAL typedef typename Traits::Array Array; ///< Array type. @@ -93,6 +94,12 @@ class Orthtree { /// \endcond /// @} + /// \name Types specific to Point_set_3 + /// todo: should be moved to Traits + /// @{ + typedef boost::iterator_range Node_point_range; + /// @} + /// \name Public Types /// @{ @@ -111,15 +118,36 @@ class Orthtree { */ typedef std::size_t Node_index; - typedef Properties::Property_container Node_property_container; - /*! * \brief Optional index of a node in the tree. */ typedef boost::optional Maybe_node_index; + // todo: maybe this could be private? + typedef Properties::Property_container Node_property_container; + + /*! + \brief Set of bits representing this node's relationship to its parent. + + Equivalent to an array of Booleans, where index[0] is whether `x` + is greater, index[1] is whether `y` is greater, index[2] is whether + `z` is greater, and so on for higher dimensions if needed. + Used to represent a node's relationship to the center of its parent. + */ + typedef std::bitset Local_coordinates; + + /*! + \brief Coordinates representing this node's relationship + with the rest of the tree. + + Each value `(x, y, z, ...)` of global coordinates is calculated by doubling + the parent's global coordinates and adding the local coordinates. + */ + typedef std::array Global_coordinates; + /*! * \brief The Sub-tree / Orthant type. + * todo: this should be removed */ class Node; @@ -163,9 +191,9 @@ class Orthtree { PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */ Node_property_container m_node_properties; - Node_property_container::Array >& m_node_points; + Node_property_container::Array & m_node_points; Node_property_container::Array & m_node_depths; - Node_property_container::Array >& m_node_coordinates; + Node_property_container::Array & m_node_coordinates; Node_property_container::Array & m_node_parents; Node_property_container::Array & m_node_children; @@ -212,9 +240,9 @@ class Orthtree { const FT enlarge_ratio = 1.2, Traits traits = Traits()) : m_traits(traits), m_range(point_range), m_point_map(point_map), - m_node_points(m_node_properties.add_property>("points")), + m_node_points(m_node_properties.add_property("points")), m_node_depths(m_node_properties.add_property("depths", 0)), - m_node_coordinates(m_node_properties.add_property>("coordinates")), + m_node_coordinates(m_node_properties.add_property("coordinates")), m_node_parents(m_node_properties.add_property("parents")), m_node_children(m_node_properties.add_property("children")) { @@ -275,9 +303,9 @@ class Orthtree { m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(other.m_node_properties), - m_node_points(m_node_properties.get_property>("points")), + m_node_points(m_node_properties.get_property("points")), m_node_depths(m_node_properties.get_property("depths")), - m_node_coordinates(m_node_properties.get_property>("coordinates")), + m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), m_node_children(m_node_properties.get_property("children")) {} @@ -286,9 +314,9 @@ class Orthtree { m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(std::move(other.m_node_properties)), - m_node_points(m_node_properties.get_property>("points")), + m_node_points(m_node_properties.get_property("points")), m_node_depths(m_node_properties.get_property("depths")), - m_node_coordinates(m_node_properties.get_property>("coordinates")), + m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), m_node_children(m_node_properties.get_property("children")) { @@ -312,10 +340,9 @@ class Orthtree { /*! \brief recursively subdivides the orthtree until it meets the given criteria. - todo: split predicate now works with node indices! - The split predicate is a `std::function` that takes a `Node` and - returns a Boolean value (where `true` implies that a `Node` needs to - be split, `false` that the `Node` should be a leaf). + The split predicate is an `std::function` that takes a `Node_index` and an Orthtree reference, and + returns a Boolean value (where `true` implies that the corresponding node needs to + be split, `false` that the node should be a leaf). This function may be called several times with different predicates: in that case, nodes already split are left unaltered, @@ -600,7 +627,7 @@ class Orthtree { Point center = barycenter(node_for_point); // Find the index of the correct sub-node - typename Node::Local_coordinates local_coords; + Local_coordinates local_coords; std::size_t dimension = 0; for (const auto& r: cartesian_range(center, point)) local_coords[dimension++] = (get < 0 > (r) < get < 1 > (r)); @@ -657,7 +684,7 @@ class Orthtree { This function finds all the intersecting nodes and returns them as const pointers. \tparam Query the primitive class (e.g. sphere, ray) - \tparam OutputIterator a model of `OutputIterator` that accepts `Node` objects + \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types \param query the intersecting primitive. \param output output iterator. */ @@ -676,7 +703,6 @@ class Orthtree { Trees may be considered equivalent even if they contain different points. Equivalent trees must have the same bounding box and the same node structure. - Node structure is evaluated by comparing the root nodes using the node equality operator. */ bool operator==(const Self& rhs) const { @@ -716,20 +742,20 @@ class Orthtree { return m_node_depths[n]; } - typename Node::Point_range& points(Node_index n) { + Node_point_range& points(Node_index n) { return m_node_points[n]; } - const typename Node::Point_range& points(Node_index n) const { + const Node_point_range& points(Node_index n) const { return m_node_points[n]; } - typename Node::Global_coordinates global_coordinates(Node_index n) const { + Global_coordinates global_coordinates(Node_index n) const { return m_node_coordinates[n]; } - typename Node::Local_coordinates local_coordinates(Node_index n) const { - typename Node::Local_coordinates result; + Local_coordinates local_coordinates(Node_index n) const { + Local_coordinates result; for (std::size_t i = 0; i < Dimension::value; ++i) result[i] = global_coordinates(n)[i] & 1; return result; @@ -770,7 +796,7 @@ class Orthtree { std::size_t local_coords = local_coordinates(n).to_ulong(); // todo: add local_coordinates(n) helper // The last child has no more siblings - if (int(local_coords) == Node::Degree::value - 1) + if (int(local_coords) == Degree::value - 1) return {}; // The next sibling is the child of the parent with the following local coordinates @@ -815,7 +841,7 @@ class Orthtree { return node; if (!is_leaf(node)) - for (int i = 0; i < Node::Degree::value; ++i) + for (int i = 0; i < Degree::value; ++i) todo.push(child(node, i)); } @@ -837,7 +863,7 @@ class Orthtree { CGAL_precondition (is_leaf(n)); // Split the node to create children - using Local_coordinates = typename Node::Local_coordinates; + using Local_coordinates = Local_coordinates; m_node_children[n] = m_node_properties.emplace_group(Degree::value); for (std::size_t i = 0; i < Degree::value; i++) { @@ -966,7 +992,7 @@ class Orthtree { \return the index of the adjacent node if it exists, nothing otherwise. */ - Maybe_node_index adjacent_node(Node_index n, typename Node::Local_coordinates direction) const { + Maybe_node_index adjacent_node(Node_index n, Local_coordinates direction) const { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 @@ -1012,7 +1038,7 @@ class Orthtree { /*! \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. */ - Maybe_node_index adjacent_node(Node_index n, typename Node::Adjacency adjacency) const { + Maybe_node_index adjacent_node(Node_index n, Adjacency adjacency) const { return adjacent_node(n, std::bitset(static_cast(adjacency))); } @@ -1286,6 +1312,4 @@ class Orthtree { } // namespace CGAL -#include - #endif // CGAL_ORTHTREE_H diff --git a/Orthtree/include/CGAL/Orthtree/Node.h b/Orthtree/include/CGAL/Orthtree/Node.h deleted file mode 100644 index 2fbe9e56a773..000000000000 --- a/Orthtree/include/CGAL/Orthtree/Node.h +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) 2007-2020 INRIA (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) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao - -#ifndef CGAL_ORTHTREE_NODE_H -#define CGAL_ORTHTREE_NODE_H - -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace CGAL { - -/*! - - \brief represents a single node of the tree. Alternatively referred - to as a cell, orthant, or sub-tree. - - A `Node` is a lightweight object and thus generally passed by - copy. It is also a model of `ConstRange` with value type `Traits::Point_d`. - - \cgalModels `ConstRange` - */ -template -class Orthtree::Node { - -public: - - /// \name Types - /// @{ - - typedef Orthtree Enclosing; ///< Orthtree type (enclosing class). - typedef typename Enclosing::Dimension Dimension; ///< Dimension type. - typedef typename Enclosing::Degree Degree; ///< Degree type. - typedef typename Enclosing::Node_index Node_index; ///< Index type. - typedef typename Enclosing::Maybe_node_index Maybe_node_index; ///< Index type. - - /*! - \brief Self typedef for convenience. - */ - typedef typename Orthtree::Node Self; - - /*! - \brief Set of bits representing this node's relationship to its parent. - - Equivalent to an array of Booleans, where index[0] is whether `x` - is greater, index[1] is whether `y` is greater, index[2] is whether - `z` is greater, and so on for higher dimensions if needed. - Used to represent a node's relationship to the center of its parent. - */ - typedef std::bitset Local_coordinates; - - /*! - \brief Coordinates representing this node's relationship - with the rest of the tree. - - Each value `(x, y, z, ...)` of global coordinates is calculated by doubling - the parent's global coordinates and adding the local coordinates. - */ - typedef std::array Global_coordinates; - - - typedef typename PointRange::const_iterator const_iterator; ///< constant iterator type. - - /*! - \brief Adjacency directions. - */ - typedef typename Traits::Adjacency Adjacency; - - /// @} - -private: - - /// \cond SKIP_IN_MANUAL - /*! - \brief point range type. - */ - typedef typename PointRange::iterator iterator; ///< constant iterator type. - typedef boost::iterator_range Point_range; - /// \endcond - - Point_range m_points; - std::uint8_t m_depth = 0; - Global_coordinates m_global_coordinates{}; - - Maybe_node_index m_parent_index{}; - Maybe_node_index m_children_index{}; - - // Only the Orthtree class has access to the non-default - // constructor, mutators, etc. - friend Enclosing; - -public: - - /// \name Construction - /// @{ - - /// \cond SKIP_IN_MANUAL - Node() = default; // constructs a root node - /// \endcond - - /*! - \brief creates a new node, optionally as the child of a parent - - If no parent is provided, the node created is assumed to be the - root of a tree. This means that `parent.is_null()` returns - `true`, and the depth is zero. If a parent is provided, the node - becomes the child of that parent. In that case, an index should - be passed, telling this node its relationship to its parent. - Depth and global coordinates are automatically determined in the - constructor, and should generally be considered immutable after - construction. - - \param parent the node containing this one - \param index this node's relationship to its parent - */ - explicit Node(Node_index parent_index, Global_coordinates parent_coordinates, - std::size_t depth, Local_coordinates local_coordinates) : - m_parent_index(parent_index), m_depth(depth) { - - for (int i = 0; i < Dimension::value; i++) - m_global_coordinates[i] = (2 * parent_coordinates[i]) + local_coordinates[i]; - - } - - /// @} - -public: - - /// \name Member Access - /// @{ - - /*! - * \brief Access to the content held by this node - * \return a reference to the collection of point indices - */ - Point_range& points() { return m_points; } - - const Point_range& points() const { return m_points; } - - /// @} - - /// \name Type & Location - /// @{ - - /*! - \brief returns `true` if the node has no parent, `false` otherwise. - \pre `!is_null()` - */ - bool is_root() const { - return !m_parent_index.has_value(); - } - - /*! - \brief returns `true` if the node has no children, `false` otherwise. - \pre `!is_null()` - */ - bool is_leaf() const { - return !m_children_index.has_value(); - } - - /*! - \brief returns this node's depth. - \pre `!is_null()` - */ - std::uint8_t depth() const { - return m_depth; - } - - /*! - \brief returns this node's local coordinates (in relation to its parent). - \pre `!is_null()` - */ - Local_coordinates local_coordinates() const { - - Local_coordinates result; - - for (std::size_t i = 0; i < Dimension::value; ++i) - result[i] = global_coordinates()[i] & 1; - - return result; - } - - /*! - \brief returns this node's global coordinates. - \pre `!is_null()` - */ - Global_coordinates global_coordinates() const { - return m_global_coordinates; - } - - /// @} - - /// \name Point Range - /// @{ - - /*! - \brief returns the number of points of this node. - */ - std::size_t size() const { - return std::size_t(std::distance(m_points.begin(), m_points.end())); - } - - /// @} - - /// \cond SKIP_IN_MANUAL - /// \name Operators - /// @{ - - /*! - * \brief compares the topology of this node to another node. - * - * \param rhs node to compare with - * \return whether the nodes have different topology. - */ - bool operator==(const Self& rhs) const = default; // todo: this doesn't work in C++17 - /// \endcond -}; - -} - -#endif //CGAL_ORTHTREE_NODE_H diff --git a/Orthtree/test/Orthtree/test_node.cpp b/Orthtree/test/Orthtree/test_node.cpp deleted file mode 100644 index 13e3a3f07285..000000000000 --- a/Orthtree/test/Orthtree/test_node.cpp +++ /dev/null @@ -1,55 +0,0 @@ - -#include -#include -#include -#include - -typedef CGAL::Orthtree::Node::iterator> Node; - -int main(void) { - - // Build a new node - Node n = Node(); - - // Check that its values are correct - assert(n.is_root()); - assert(n.is_leaf()); - assert(!n.parent()); - assert(n.depth() == 0); - assert(n.location()[0] == 0 && n.location()[1] == 0 && n.location()[2] == 0); - - // Split the node - n.split(); - - // Check that it's children's values are also correct - for (std::size_t i = 0; i < 8; ++i) { - - assert(!n[i].is_root()); - assert(n[i].is_leaf()); - assert(*n[i].parent() == n); - assert(n[i].depth() == 1); - } - - // Check that the parent has updated - assert(n.is_root()); - assert(!n.is_leaf()); - - // Split one of the children - n[1].split(); - - // Check each of that child's children - for (std::size_t i = 0; i < 8; ++i) { - - assert(!n[1][i].is_root()); - assert(n[1][i].is_leaf()); - assert(*n[1][i].parent() == n[1]); - assert(*n[1][i].parent()->parent() == n); - assert(n[1][i].depth() == 2); - } - - // Check that the child's values have updated - assert(!n[1].is_root()); - assert(!n[1].is_leaf()); - - return 0; -} From b08eabae93057231f0307a015cb5d85d5d19a253 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 26 Jun 2023 16:59:02 +0200 Subject: [PATCH 071/520] Move point-specific functionality to traits class (only Point_3 is currently supported) --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- Orthtree/doc/Orthtree/PackageDescription.txt | 2 +- Orthtree/include/CGAL/Octree.h | 3 +- Orthtree/include/CGAL/Orthtree.h | 132 ++------- Orthtree/include/CGAL/Orthtree/Traversals.h | 2 +- .../include/CGAL/Orthtree_traits_point_3.h | 258 ++++++++++++++++++ Orthtree/test/Orthtree/test_node_adjacent.cpp | 2 +- Orthtree/test/Orthtree/test_node_index.cpp | 2 +- Orthtree/test/Orthtree/test_octree_bbox.cpp | 6 +- .../test_octree_copy_move_constructors.cpp | 2 +- .../test_octree_custom_properties.cpp | 2 +- .../test/Orthtree/test_octree_equality.cpp | 12 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 2 +- .../Orthtree/test_octree_intersecting.cpp | 2 +- .../test/Orthtree/test_octree_kernels.cpp | 2 +- Orthtree/test/Orthtree/test_octree_locate.cpp | 6 +- .../Orthtree/test_octree_nearest_neighbor.cpp | 12 +- Orthtree/test/Orthtree/test_octree_refine.cpp | 10 +- .../test/Orthtree/test_octree_traverse.cpp | 8 +- 19 files changed, 325 insertions(+), 142 deletions(-) create mode 100644 Orthtree/include/CGAL/Orthtree_traits_point_3.h diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 060bda6aaa5f..d32afbb0234c 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -73,7 +73,7 @@ split. \subsection Section_Orthtree_Point_Vector Building an Octree -The `Orthtree` class may be templated with `Orthtree_traits_point_set_3` and thus +The `Orthtree` class may be templated with `Orthtree_traits_point_3` and thus behave as an %octree. For convenience, the alias `Octree` is provided. The following example shows how to create an %octree from a vector of diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index cb9864363ef4..818358a29c88 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -48,7 +48,7 @@ \cgalCRPSection{Traits} - `CGAL::Orthtree_traits_2` -- `CGAL::Orthtree_traits_point_set_3` +- `CGAL::Orthtree_traits_point_3` - `CGAL::Orthtree_traits_d` \cgalCRPSection{Split Predicates} diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 848b2731c730..1d6e8318ff8c 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -16,6 +16,7 @@ #include #include +#include namespace CGAL { @@ -43,7 +44,7 @@ template < #ifdef DOXYGEN_RUNNING class Octree; #else -using Octree = Orthtree, PointRange, PointMap>; +using Octree = Orthtree>; #endif } // namespace CGAL diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 34a15ab0fc1b..7abd20858fff 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -62,11 +62,8 @@ namespace CGAL { \sa `CGAL::Octree` \tparam Traits_ must be a model of `OrthtreeTraits` - \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap_` - \tparam PointMap_ must be a model of `ReadablePropertyMap` whose value type is `Traits_::Point_d` */ -template > +template class Orthtree { public: @@ -74,8 +71,6 @@ class Orthtree { /// \name Template Types /// @{ typedef Traits_ Traits; ///< Geometry traits - typedef PointRange_ PointRange; ///< Point range - typedef PointMap_ PointMap; ///< Point map /// @} /// \name Traits Types @@ -87,6 +82,9 @@ class Orthtree { typedef typename Traits::Sphere_d Sphere; ///< Sphere type. typedef typename Traits::Adjacency Adjacency; ///< Adjacency type. + typedef typename Traits::Node_data Node_data; + typedef typename Traits::Node_data_element Node_data_element; + /// \cond SKIP_IN_MANUAL typedef typename Traits::Array Array; ///< Array type. typedef typename Traits::Construct_point_d_from_array Construct_point_d_from_array; @@ -94,19 +92,13 @@ class Orthtree { /// \endcond /// @} - /// \name Types specific to Point_set_3 - /// todo: should be moved to Traits - /// @{ - typedef boost::iterator_range Node_point_range; - /// @} - /// \name Public Types /// @{ /*! * \brief Self typedef for convenience. */ - typedef Orthtree Self; + typedef Orthtree Self; /*! * \brief Degree of the tree (number of children of non-leaf nodes). @@ -168,17 +160,6 @@ class Orthtree { #endif /// \cond SKIP_IN_MANUAL - - /*! - * \brief A function that determines the next node in a traversal given the current one. - */ - typedef std::function Node_traversal_method_const; - - /// \endcond - - /// \cond SKIP_IN_MANUAL - typedef typename PointRange::iterator Range_iterator; - typedef typename std::iterator_traits::value_type Range_type; typedef Orthtrees::internal::Cartesian_ranges Cartesian_ranges; /// \endcond @@ -187,11 +168,9 @@ class Orthtree { private: // data members : Traits m_traits; /* the tree traits */ - PointRange& m_range; /* input point range */ - PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */ Node_property_container m_node_properties; - Node_property_container::Array & m_node_points; + Node_property_container::Array & m_node_points; Node_property_container::Array & m_node_depths; Node_property_container::Array & m_node_coordinates; Node_property_container::Array & m_node_parents; @@ -235,12 +214,10 @@ class Orthtree { \param enlarge_ratio ratio to which the bounding box should be enlarged. \param traits the traits object. */ - Orthtree(PointRange& point_range, - PointMap point_map = PointMap(), - const FT enlarge_ratio = 1.2, - Traits traits = Traits()) : - m_traits(traits), m_range(point_range), m_point_map(point_map), - m_node_points(m_node_properties.add_property("points")), + Orthtree(Traits traits, + const FT enlarge_ratio = 1.2) : + m_traits(traits), + m_node_points(m_node_properties.add_property("points")), m_node_depths(m_node_properties.add_property("depths", 0)), m_node_coordinates(m_node_properties.add_property("coordinates")), m_node_parents(m_node_properties.add_property("parents")), @@ -248,30 +225,11 @@ class Orthtree { m_node_properties.emplace(); - Array bbox_min; - Array bbox_max; // init bbox with first values found - { - const Point& p = get(m_point_map, *(point_range.begin())); - std::size_t i = 0; - for (const FT& x: cartesian_range(p)) { - bbox_min[i] = x; - bbox_max[i] = x; - ++i; - } - } - - for (const Range_type& r: point_range) { - const Point& p = get(m_point_map, r); - std::size_t i = 0; - for (const FT& x: cartesian_range(p)) { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++i; - } - } + auto [bbox_min, bbox_max] = m_traits.root_node_bbox(); + // Dilate the bounding box Array bbox_centroid; FT max_length = FT(0); for (std::size_t i = 0; i < Dimension::value; ++i) { @@ -279,7 +237,6 @@ class Orthtree { max_length = (std::max)(max_length, bbox_max[i] - bbox_min[i]); } max_length *= enlarge_ratio / FT(2); - for (std::size_t i = 0; i < Dimension::value; ++i) { bbox_min[i] = bbox_centroid[i] - max_length; bbox_max[i] = bbox_centroid[i] + max_length; @@ -291,7 +248,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = construct_point_d_from_array(bbox_min); m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - points(root()) = {point_range.begin(), point_range.end()}; + points(root()) = m_traits.root_node_contents(); } /// @} @@ -300,10 +257,10 @@ class Orthtree { // copy constructor Orthtree(const Orthtree& other) : - m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), + m_traits(other.m_traits), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(other.m_node_properties), - m_node_points(m_node_properties.get_property("points")), + m_node_points(m_node_properties.get_property("points")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), @@ -311,10 +268,10 @@ class Orthtree { // move constructor Orthtree(Orthtree&& other) : - m_traits(other.m_traits), m_range(other.m_range), m_point_map(other.m_point_map), + m_traits(other.m_traits), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(std::move(other.m_node_properties)), - m_node_points(m_node_properties.get_property("points")), + m_node_points(m_node_properties.get_property("points")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), @@ -742,11 +699,11 @@ class Orthtree { return m_node_depths[n]; } - Node_point_range& points(Node_index n) { + Node_data& points(Node_index n) { return m_node_points[n]; } - const Node_point_range& points(Node_index n) const { + const Node_data& points(Node_index n) const { return m_node_points[n]; } @@ -883,7 +840,8 @@ class Orthtree { Point center = barycenter(n); // Add the node's points to its children - reassign_points(n, points(n).begin(), points(n).end(), center); + m_traits.distribute_node_contents(n, *this, center); + //reassign_points(n, points(n).begin(), points(n).end(), center); } /*! @@ -1046,37 +1004,6 @@ class Orthtree { private: // functions : - void reassign_points(Node_index n, Range_iterator begin, Range_iterator end, const Point& center, - std::bitset coord = {}, - std::size_t dimension = 0) { - - // Root case: reached the last dimension - if (dimension == Dimension::value) { - points(child(n, coord.to_ulong())) = {begin, end}; - return; - } - - // Split the point collection around the center point on this dimension - Range_iterator split_point = std::partition( - begin, end, - [&](const Range_type& a) -> bool { - // This should be done with cartesian iterator, - // but it seems complicated to do efficiently - return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); - } - ); - - // Further subdivide the first side of the split - std::bitset coord_left = coord; - coord_left[dimension] = false; - reassign_points(n, begin, split_point, center, coord_left, dimension + 1); - - // Further subdivide the second side of the split - std::bitset coord_right = coord; - coord_right[dimension] = true; - reassign_points(n, split_point, end, center, coord_right, dimension + 1); - } - bool do_intersect(Node_index n, const Sphere& sphere) const { // Create a cubic bounding box from the node @@ -1087,8 +1014,8 @@ class Orthtree { } // TODO: There has to be a better way than using structs like these! - struct Point_with_distance { - Point point; + struct Node_element_with_distance { + Node_data_element point; FT distance; }; @@ -1102,7 +1029,7 @@ class Orthtree { }; void nearest_k_neighbors_recursive(Sphere& search_bounds, Node_index node, - std::vector& results, FT epsilon = 0) const { + std::vector& results, FT epsilon = 0) const { // Check whether the node has children if (is_leaf(node)) { @@ -1111,14 +1038,11 @@ class Orthtree { // Loop through each of the points contained by the node // Note: there might be none, and that should be fine! - for (auto point_index: points(node)) { - - // Retrieve each point from the orthtree's point map - auto point = get(m_point_map, point_index); + for (auto p: points(node)) { // Pair that point with its distance from the search point - Point_with_distance current_point_with_distance = - {point, squared_distance(point, search_bounds.center())}; + Node_element_with_distance current_point_with_distance = + {p, squared_distance(m_traits.get_element(p), search_bounds.center())}; // Check if the new point is within the bounds if (current_point_with_distance.distance < search_bounds.squared_radius()) { @@ -1226,7 +1150,7 @@ class Orthtree { OutputIterator nearest_k_neighbors_in_radius(Sphere& query_sphere, std::size_t k, OutputIterator output) const { // Create an empty list of points - std::vector points_list; + std::vector points_list; if (k != (std::numeric_limits::max)()) points_list.reserve(k); diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 3c5cf2dc43f9..708faa435e07 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -25,7 +25,7 @@ namespace CGAL { /// \cond SKIP_IN_MANUAL // todo: is this necessary? // Forward declaration -template +template class Orthtree; /// \endcond diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h new file mode 100644 index 000000000000..b016dc945a3d --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -0,0 +1,258 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H + + +#include + +#include +#include +#include +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_point_3` can be used as a template parameter of + the `Orthtree` class. + + \tparam GeomTraits model of `Kernel`. + \tparam Point_set must be a model of range whose value type is the key type of `Point_map` + \tparam Point_map must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` + + \cgalModels `OrthtreeTraits` + \sa `CGAL::Octree` + \sa `CGAL::Orthtree_traits_2` + \sa `CGAL::Orthtree_traits_d` +*/ +template < + typename GeomTraits, + typename Point_set, + typename Point_map = Identity_property_map +> +struct Orthtree_traits_point_3 { +public: + + /// \name Types + /// @{ + + using Self = Orthtree_traits_point_3; + + using Dimension = Dimension_tag<3>; + using Bbox_d = Bbox_3; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_3; + using Sphere_d = typename GeomTraits::Sphere_3; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_3; + using Array = std::array; // todo: This should have a more descriptive name + + // todo: looking for better names + using Node_data = boost::iterator_range; + using Node_data_element = typename std::iterator_traits::value_type; + + + /*! + * \brief Two directions along each axis in Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 3-bit integers, + * though the numbers 6 and 7 are not used because there are only 6 different directions. + * + * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), + * the third bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram may be a useful reference: + * + * 3 * + * | * 5 + * | / y+ + * |/ * z+ + * 0 *------+------* 1 | * + * /| |/ + * / | +-----* x+ + * 4 * | + * * 2 + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 000 | 0 | LEFT | + * | `+x` | 001 | 1 | RIGHT | + * | `-y` | 010 | 2 | DOWN | + * | `+y` | 011 | 3 | UP | + * | `-z` | 100 | 4 | BACK | + * | `+z` | 101 | 5 | FRONT | + */ + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP, + BACK, + FRONT + }; + + /// \cond SKIP_IN_MANUAL + enum Child { + LEFT_BOTTOM_BACK, + RIGHT_BOTTOM_BACK, + LEFT_TOP_BACK, + RIGHT_TOP_BACK, + LEFT_BOTTOM_FRONT, + RIGHT_BOTTOM_FRONT, + LEFT_TOP_FRONT, + RIGHT_TOP_FRONT + }; + /// \endcond + +#ifdef DOXYGEN_RUNNING + /*! + Functor with an operator to construct a `Point_d` from an `Array` object. + */ + typedef unspecified_type Construct_point_d_from_array; +#else + + struct Construct_point_d_from_array { + Point_d operator()(const Array& array) const { + return Point_d(array[0], array[1], array[2]); + } + }; + +#endif + +#ifdef DOXYGEN_RUNNING + /*! + Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). + */ + typedef unspecified_type Construct_bbox_d; +#else + + struct Construct_bbox_d { + Bbox_d operator()(const Array& min, + const Array& max) const { + return Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); + } + }; + +#endif + + /// @} + + Orthtree_traits_point_3( + Point_set& point_set, + Point_map point_map = Point_map() + ) : m_point_set(point_set), m_point_map(point_map) {} + + /// \name Operations + /// The user shouldn't need to define these themselves + /// @{ + + /*! + Function used to construct an object of type `Construct_point_d_from_array`. + */ + Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } + + /*! + Function used to construct an object of type `Construct_bbox_d`. + */ + Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } + + std::pair root_node_bbox() const { + + Array bbox_min; + Array bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const Point_d& point = get(m_point_map, *(m_point_set.begin())); + std::size_t i = 0; + for (const FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } + } + // Expand bbox to contain all points + for (const auto& p: m_point_set) { + const Point_d& point = get(m_point_map, p); + std::size_t i = 0; + for (const FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } + } + + return {bbox_min, bbox_max}; + } + + Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + + template + void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(n, tree, center, tree.points(n)); + } + + Point_d get_element(const Node_data_element& index) const { + return get(m_point_map, index); + } + + /// @} + +private: + + Point_set& m_point_set; + Point_map m_point_map; + + template + void reassign_points(Node_index n, Tree& tree, + const Point_d& center, Node_data points, + std::bitset coord = {}, + std::size_t dimension = 0) { + + // Root case: reached the last dimension + if (dimension == Dimension::value) { + tree.points(tree.child(n, coord.to_ulong())) = points; + return; + } + + // Split the point collection around the center point on this dimension + auto split_point = std::partition( + points.begin(), points.end(), + [&](const auto& p) -> bool { + // This should be done with cartesian iterator, + // but it seems complicated to do efficiently + return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); + } + ); + + // Further subdivide the first side of the split + std::bitset coord_left = coord; + coord_left[dimension] = false; + reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); + + // Further subdivide the second side of the split + std::bitset coord_right = coord; + coord_right[dimension] = true; + reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); + } + +}; + +} + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 5eeb1f17cb15..29f5b91dfdcd 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -36,7 +36,7 @@ int main(void) { points.insert({-1, -1, -1.8}); points.insert({-1, -1, -1.9}); - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); std::cout << octree << std::endl; diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index 34e6f24f5c62..2390c2c11476 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -34,7 +34,7 @@ int main(void) { points.insert({-1, -1, -1.8}); points.insert({-1, -1, -1.9}); - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); std::cout << "root: " << octree.local_coordinates(octree.root()) << std::endl; diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 1e3718d59ef1..c36e0498474f 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -20,7 +20,7 @@ void test_1_node() { points.insert({-1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Compare the top (only) node @@ -35,7 +35,7 @@ void test_9_nodes() { points.insert({1, 1, 1}); // Create the octree - Octree octree(points, points.point_map(), 1.1); + Octree octree({points, points.point_map()}, 1.1); octree.refine(10, 1); // Compare the top node @@ -62,7 +62,7 @@ void test_25_nodes() { points.insert({1, 0.5, 1}); // Create the octree - Octree octree(points, points.point_map(), 1.5); + Octree octree({points, points.point_map()}, 1.5); octree.refine(10, 1); // Compare the top node diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index 2b4dd730e47d..ee7c8f3be524 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -24,7 +24,7 @@ int main(void) for (std::size_t i = 0; i < nb_pts; ++i) points.insert(*(generator++)); - Octree base (points, points.point_map()); + Octree base ({points, points.point_map()}); assert (base.is_leaf(base.root())); // base is not refined yet Octree copy1 (base); diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 0ca1dafab130..cb1139a7c042 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -23,7 +23,7 @@ int main(void) { for (std::size_t i = 0; i < nb_pts; ++i) points.insert(*(generator++)); - Octree tree(points, points.point_map()); + Octree tree({points, points.point_map()}); // Default value should be respected auto &node_int_property = tree.add_node_property("int", 5); diff --git a/Orthtree/test/Orthtree/test_octree_equality.cpp b/Orthtree/test/Orthtree/test_octree_equality.cpp index 1eb88263774e..e73a5cf3cbd3 100644 --- a/Orthtree/test/Orthtree/test_octree_equality.cpp +++ b/Orthtree/test/Orthtree/test_octree_equality.cpp @@ -26,8 +26,8 @@ void test_identical_trees() { points.insert({1, 1, 1}); // Create a pair of trees from the same point set - Octree a(points, points.point_map()); - Octree b(points, points.point_map()); + Octree a({points, points.point_map()}); + Octree b({points, points.point_map()}); // Refine both trees using the same criteria a.refine(10, 1); @@ -52,8 +52,8 @@ void test_identical_contents_different_criteria() { points.insert({1, 1, 1}); // Create a pair of trees from the same point set - Octree a(points, points.point_map()); - Octree b(points, points.point_map()); + Octree a({points, points.point_map()}); + Octree b({points, points.point_map()}); // Refine both trees using different criteria a.refine(10, 1); @@ -87,8 +87,8 @@ void test_different_contents_identical_criteria() { points_b.insert({1, 1, 2}); // Create a pair of trees from the different point sets - Octree a(points_a, points_a.point_map()); - Octree b(points_b, points_b.point_map()); + Octree a({points_a, points_a.point_map()}); + Octree b({points_b, points_b.point_map()}); // Refine both trees using the same criteria a.refine(10, 1); diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index da3b438e3fd0..9c0fed16e791 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -48,7 +48,7 @@ void test(std::size_t dataset_size) { points.insert(*(generator++)); // Build an octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Refine the octree octree.refine(); diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index a5e385ea98af..5f1fbc3ee75b 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -34,7 +34,7 @@ int main(void) { points.emplace_back(-0.9, -1, -1); // Create an octree from the vector - Octree octree(points); + Octree octree(Octree::Traits{points}); // Build the octree octree.refine(10, 2); diff --git a/Orthtree/test/Orthtree/test_octree_kernels.cpp b/Orthtree/test/Orthtree/test_octree_kernels.cpp index 58d49eb4fccd..20a2d616aa7d 100644 --- a/Orthtree/test/Orthtree/test_octree_kernels.cpp +++ b/Orthtree/test/Orthtree/test_octree_kernels.cpp @@ -18,7 +18,7 @@ void test() for (std::size_t i = 0; i < 100; ++i) points.insert(*(generator++)); - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(); octree.grade(); } diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index cffd6c0a3d41..9407afe8d3b8 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -22,7 +22,7 @@ void test_1_point() { points.insert({-1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Because there's only the root node, any point should be placed in it @@ -48,7 +48,7 @@ void test_8_points() { points.insert({1, 1, 1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Existing points should end up in the same place @@ -89,7 +89,7 @@ void test_10_points() { points.insert({-1, -0.75, 1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Existing points should end up in the same place diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index bec700dbc46b..df58254cb80f 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -68,13 +68,13 @@ void naive_vs_octree(std::size_t dataset_size) { // Do the same using the octree Point octree_nearest = *generator; - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); { - std::vector k_neighbors; + std::vector k_neighbors; octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); - octree_nearest = *k_neighbors.begin(); + octree_nearest = get(points.point_map(), *k_neighbors.begin()); } duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; @@ -119,8 +119,8 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { << std::endl; // Do the same using the octree - std::vector octree_nearest_neighbors; - Octree octree(points, points.point_map()); + std::vector octree_nearest_neighbors; + Octree octree({points, points.point_map()}); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); @@ -136,7 +136,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { // Check that they produce the same answer for (std::size_t j = 0; j < K; ++j) - assert(octree_nearest_neighbors[j] == kd_tree_nearest_neighbors[j]); + assert(get(points.point_map(), octree_nearest_neighbors[j]) == kd_tree_nearest_neighbors[j]); } diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index ffdefe14a5ca..afea2e7734ba 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -38,7 +38,7 @@ void test_1_point() { points.insert({-1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Check that the root node was never split @@ -54,11 +54,11 @@ void test_2_points() { points.insert({1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // The octree should have been split once - Octree other(points, points.point_map()); + Octree other({points, points.point_map()}); other.split(other.root()); assert(Octree::is_topology_equal(other, octree)); assert(1 == octree.depth()); @@ -73,11 +73,11 @@ void test_4_points() { points.insert({1, 1, 4}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // The octree should have been split once on the first level, and twice on the second - Octree other(points, points.point_map()); + Octree other({points, points.point_map()}); other.split(other.root()); other.split(other.node(3)); other.split(other.node(7)); diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 85217f77f92b..0d442051faf5 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -22,7 +22,7 @@ bool test_preorder_1_node() { points.insert({-1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Create the range @@ -43,7 +43,7 @@ bool test_preorder_9_nodes() { points.insert({1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Create the range @@ -68,7 +68,7 @@ bool test_level_9_nodes() { points.insert({1, -1, -1}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Create the range @@ -94,7 +94,7 @@ bool test_preorder_25_nodes() { points.insert({1, 1, 4}); // Create the octree - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(10, 1); // Create the range From 22e76aac653a74dda4b5e9da8b858133f09a1f3d Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Mon, 26 Jun 2023 18:07:17 +0200 Subject: [PATCH 072/520] Adapt 2D & dD traits for the new system There's a lot of duplicated code in the traits classes, at the very least `reassign_points` should be shared. --- Orthtree/examples/Orthtree/orthtree_build.cpp | 1 - Orthtree/include/CGAL/Octree.h | 1 - Orthtree/include/CGAL/Orthtree_traits_2.h | 143 ----------- Orthtree/include/CGAL/Orthtree_traits_3.h | 160 ------------ Orthtree/include/CGAL/Orthtree_traits_d.h | 137 ---------- .../include/CGAL/Orthtree_traits_point_2.h | 240 ++++++++++++++++++ .../include/CGAL/Orthtree_traits_point_3.h | 24 +- .../include/CGAL/Orthtree_traits_point_d.h | 197 ++++++++++++++ Orthtree/include/CGAL/Quadtree.h | 4 +- 9 files changed, 450 insertions(+), 457 deletions(-) delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_2.h delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_3.h delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_d.h create mode 100644 Orthtree/include/CGAL/Orthtree_traits_point_2.h create mode 100644 Orthtree/include/CGAL/Orthtree_traits_point_d.h diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index f553eccc695c..69d479e1a8bf 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -2,7 +2,6 @@ #include #include -#include #include // Type Declarations diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 1d6e8318ff8c..b0b247f00a4d 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -15,7 +15,6 @@ #include #include -#include #include namespace CGAL { diff --git a/Orthtree/include/CGAL/Orthtree_traits_2.h b/Orthtree/include/CGAL/Orthtree_traits_2.h deleted file mode 100644 index 0ef87d2ce6e7..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_2.h +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2020 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Simon Giraudot - -#ifndef CGAL_ORTHTREE_TRAITS_2_H -#define CGAL_ORTHTREE_TRAITS_2_H - -#include - -#include -#include - -namespace CGAL -{ - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_2` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels `OrthtreeTraits` - \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_3` - \sa `CGAL::Orthtree_traits_d` -*/ -template -struct Orthtree_traits_2 -{ -public: - - /// \name Types - /// @{ - - typedef Dimension_tag<2> Dimension; ///< Dimension type. - typedef Bbox_2 Bbox_d; ///< Bounding box type. - typedef typename GeomTraits::FT FT; ///< Number type. - typedef typename GeomTraits::Point_2 Point_d; ///< Point type. - typedef typename GeomTraits::Circle_2 Sphere_d; ///< Sphere type. - typedef typename GeomTraits::Cartesian_const_iterator_2 Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates. - typedef std::array Array; ///< Array type. - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 2-bit integers. - * - * The first bit indicates the axis (0 = x, 1 = y), - * the second bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | - * | y+ - * | * - * 0 *------+------* 1 | - * | | - * | +-----* x+ - * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 00 | 0 | LEFT | - * | `+x` | 01 | 1 | RIGHT | - * | `-y` | 10 | 2 | DOWN | - * | `+y` | 11 | 3 | UP | - */ - enum Adjacency - { - LEFT, - RIGHT, - DOWN, - UP - }; - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - Point_d operator() (const Array& array) const - { - return Point_d (array[0], array[1]); - } - }; -#endif - - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - Bbox_d operator() (const Array& min, - const Array& max) const - { - return Bbox_d (min[0], min[1], max[0], max[1]); - } - }; -#endif - - /// @} - - /// \name Operations - /// @{ - - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const - { return Construct_point_d_from_array(); } - - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const - { return Construct_bbox_d(); } - - /// @} -}; - -} - -#endif // CGAL_ORTHTREE_TRAITS_2_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_3.h b/Orthtree/include/CGAL/Orthtree_traits_3.h deleted file mode 100644 index 7cfb0723b27b..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_3.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2020 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Simon Giraudot - -#ifndef CGAL_ORTHTREE_TRAITS_3_H -#define CGAL_ORTHTREE_TRAITS_3_H - -#include - -#include -#include - -namespace CGAL -{ - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_3` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels `OrthtreeTraits` - \sa `CGAL::Octree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_d` -*/ -template -struct Orthtree_traits_3 -{ -public: - - /// \name Types - /// @{ - - typedef Dimension_tag<3> Dimension; ///< Dimension type. - typedef Bbox_3 Bbox_d; ///< Bounding box type. - typedef typename GeomTraits::FT FT; ///< Number type. - typedef typename GeomTraits::Point_3 Point_d; ///< Point type. - typedef typename GeomTraits::Sphere_3 Sphere_d; ///< Sphere type. - typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates. - typedef std::array Array; ///< Array type. - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 3-bit integers, - * though the numbers 6 and 7 are not used because there are only 6 different directions. - * - * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), - * the third bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | * 5 - * | / y+ - * |/ * z+ - * 0 *------+------* 1 | * - * /| |/ - * / | +-----* x+ - * 4 * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 000 | 0 | LEFT | - * | `+x` | 001 | 1 | RIGHT | - * | `-y` | 010 | 2 | DOWN | - * | `+y` | 011 | 3 | UP | - * | `-z` | 100 | 4 | BACK | - * | `+z` | 101 | 5 | FRONT | - */ - enum Adjacency - { - LEFT, - RIGHT, - DOWN, - UP, - BACK, - FRONT - }; - - /// \cond SKIP_IN_MANUAL - enum Child { - LEFT_BOTTOM_BACK, - RIGHT_BOTTOM_BACK, - LEFT_TOP_BACK, - RIGHT_TOP_BACK, - LEFT_BOTTOM_FRONT, - RIGHT_BOTTOM_FRONT, - LEFT_TOP_FRONT, - RIGHT_TOP_FRONT - }; - /// \endcond - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - Point_d operator() (const Array& array) const - { - return Point_d (array[0], array[1], array[2]); - } - }; -#endif - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - Bbox_d operator() (const Array& min, - const Array& max) const - { - return Bbox_d (min[0], min[1], min[2], max[0], max[1], max[2]); - } - }; -#endif - - /// @} - - /// \name Operations - /// @{ - - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const - { return Construct_point_d_from_array(); } - - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const - { return Construct_bbox_d(); } - - /// @} -}; - -} - -#endif // CGAL_ORTHTREE_TRAITS_3_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_d.h b/Orthtree/include/CGAL/Orthtree_traits_d.h deleted file mode 100644 index 5415487afeda..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_d.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2020 GeometryFactory (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Simon Giraudot - -#ifndef CGAL_ORTHTREE_TRAITS_D_H -#define CGAL_ORTHTREE_TRAITS_D_H - -#include - -#include - -namespace CGAL -{ - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_d` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - \tparam DimensionTag specialization of `CGAL::Dimension_tag`. - - \cgalModels `OrthtreeTraits` - \sa `CGAL::Orthtree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_3` -*/ - -template -struct Orthtree_traits_d -{ -public: - - /// \name Types - /// @{ - - typedef DimensionTag Dimension; ///< Dimension type. - typedef typename GeomTraits::FT FT; ///< Number type. - typedef typename GeomTraits::Point_d Point_d; ///< Point type. - typedef typename GeomTraits::Sphere_d Sphere_d; ///< Sphere type. - typedef typename GeomTraits::Cartesian_const_iterator_d Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates. - typedef std::array Array; ///< Array type. - -#ifdef DOXYGEN_RUNNING - typedef unspecified_type Bbox_d; ///< Bounding box type. -#else - class Bbox_d - { - Point_d m_min, m_max; - public: - - Bbox_d (const Point_d& pmin, const Point_d& pmax) - : m_min (pmin), m_max (pmax) - { } - - const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION () { return m_min; } - const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION () { return m_max; } - }; -#endif - - /*! - Adjacency type. - - \note This type is used to identify adjacency directions with - easily understandable keywords (left, right, up, etc.) and is thus - mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In - higher dimensions, such keywords do not exist and this type is - simply an integer. Conversions from this integer to bitsets still - works but do not provide any easier API for adjacency selection. - */ - typedef int Adjacency; - - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - Point_d operator() (const Array& array) const - { - return Point_d (array.begin(), array.end()); - } - }; -#endif - - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - Bbox_d operator() (const Array& min, - const Array& max) const - { - return Bbox_d (Point_d (min.begin(), min.end()), - Point_d (max.begin(), max.end())); - } - }; -#endif - - /// @} - - /// \name Operations - /// @{ - - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const - { return Construct_point_d_from_array(); } - - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const - { return Construct_bbox_d(); } - - /// @} -}; - -} - -#endif // CGAL_ORTHTREE_TRAITS_D_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h new file mode 100644 index 000000000000..96940dc02762 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -0,0 +1,240 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_2_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_2_H + +#include + +#include +#include +#include +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_point_2` can be used as a template parameter of + the `Orthtree` class. + + \tparam GeomTraits model of `Kernel`. + \tparam PointSet must be a model of range whose value type is the key type of `PointMap` + \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` + + \cgalModels `OrthtreeTraits` + \sa `CGAL::Octree` + \sa `CGAL::Orthtree_traits_2` + \sa `CGAL::Orthtree_traits_d` +*/ +template < + typename GeomTraits, + typename PointSet, + typename PointMap = Identity_property_map +> +struct Orthtree_traits_point_2 { +public: + + /// \name Types + /// @{ + + using Self = Orthtree_traits_point_2; + + using Dimension = Dimension_tag<2>; + using Bbox_d = Bbox_2; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_2; + using Sphere_d = typename GeomTraits::Sphere_2; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_2; + using Array = std::array; // todo: This should have a more descriptive name + + // todo: looking for better names + using Node_data = boost::iterator_range; + using Node_data_element = typename std::iterator_traits::value_type; + + /*! + * \brief Two directions along each axis in Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 2-bit integers. + * + * The first bit indicates the axis (0 = x, 1 = y), + * the second bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram may be a useful reference: + * + * 3 * + * | + * | y+ + * | * + * 0 *------+------* 1 | + * | | + * | +-----* x+ + * | + * * 2 + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 00 | 0 | LEFT | + * | `+x` | 01 | 1 | RIGHT | + * | `-y` | 10 | 2 | DOWN | + * | `+y` | 11 | 3 | UP | + */ + enum Adjacency + { + LEFT, + RIGHT, + DOWN, + UP + }; + +#ifdef DOXYGEN_RUNNING + /*! + Functor with an operator to construct a `Point_d` from an `Array` object. + */ + typedef unspecified_type Construct_point_d_from_array; +#else + struct Construct_point_d_from_array + { + Point_d operator() (const Array& array) const + { + return Point_d (array[0], array[1]); + } + }; +#endif + + +#ifdef DOXYGEN_RUNNING + /*! + Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). + */ + typedef unspecified_type Construct_bbox_d; +#else + struct Construct_bbox_d + { + Bbox_d operator() (const Array& min, + const Array& max) const + { + return Bbox_d (min[0], min[1], max[0], max[1]); + } + }; +#endif + + /// @} + + Orthtree_traits_point_2( + PointSet& point_set, + PointMap point_map = PointMap() + ) : m_point_set(point_set), m_point_map(point_map) {} + + /// \name Operations + /// @{ + + /*! + Function used to construct an object of type `Construct_point_d_from_array`. + */ + Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } + + /*! + Function used to construct an object of type `Construct_bbox_d`. + */ + Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } + + std::pair root_node_bbox() const { + + Array bbox_min; + Array bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const Point_d& point = get(m_point_map, *(m_point_set.begin())); + std::size_t i = 0; + for (const FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } + } + // Expand bbox to contain all points + for (const auto& p: m_point_set) { + const Point_d& point = get(m_point_map, p); + std::size_t i = 0; + for (const FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } + } + + return {bbox_min, bbox_max}; + } + + Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + + template + void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(n, tree, center, tree.points(n)); + } + + Point_d get_element(const Node_data_element& index) const { + return get(m_point_map, index); + } + + /// @} + +private: + + PointSet& m_point_set; + PointMap m_point_map; + + template + void reassign_points(Node_index n, Tree& tree, + const Point_d& center, Node_data points, + std::bitset coord = {}, + std::size_t dimension = 0) { + + // Root case: reached the last dimension + if (dimension == Dimension::value) { + tree.points(tree.child(n, coord.to_ulong())) = points; + return; + } + + // Split the point collection around the center point on this dimension + auto split_point = std::partition( + points.begin(), points.end(), + [&](const auto& p) -> bool { + // This should be done with cartesian iterator, + // but it seems complicated to do efficiently + return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); + } + ); + + // Further subdivide the first side of the split + std::bitset coord_left = coord; + coord_left[dimension] = false; + reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); + + // Further subdivide the second side of the split + std::bitset coord_right = coord; + coord_right[dimension] = true; + reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); + } + +}; + +} + + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_2_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index b016dc945a3d..2764c41795f0 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -12,7 +12,6 @@ #ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H #define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H - #include #include @@ -29,8 +28,8 @@ namespace CGAL { the `Orthtree` class. \tparam GeomTraits model of `Kernel`. - \tparam Point_set must be a model of range whose value type is the key type of `Point_map` - \tparam Point_map must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` + \tparam PointSet must be a model of range whose value type is the key type of `PointMap` + \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` \cgalModels `OrthtreeTraits` \sa `CGAL::Octree` @@ -39,8 +38,8 @@ namespace CGAL { */ template < typename GeomTraits, - typename Point_set, - typename Point_map = Identity_property_map + typename PointSet, + typename PointMap = Identity_property_map > struct Orthtree_traits_point_3 { public: @@ -48,7 +47,7 @@ struct Orthtree_traits_point_3 { /// \name Types /// @{ - using Self = Orthtree_traits_point_3; + using Self = Orthtree_traits_point_3; using Dimension = Dimension_tag<3>; using Bbox_d = Bbox_3; @@ -59,8 +58,8 @@ struct Orthtree_traits_point_3 { using Array = std::array; // todo: This should have a more descriptive name // todo: looking for better names - using Node_data = boost::iterator_range; - using Node_data_element = typename std::iterator_traits::value_type; + using Node_data = boost::iterator_range; + using Node_data_element = typename std::iterator_traits::value_type; /*! @@ -151,12 +150,11 @@ struct Orthtree_traits_point_3 { /// @} Orthtree_traits_point_3( - Point_set& point_set, - Point_map point_map = Point_map() + PointSet& point_set, + PointMap point_map = PointMap() ) : m_point_set(point_set), m_point_map(point_map) {} /// \name Operations - /// The user shouldn't need to define these themselves /// @{ /*! @@ -215,8 +213,8 @@ struct Orthtree_traits_point_3 { private: - Point_set& m_point_set; - Point_map m_point_map; + PointSet& m_point_set; + PointMap m_point_map; template void reassign_points(Node_index n, Tree& tree, diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h new file mode 100644 index 000000000000..23ebb8b7517a --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -0,0 +1,197 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_D_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_D_H + +#include + +#include +#include +#include +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_point_d` can be used as a template parameter of + the `Orthtree` class. + + \tparam GeomTraits model of `Kernel`. + \tparam DimensionTag specialization of `CGAL::Dimension_tag`. + \tparam PointSet must be a model of range whose value type is the key type of `Point_map` + \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` + + \cgalModels `OrthtreeTraits` + \sa `CGAL::Orthtree` + \sa `CGAL::Orthtree_traits_2` + \sa `CGAL::Orthtree_traits_3` +*/ +template < + typename GeomTraits, + typename DimensionTag, + typename PointSet, + typename PointMap = Identity_property_map +> +struct Orthtree_traits_point_d { +public: + + /// \name Types + /// @{ + + using Self = Orthtree_traits_point_d; + + using Dimension = DimensionTag; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_d; + using Sphere_d = typename GeomTraits::Sphere_d; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_d; + using Array = std::array; + + using Node_data = boost::iterator_range; + using Node_data_element = typename std::iterator_traits::value_type; + +#ifdef DOXYGEN_RUNNING + typedef unspecified_type Bbox_d; ///< Bounding box type. +#else + + class Bbox_d { + Point_d m_min, m_max; + public: + + Bbox_d(const Point_d& pmin, const Point_d& pmax) + : m_min(pmin), m_max(pmax) {} + + const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_min; } + + const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_max; } + }; + +#endif + + /*! + Adjacency type. + + \note This type is used to identify adjacency directions with + easily understandable keywords (left, right, up, etc.) and is thus + mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In + higher dimensions, such keywords do not exist and this type is + simply an integer. Conversions from this integer to bitsets still + works but do not provide any easier API for adjacency selection. + */ + using Adjacency = int; + +#ifdef DOXYGEN_RUNNING + /*! + Functor with an operator to construct a `Point_d` from an `Array` object. + */ + typedef unspecified_type Construct_point_d_from_array; +#else + + struct Construct_point_d_from_array { + Point_d operator()(const Array& array) const { + return Point_d(array.begin(), array.end()); + } + }; + +#endif + +#ifdef DOXYGEN_RUNNING + /*! + Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). + */ + typedef unspecified_type Construct_bbox_d; +#else + + struct Construct_bbox_d { + Bbox_d operator()(const Array& min, const Array& max) const { + return Bbox_d(Point_d(min.begin(), min.end()), Point_d(max.begin(), max.end())); + } + }; + +#endif + + /// @} + + Orthtree_traits_point_d( + PointSet& point_set, + PointMap point_map = PointMap() + ) : m_point_set(point_set), m_point_map(point_map) {} + + /// \name Operations + /// @{ + + /*! + Function used to construct an object of type `Construct_point_d_from_array`. + */ + Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } + + /*! + Function used to construct an object of type `Construct_bbox_d`. + */ + Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } + + std::pair root_node_bbox() const { + + Array bbox_min; + Array bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const auto& point = get(m_point_map, *(m_point_set.begin())); + std::size_t i = 0; + for (const FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } + } + // Expand bbox to contain all points + for (const auto& p: m_point_set) { + const auto& point = get(m_point_map, p); + std::size_t i = 0; + for (const FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } + } + + return {bbox_min, bbox_max}; + } + + Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + + template + void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(n, tree, center, tree.points(n)); + } + + Point_d get_element(const Node_data_element& index) const { + return get(m_point_map, index); + } + + /// @} + +private: + + PointSet& m_point_set; + PointMap m_point_map; + +}; + +} + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_D_H diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 0434df2d13ab..3c8649d95c93 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -15,7 +15,7 @@ #include #include -#include +#include namespace CGAL { @@ -41,7 +41,7 @@ template , PointRange, PointMap>; +using Quadtree = Orthtree>; #endif } // namespace CGAL From dc18e1b1db7381d5a6ffc5816295ed99447e0117 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 27 Jun 2023 14:58:13 +0200 Subject: [PATCH 073/520] Replace `tree.points(node)` with `tree.data(node)` --- Orthtree/include/CGAL/Orthtree.h | 9 ++++----- Orthtree/include/CGAL/Orthtree/IO.h | 2 +- Orthtree/include/CGAL/Orthtree/Split_predicates.h | 4 ++-- Orthtree/include/CGAL/Orthtree_traits_point_2.h | 4 ++-- Orthtree/include/CGAL/Orthtree_traits_point_3.h | 4 ++-- Orthtree/include/CGAL/Orthtree_traits_point_d.h | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 7abd20858fff..89b5eabd9f95 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -248,7 +248,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = construct_point_d_from_array(bbox_min); m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - points(root()) = m_traits.root_node_contents(); + data(root()) = m_traits.root_node_contents(); } /// @} @@ -699,11 +699,11 @@ class Orthtree { return m_node_depths[n]; } - Node_data& points(Node_index n) { + Node_data& data(Node_index n) { return m_node_points[n]; } - const Node_data& points(Node_index n) const { + const Node_data& data(Node_index n) const { return m_node_points[n]; } @@ -841,7 +841,6 @@ class Orthtree { // Add the node's points to its children m_traits.distribute_node_contents(n, *this, center); - //reassign_points(n, points(n).begin(), points(n).end(), center); } /*! @@ -1038,7 +1037,7 @@ class Orthtree { // Loop through each of the points contained by the node // Note: there might be none, and that should be fine! - for (auto p: points(node)) { + for (auto p: data(node)) { // Pair that point with its distance from the search point Node_element_with_distance current_point_with_distance = diff --git a/Orthtree/include/CGAL/Orthtree/IO.h b/Orthtree/include/CGAL/Orthtree/IO.h index 58155fefb064..1bf6bdd71438 100644 --- a/Orthtree/include/CGAL/Orthtree/IO.h +++ b/Orthtree/include/CGAL/Orthtree/IO.h @@ -47,7 +47,7 @@ std::ostream& print_orthtree_node(std::ostream& os, const Node_index& node, cons << ") "; os << "(" - << tree.points(node).size() + << tree.data(node).size() << ") "; // If a node is a leaf, mark it diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index 766a76e35707..b1a38ee1c223 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -51,7 +51,7 @@ class Maximum_number_of_inliers { */ template bool operator()(Node_index i, const Tree &tree) const { - return (tree.points(i).size() > m_bucket_size); + return (tree.data(i).size() > m_bucket_size); } }; @@ -132,7 +132,7 @@ class Maximum_depth_and_maximum_number_of_inliers { */ template bool operator()(Node_index i, const Tree &tree) const { - std::size_t num_points = tree.points(i).size(); + std::size_t num_points = tree.data(i).size(); std::size_t depth = tree.depth(i); return (num_points > m_bucket_size && depth < m_max_depth); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index 96940dc02762..ff4030a85106 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -185,7 +185,7 @@ struct Orthtree_traits_point_2 { template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); - reassign_points(n, tree, center, tree.points(n)); + reassign_points(n, tree, center, tree.data(n)); } Point_d get_element(const Node_data_element& index) const { @@ -207,7 +207,7 @@ struct Orthtree_traits_point_2 { // Root case: reached the last dimension if (dimension == Dimension::value) { - tree.points(tree.child(n, coord.to_ulong())) = points; + tree.data(tree.child(n, coord.to_ulong())) = points; return; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index 2764c41795f0..0698b93728b4 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -202,7 +202,7 @@ struct Orthtree_traits_point_3 { template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); - reassign_points(n, tree, center, tree.points(n)); + reassign_points(n, tree, center, tree.data(n)); } Point_d get_element(const Node_data_element& index) const { @@ -224,7 +224,7 @@ struct Orthtree_traits_point_3 { // Root case: reached the last dimension if (dimension == Dimension::value) { - tree.points(tree.child(n, coord.to_ulong())) = points; + tree.data(tree.child(n, coord.to_ulong())) = points; return; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 23ebb8b7517a..243ff8fb29e0 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -176,7 +176,7 @@ struct Orthtree_traits_point_d { template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); - reassign_points(n, tree, center, tree.points(n)); + reassign_points(n, tree, center, tree.data(n)); } Point_d get_element(const Node_data_element& index) const { From 6e1aa892feb123853ced55e31e0902fcdbedfeb9 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 27 Jun 2023 14:58:48 +0200 Subject: [PATCH 074/520] Add documentation for new features of the OrthtreeTraits concept --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index eac6e2e914de..b32701ce072e 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -29,6 +29,34 @@ class OrthtreeTraits typedef unspecified_type Cartesian_const_iterator_d; typedef std::array Array; ///< Array used for easy point constructions. + + /*! + * \brief List-like or iterable type contained by each node + * + * Should provide begin() and end() iterators which span all the items contained by a node. + * Must also be default-constructible, because node data is allocated ahead of time. + * Many split predicates also expect a `Node_data::size()` method. + * For example, this could be a `boost::range` of point indices, or an `std::vector` containing primitives. + * + * todo: For an empty tree, this should contain something like `std::array`. + * This way, nearest_neighbors still compiles, and simply returns nothing because all nodes are empty. + * Eventually, nearest_neighbors will be removed and/or moved, and then this won't have to behave like a list. + * Once that's done, `boost::none_t` will work for an empty tree. + */ + typedef unspecified_type Node_data; + + /*! + * \brief An element of the `Node_data` list-like type. + * + * Must be constructible from the type produced by dereferencing a `Node_data` iterator. + * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. + * + * todo: This is only used as part of the return type for `nearest_neighbors()`. + * Because `nearest_neighbors()` may be ill defined for empty node types, + * this can be omitted in the final version of Orthtree_traits. + */ + typedef unspecified_type Node_data_element; + typedef unspecified_type Adjacency; ///< Specify the adjacency directions /*! @@ -56,5 +84,46 @@ class OrthtreeTraits */ Construct_bbox_d construct_bbox_d_object() const; + /*! + * \brief Produces a bounding box which encloses the contents of the tree + * + * The bounding box must enclose all elements contained by the tree. + * It may be tight-fitting, the orthtree constructor produces a bounding cube surrounding this region. + * For traits which assign no data to each node, this can be defined to return a fixed region. + * + * @return std::pair, where min and max represent cartesian corners which define a bounding box + */ + std::pair root_node_bbox() const; + + /*! + * \brief Initializes the contained elements for the root node. + * + * Typically produces a `Node_data` which contains all the elements in the tree. + * + * @return The `Node_data` instance to be contained by the root node + */ + Node_data root_node_contents() const; + + /*! + * \brief Distributes the `Node_data` contents of a node to its immediate children. + * + * Invoked after a node is split. + * Adds the contents of the node n to each of its children. + * May rearrange or modify n's `Node_data`, but generally expected not to reset n. + * After distributing n's contents, n should still have an list of elements it encloses. + * Each of n's children should have an accurate list of the subset of elements within n they enclose. + * + * For an empty tree, this can be a null-op. + * + * @tparam Node_index The index type used by an orthtree implementation + * @tparam Tree An Orthree implementation + * + * @param n The index of the node who's contents must be distributed. + * @param tree The Orthtree which n belongs to + * @param center The coordinate center of the node n, which its contents should be split around. + */ + template + void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center); + /// @} }; From 4e1dabe0c01a5a8310b3003f268767c96d9c3b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 3 Jul 2023 17:16:10 +0200 Subject: [PATCH 075/520] version version of traits for triangle meshes example currently segfault, to be debugged... --- Orthtree/examples/Orthtree/CMakeLists.txt | 1 + .../examples/Orthtree/octree_surface_mesh.cpp | 69 ++++++++ .../include/CGAL/Orthtree_traits_face_graph.h | 155 ++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 Orthtree/examples/Orthtree/octree_surface_mesh.cpp create mode 100644 Orthtree/include/CGAL/Orthtree_traits_face_graph.h diff --git a/Orthtree/examples/Orthtree/CMakeLists.txt b/Orthtree/examples/Orthtree/CMakeLists.txt index 432d99b21c48..9e5dc642c418 100644 --- a/Orthtree/examples/Orthtree/CMakeLists.txt +++ b/Orthtree/examples/Orthtree/CMakeLists.txt @@ -15,6 +15,7 @@ create_single_source_cgal_program("octree_traversal_manual.cpp") create_single_source_cgal_program("octree_traversal_preorder.cpp") create_single_source_cgal_program("octree_grade.cpp") create_single_source_cgal_program("quadtree_build_from_point_vector.cpp") +create_single_source_cgal_program("octree_surface_mesh.cpp") find_package(Eigen3 3.1.91 QUIET) #(requires 3.1.91 or greater) include(CGAL_Eigen_support) diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp new file mode 100644 index 000000000000..4539f384b09f --- /dev/null +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -0,0 +1,69 @@ +#include +#include + +#include +#include + +#include + +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Mesh = CGAL::Surface_mesh; + +using OTraits = CGAL::Orthtree_traits_face_graph>; +using Octree = CGAL::Orthtree; + +void dump_as_polylines(const Octree& ot) +{ + // SL: I cheated for this part and looked at the implementation + std::ofstream out("octree.polylines.txt"); + for (Octree::Node_index node : ot.traverse_indices(CGAL::Orthtrees::Leaves_traversal(ot))) + { + if (!ot.is_leaf(node)) + continue; + CGAL::Bbox_3 bb = ot.bbox(node); + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin(); + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin(); + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax(); + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin(); + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax(); + out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax(); + out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin(); + out << "2 " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax(); + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax(); + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin(); + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax(); + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax(); + } +} + +int main(int argc, char** argv) +{ + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off"); + + Mesh mesh; + if(!CGAL::IO::read_polygon_mesh(filename, mesh)) + { + std::cerr << "Error: cannot read file" << std::endl; + return EXIT_FAILURE; + } + + OTraits otraits(mesh, mesh.points()); + + Octree tree(otraits); + OTraits::Split_predicate_node_min_extent sp(0.01); + tree.refine(sp); + + dump_as_polylines(tree); +} diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h new file mode 100644 index 000000000000..c062bb15d783 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -0,0 +1,155 @@ +// Copyright (c) 2023 INRIA (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) : Sebastien LORIOT + + +#ifndef CGAL_ORTHREE_TRAITS_FACE_GRAPH_H +#define CGAL_ORTHREE_TRAITS_FACE_GRAPH_H + +#include +#include + +namespace CGAL +{ + +template +struct Orthtree_traits_face_graph +{ + Orthtree_traits_face_graph(const PolygonMesh& pm, VPM vpm) + : m_pm(pm) + , m_vpm(vpm) + {} + + + using Point_d = typename boost::property_traits::value_type; + using Geom_traits = typename Kernel_traits::type; + using Dimension = Dimension_tag<3>; + using Bbox_d = Bbox_3; + using FT = typename Geom_traits::FT; + using Sphere_d = typename Geom_traits::Sphere_3; // SL: why? + using Array = std::array; // SL: why? + using Cartesian_const_iterator_d = typename Geom_traits::Cartesian_const_iterator_3; + // SL: these could be considered as built-in data and if the typedefs are not present, the tree have none + using Node_data_element = typename boost::graph_traits::face_descriptor; + using Node_data = std::vector; + + struct Construct_bbox_d { + Bbox_d operator()(const Array& min, + const Array& max) const { + return Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); + } + }; + + // SL: why? + struct Construct_point_d_from_array { + Point_d operator()(const Array& array) const { + return Point_d(array[0], array[1], array[2]); + } + }; + + Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } + Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } + + // SL: why in each traits? because it's dimension dependant? + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP, + BACK, + FRONT + }; + + std::pair root_node_bbox() const + { + Array min={0.0,0}, max={0.0,0}; + if (faces(m_pm).begin()!=faces(m_pm).end()) + { + const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); + min={p.x(), p.y(), p.z()}; + max=min; + for (auto v : vertices(m_pm)) + { + const Point_d& p_v = get(m_vpm, v); + for (int i=0; i<3; ++i) + { + if (p_v[i]max[i]) max[i]=p_v[i]; + } + } + } + + return {min, max}; + } + + // SL: not clear to me what it should do from the doc + Node_data root_node_contents() + { + return {faces(m_pm).begin(), faces(m_pm).end()}; + } + + template + void distribute_node_contents(NodeIndex n, Tree &tree, const Point_d ¢er) + { + Node_data& ndata = tree.data(n); + for (int i=0; i<8; ++i) + { + NodeIndex child = tree.child(n, i); + Node_data& child_data = tree.data(child); + Bbox_d bbox = tree.bbox(child); + for (Node_data_element f : ndata) + { + typename boost::graph_traits::halfedge_descriptor + h = halfedge(f, m_pm); + typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), + get(m_vpm, target(h, m_pm)), + get(m_vpm, target(next(h, m_pm), m_pm))); + if(do_intersect(t, bbox)) + child_data.push_back(f); + } + } + } + + //SL: I find convenient to put it here + class Split_predicate_node_min_extent { + + FT m_min_extent; + + public: + + Split_predicate_node_min_extent(FT me) + : m_min_extent(me) + {} + + /*! + \brief returns `true` if `ni` should be split, `false` otherwise. + */ + template + bool operator()(Node_index ni, const Tree &tree) const + { + if (tree.data(ni).empty()) return false; + + Bbox_d bb = tree.bbox(ni); + //TODO: we should get better version to get guarantees + for (int i=0; i<3; ++i) + if ( (bb.max(i) - bb.min(i)) < 2*m_min_extent ) + return false; + return true; + } + }; + + const PolygonMesh& m_pm; + VPM m_vpm; +}; + +} // end of CGAL namespace + + +#endif // CGAL_ORTHREE_TRAITS_FACE_GRAPH_H From 7c60fad080f466cbe45fc0f37774faa220a1d4e9 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 4 Jul 2023 20:01:28 -0600 Subject: [PATCH 076/520] < +std::ostream& operator<<(std::ostream& os, + const Multipolygon_with_holes_2& mp) { + typename Multipolygon_with_holes_2::Polygon_const_iterator i; + + switch(IO::get_mode(os)) { + case IO::ASCII : + os << mp.number_of_polygons() << ' '; + for (i = mp.polygons_begin(); i != mp.polygons_end(); ++i) { + os << *i << ' '; + } + return os; + + case IO::BINARY : + os << mp.number_of_polygons(); + for (i = mp.polygons_begin(); i != mp.polygons_end(); ++i) { + os << *i ; + } + return os; + + default: + os << "Multipolygon_with_holes_2(" << std::endl; + for (i = mp.polygons_begin(); i != mp.polygons_end(); ++i) { + os << " " << *i << std::endl; + } + + os << ")" << std::endl; + return os; + } +} + } //namespace CGAL From b46e3e8f288ba84fb7bd9b96d22ba8cfcad513e4 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 4 Jul 2023 20:02:29 -0600 Subject: [PATCH 077/520] reconstruction of multipolygon using new face base with repair info --- .../Polygon_repair_2/repair_polygon_2.cpp | 28 +- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 256 +++++++++++++++--- ...iangulation_face_base_with_repair_info_2.h | 51 ++++ ...riangulation_with_odd_even_constraints_2.h | 10 +- 4 files changed, 294 insertions(+), 51 deletions(-) create mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index e4828301db73..76cd3bf677c5 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -18,10 +18,14 @@ int main(int argc, char* argv[]) { // Square // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; // Polygon p(ps, ps+4); + // Multipolygon mp; + // mp.add_polygon(p); // Bowtie - // Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; - // Polygon p(ps, ps+4); + Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; + Polygon p(ps, ps+4); + Multipolygon mp; + mp.add_polygon(p); // Overlapping edge // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; @@ -42,20 +46,20 @@ int main(int argc, char* argv[]) { // mp.add_polygon(p2); // Edge partly overlapping (middle) - Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - Polygon p1(ps1, ps1+4); - Point ps2[] = {Point(1,0.25), Point(2,0.25), Point(2,0.75), Point(1,0.75)}; - Polygon p2(ps2, ps2+4); - Multipolygon mp; - mp.add_polygon(p1); - mp.add_polygon(p2); + // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon p1(ps1, ps1+4); + // Point ps2[] = {Point(1,0.25), Point(2,0.25), Point(2,0.75), Point(1,0.75)}; + // Polygon p2(ps2, ps2+4); + // Multipolygon mp; + // mp.add_polygon(p1); + // mp.add_polygon(p2); Polygon_repair pr; pr.add_to_triangulation(mp); + pr.label_triangulation(); + pr.reconstruct_multipolygon(); + std::cout << pr.multipolygon(); - for (auto const& ce: pr.triangulation().constrained_edges()) { - std::cout << ce.first->vertex(ce.first->cw(ce.second))->point() << " to " << ce.first->vertex(ce.first->ccw(ce.second))->point() << std::endl; - } // CGAL::Polygon_repair_2::repair(mp); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index a50fee4192e1..d2d9b8f8a19b 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -15,9 +15,9 @@ #include #include #include -#include #include +#include #include namespace CGAL { @@ -30,7 +30,7 @@ class Polygon_repair_2; /// \ingroup PkgPolygonRepair2Functions /// Repair a polygon without holes template -Multipolygon_with_holes_2 repair(const CGAL::Polygon_2& p) { +Multipolygon_with_holes_2 repair(const Polygon_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); @@ -41,7 +41,7 @@ Multipolygon_with_holes_2 repair(const CGAL::Polygon_2 /// \ingroup PkgPolygonRepair2Functions /// Repair a polygon with holes template -Multipolygon_with_holes_2 repair(const CGAL::Polygon_with_holes_2& p) { +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); @@ -69,18 +69,9 @@ template > class Polygon_repair_2 { public: - - struct Repair_face_info { - bool processed; - bool interior; - Repair_face_info() { - processed = false; - interior = false; - } - }; typedef CGAL::Triangulation_vertex_base_2 Vertex_base; typedef CGAL::Constrained_triangulation_face_base_2 Face_base; - typedef CGAL::Triangulation_face_base_with_info_2 Face_base_with_repair_info; + typedef CGAL::Triangulation_face_base_with_repair_info_2 Face_base_with_repair_info; typedef CGAL::Triangulation_data_structure_2 Triangulation_data_structure; typedef CGAL::Exact_predicates_tag Tag; // assumed for now typedef CGAL::Constrained_Delaunay_triangulation_2 Constrained_Delaunay_triangulation; @@ -91,39 +82,230 @@ class Polygon_repair_2 { } - /// \name Modifiers + /// \name Modifiers /// @{ - // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_2& p) { - for (auto const& e: p.edges()) { - t.odd_even_insert_constraint(e.source(), e.target()); + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Polygon_2& polygon) { + for (auto const& edge: polygon.edges()) { + t.odd_even_insert_constraint(edge.source(), edge.target()); } } - // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_with_holes_2& p) { - add_to_triangulation(p.outer_boundary()); - for (auto const& h: p.holes()) { - add_to_triangulation(h); + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Polygon_with_holes_2& polygon) { + add_to_triangulation(polygon.outer_boundary()); + for (auto const& hole: polygon.holes()) { + add_to_triangulation(hole); } } - // Add edges of the polygon to the triangulation - void add_to_triangulation(const Multipolygon_with_holes_2& mp) { - for (auto const &p: mp.polygons()) { - add_to_triangulation(p); + // Add edges of the polygon to the triangulation + void add_to_triangulation(const Multipolygon_with_holes_2& multipolygon) { + for (auto const& polygon: multipolygon.polygons()) { + add_to_triangulation(polygon); } } - // Label triangles in triangulation as inside or outside the polygon - void label_triangulation() { + // Label triangles in triangulation as inside or outside the polygon + void label_triangulation() { + for (auto const face: t.all_face_handles()) { + face->processed() = false; + face->interior() = false; + } std::list to_check; + t.infinite_face()->processed() = true; + to_check.push_back(t.infinite_face()); + while (!to_check.empty()) { + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (!to_check.front()->neighbor(neighbour)->processed()) { + to_check.front()->neighbor(neighbour)->processed() = true; + if (t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { + to_check.front()->neighbor(neighbour)->interior() = !to_check.front()->interior(); + to_check.push_back(to_check.front()->neighbor(neighbour)); + } else { + to_check.front()->neighbor(neighbour)->interior() = to_check.front()->interior(); + to_check.push_back(to_check.front()->neighbor(neighbour)); + } + } + } to_check.pop_front(); + } + } + void get_boundary(typename Triangulation::Face_handle face, int edge, std::list &out_vertices) { + // Check clockwise edge + if (face->neighbor(face->cw(edge))->interior() && !face->neighbor(face->cw(edge))->reconstructed()) { + face->neighbor(face->cw(edge))->reconstructed() = true; + std::list v1; + get_boundary(face->neighbor(face->cw(edge)), face->neighbor(face->cw(edge))->index(face), v1); + out_vertices.splice(out_vertices.end(), v1); + } + + // Add central vertex + out_vertices.push_back(face->vertex(edge)); + + // Check counterclockwise edge + if (face->neighbor(face->ccw(edge))->interior() && !face->neighbor(face->ccw(edge))->reconstructed()) { + face->neighbor(face->ccw(edge))->reconstructed() = true; + std::list v2; + get_boundary(face->neighbor(face->ccw(edge)), face->neighbor(face->ccw(edge))->index(face), v2); + out_vertices.splice(out_vertices.end(), v2); + } } // Reconstruct multipolygon based on the triangles labelled as inside the polygon - void reconstruct_polygon() { - + void reconstruct_multipolygon() { + mp.clear(); + for (auto const face: t.all_face_handles()) { + face->reconstructed() = false; + } + + for (auto const interior_face: t.finite_face_handles()) { + if (!interior_face->interior() || interior_face->reconstructed()) continue; + + // Get boundary + std::list vertices; + if (interior_face->neighbor(2)->interior() && !interior_face->neighbor(2)->reconstructed()) { + interior_face->neighbor(2)->reconstructed() = true; + std::list l2; + get_boundary(interior_face->neighbor(2), interior_face->neighbor(2)->index(interior_face), l2); + vertices.splice(vertices.end(), l2); + } vertices.push_back(interior_face->vertex(0)); + if (interior_face->neighbor(1)->interior() && !interior_face->neighbor(1)->reconstructed()) { + interior_face->neighbor(1)->reconstructed() = true; + std::list l1; + get_boundary(interior_face->neighbor(1), interior_face->neighbor(1)->index(interior_face), l1); + vertices.splice(vertices.end(), l1); + } vertices.push_back(interior_face->vertex(2)); + if (interior_face->neighbor(0)->interior() && !interior_face->neighbor(0)->reconstructed()) { + interior_face->neighbor(0)->reconstructed() = true; + std::list l0; + get_boundary(interior_face->neighbor(0), interior_face->neighbor(0)->index(interior_face), l0); + vertices.splice(vertices.end(), l0); + } vertices.push_back(interior_face->vertex(1)); + + // Find cutting vertices + std::set visited_vertices, repeated_vertices; + for (auto const& current_vertex: vertices) { + if (!visited_vertices.insert(current_vertex).second) repeated_vertices.insert(current_vertex); + } visited_vertices.clear(); + + // Cut and join rings in the correct order + std::list> rings; + std::stack> chains_stack; + std::set vertices_where_chains_begin; + rings.push_back(std::list()); + for (auto const& current_vertex: vertices) { + + // New chain + if (repeated_vertices.count(current_vertex) > 0) { + // Closed by itself + if (rings.back().front() == current_vertex) { + // Degenerate (insufficient vertices to be valid) + if (rings.back().size() < 3) { + rings.back().clear(); + } else { + typename std::list::iterator second_element = rings.back().begin(); + ++second_element; + // Degenerate (zero area) + if (rings.back().back() == *second_element) { + rings.back().clear(); + } + // Valid + else { + rings.push_back(std::list()); + } + } + } + // Open by itself + else { + // Closed with others in stack + if (vertices_where_chains_begin.count(current_vertex)) { + + while (rings.back().front() != current_vertex) { + rings.back().splice(rings.back().begin(), chains_stack.top()); + chains_stack.pop(); + } vertices_where_chains_begin.erase(current_vertex); + // Degenerate (insufficient vertices to be valid) + if (rings.back().size() < 3) { + rings.back().clear(); + } else { + typename std::list::iterator second_element = rings.back().begin(); + ++second_element; + // Degenerate (zero area) + if (rings.back().back() == *second_element) { + rings.back().clear(); + } + // Valid + else { + rings.push_back(std::list()); + } + } + } + // Open + else { + // Not first chain + if (repeated_vertices.count(rings.back().front()) > 0) { + vertices_where_chains_begin.insert(rings.back().front()); + } + chains_stack.push(std::list()); + chains_stack.top().splice(chains_stack.top().begin(), rings.back()); + } + } + } rings.back().push_back(current_vertex); + } + // Final ring + while (chains_stack.size() > 0) { + rings.back().splice(rings.back().begin(), chains_stack.top()); + chains_stack.pop(); + } + // Degenerate (insufficient vertices to be valid) + if (rings.back().size() < 3) { + rings.back().clear(); + } else { + typename std::list::iterator second_element = rings.back().begin(); + ++second_element; + // Degenerate (zero area) + if (rings.back().back() == *second_element) { + rings.back().clear(); + } + } + + // Remove last ring if too small (or empty) + if (rings.back().size() < 3) { + rings.pop_back(); + } + + // Start rings at the lexicographically smallest vertex + for (auto& current_ring: rings) { + typename std::list::iterator smallest_vertex = current_ring.begin(); + for (typename std::list::iterator current_vertex = current_ring.begin(); current_vertex != current_ring.end(); ++current_vertex) { + if ((*current_vertex)->point() < (*smallest_vertex)->point()) smallest_vertex = current_vertex; + } if (current_ring.back() != *smallest_vertex) { + ++smallest_vertex; + current_ring.splice(current_ring.begin(), current_ring, smallest_vertex, current_ring.end()); + } + } + + // Make rings + if (rings.size() == 0) continue; + typename std::list> rings_for_polygon; + for (auto& current_ring: rings) { + rings_for_polygon.push_back(Polygon_2()); + for (typename std::list::reverse_iterator current_vertex = current_ring.rbegin(); current_vertex != current_ring.rend(); ++current_vertex) { + rings_for_polygon.back().push_back(typename Kernel::Point_2((*current_vertex)->point().x(), (*current_vertex)->point().y())); + } + } typename std::list>::iterator outer_ring; + for (typename std::list>::iterator current_ring = rings_for_polygon.begin(); current_ring != rings_for_polygon.end(); ++current_ring) { + if (current_ring->orientation() == COUNTERCLOCKWISE) { + outer_ring = current_ring; + break; + } + } Polygon_with_holes_2 new_polygon(*outer_ring); + rings_for_polygon.erase(outer_ring); + + for (auto const& current_ring: rings_for_polygon) new_polygon.add_hole(current_ring); + mp.add_polygon(new_polygon); + } } // Erases the triangulation. @@ -131,24 +313,24 @@ class Polygon_repair_2 { t.clear(); } - /// @} + /// @} - /// \name Access Functions + /// \name Access Functions /// @{ - Triangulation_with_odd_even_constraints_2& triangulation() { + Triangulation& triangulation() { return t; } Multipolygon_with_holes_2 multipolygon() { - + return mp; } /// @} - + protected: - Triangulation_with_odd_even_constraints_2 t; + Triangulation t; Multipolygon_with_holes_2 mp; }; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h new file mode 100644 index 000000000000..5f8ad643bdf8 --- /dev/null +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -0,0 +1,51 @@ +// Copyright (c) 2023 GeometryFactory. All rights reserved. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H +#define CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H + +namespace CGAL { + +template > +class Triangulation_face_base_with_repair_info_2 : public FaceBase { + bool _processed; + bool _interior; + bool _reconstructed; +public: + typedef typename FaceBase::Vertex_handle Vertex_handle; + typedef typename FaceBase::Face_handle Face_handle; + + template + struct Rebind_TDS { + typedef typename FaceBase::template Rebind_TDS::Other FaceBase2; + typedef Triangulation_face_base_with_repair_info_2 Other; + }; + + Triangulation_face_base_with_repair_info_2() : FaceBase() {} + + Triangulation_face_base_with_repair_info_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2) + : FaceBase(v0, v1, v2) {} + + Triangulation_face_base_with_repair_info_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2, + Face_handle n0, Face_handle n1, Face_handle n2 ) + : FaceBase(v0, v1, v2, n0, n1, n2) {} + + const bool& processed() const { return _processed; } + bool& processed() { return _processed; } + const bool& interior() const { return _interior; } + bool& interior() { return _interior; } + const bool& reconstructed() const { return _reconstructed; } + bool& reconstructed() { return _reconstructed; } +}; + +} //namespace CGAL + +#endif // CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H \ No newline at end of file diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 84f239dad34a..5a4a83ed0f77 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -52,8 +52,14 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { // iterator over interior faces. class Interior_faces_iterator : public Base_triangulation::All_faces_iterator { - Interior_faces_iterator operator++(); - Interior_faces_iterator operator--(); + + Interior_faces_iterator operator++() { + + } + + Interior_faces_iterator operator--() { + + } }; // Inserts point p in the triangulation and returns the corresponding vertex. From 02e10bf85afb7e13d928b0679ab16b70807f71c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 6 Jul 2023 09:24:44 +0200 Subject: [PATCH 078/520] add TODO --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index c062bb15d783..cb851b1fcb37 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -138,9 +138,10 @@ struct Orthtree_traits_face_graph Bbox_d bb = tree.bbox(ni); //TODO: we should get better version to get guarantees + // TODO: as long as the bbox is cubic you can use depth and initial size to conclude. for (int i=0; i<3; ++i) - if ( (bb.max(i) - bb.min(i)) < 2*m_min_extent ) - return false; + if ( (bb.max(i) - bb.min(i)) < 2*m_min_extent ) + return false; return true; } }; From 868dd5499338e73548b785e40a3109d8080f05f3 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 6 Jul 2023 17:38:58 -0600 Subject: [PATCH 079/520] sketch of new labelling method (untested) --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 251 +++++------------- ...iangulation_face_base_with_repair_info_2.h | 15 +- 2 files changed, 68 insertions(+), 198 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index d2d9b8f8a19b..526a08387012 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -78,9 +78,7 @@ class Polygon_repair_2 { typedef Triangulation_with_odd_even_constraints_2 Triangulation; /// \name Creation - Polygon_repair_2() { - - } + Polygon_repair_2() {} /// \name Modifiers /// @{ @@ -107,205 +105,80 @@ class Polygon_repair_2 { } } - // Label triangles in triangulation as inside or outside the polygon + // Label triangles in triangulation void label_triangulation() { + std::list to_check_exterior, to_check_interior, to_check; + std::list to_check_exterior_added_by; for (auto const face: t.all_face_handles()) { - face->processed() = false; - face->interior() = false; - } std::list to_check; - t.infinite_face()->processed() = true; - to_check.push_back(t.infinite_face()); - while (!to_check.empty()) { + face->label() = 0; + face->added_to_list() = false; + } + + // Mark exterior as added to list and get interior triangles adjacent to it + to_check_exterior.push_back(t.infinite_face()); + t.infinite_face()->added_to_list() = true; + while (!to_check_exterior.empty()) { for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (!to_check.front()->neighbor(neighbour)->processed()) { - to_check.front()->neighbor(neighbour)->processed() = true; - if (t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { - to_check.front()->neighbor(neighbour)->interior() = !to_check.front()->interior(); - to_check.push_back(to_check.front()->neighbor(neighbour)); + if (!to_check_exterior.front()->neighbor(neighbour)->added_to_list()) { + if (!t.is_constrained(typename Triangulation::Edge(to_check_exterior.front(), neighbour))) { + to_check_exterior.push_back(to_check_exterior.front()->neighbor(neighbour)); } else { - to_check.front()->neighbor(neighbour)->interior() = to_check.front()->interior(); - to_check.push_back(to_check.front()->neighbor(neighbour)); - } + to_check_interior.push_back(to_check_exterior.front()->neighbor(neighbour)); + } to_check_exterior.front()->neighbor(neighbour)->added_to_list() = true; } - } to_check.pop_front(); + } to_check_exterior.pop_front(); } - } - void get_boundary(typename Triangulation::Face_handle face, int edge, std::list &out_vertices) { - // Check clockwise edge - if (face->neighbor(face->cw(edge))->interior() && !face->neighbor(face->cw(edge))->reconstructed()) { - face->neighbor(face->cw(edge))->reconstructed() = true; - std::list v1; - get_boundary(face->neighbor(face->cw(edge)), face->neighbor(face->cw(edge))->index(face), v1); - out_vertices.splice(out_vertices.end(), v1); - } - - // Add central vertex - out_vertices.push_back(face->vertex(edge)); - - // Check counterclockwise edge - if (face->neighbor(face->ccw(edge))->interior() && !face->neighbor(face->ccw(edge))->reconstructed()) { - face->neighbor(face->ccw(edge))->reconstructed() = true; - std::list v2; - get_boundary(face->neighbor(face->ccw(edge)), face->neighbor(face->ccw(edge))->index(face), v2); - out_vertices.splice(out_vertices.end(), v2); + // Label region of front element of interior and exterior lists + int current_polygon = 1, current_hole = -1; + std::unordered_map hole_nesting; + while (!to_check_interior.empty() || !to_check_exterior.empty()) { + + // Interior + if (!to_check_interior.empty()) { + if (to_check_interior.front()->label() == 0) { + to_check.push_back(to_check_interior.front()); + to_check_interior.front()->added_to_list() = true; + while (!to_check.empty()) { + to_check.front()->label() = current_polygon; + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (!t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { + to_check.push_back(to_check.front()->neighbor(neighbour)); + } else if (!to_check.front()->neighbor(neighbour)->added_to_list()) { + to_check_exterior.push_back(to_check.front()->neighbor(neighbour)); + to_check_exterior_added_by.push_back(current_polygon); + } to_check.front()->neighbor(neighbour)->added_to_list() = true; + } to_check.pop_front(); + } ++current_polygon; + } to_check_interior.pop_front(); + } + + // Exterior + if (!to_check_exterior.empty()) { + if (to_check_exterior.front()->label() == 0) { + hole_nesting[current_hole] = to_check_exterior_added_by.front(); + to_check.push_back(to_check_exterior.front()); + to_check_exterior.front()->added_to_list() = true; + while (!to_check.empty()) { + to_check.front()->label() = current_hole; + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (!t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { + to_check.push_back(to_check.front()->neighbor(neighbour)); + } else if (!to_check.front()->neighbor(neighbour)->added_to_list()) { + to_check_interior.push_back(to_check.front()->neighbor(neighbour)); + } to_check.front()->neighbor(neighbour)->added_to_list() = true; + } to_check.pop_front(); + } --current_hole; + } to_check_exterior.pop_front(); + to_check_exterior_added_by.pop_front(); + } + } } // Reconstruct multipolygon based on the triangles labelled as inside the polygon void reconstruct_multipolygon() { mp.clear(); - for (auto const face: t.all_face_handles()) { - face->reconstructed() = false; - } - - for (auto const interior_face: t.finite_face_handles()) { - if (!interior_face->interior() || interior_face->reconstructed()) continue; - - // Get boundary - std::list vertices; - if (interior_face->neighbor(2)->interior() && !interior_face->neighbor(2)->reconstructed()) { - interior_face->neighbor(2)->reconstructed() = true; - std::list l2; - get_boundary(interior_face->neighbor(2), interior_face->neighbor(2)->index(interior_face), l2); - vertices.splice(vertices.end(), l2); - } vertices.push_back(interior_face->vertex(0)); - if (interior_face->neighbor(1)->interior() && !interior_face->neighbor(1)->reconstructed()) { - interior_face->neighbor(1)->reconstructed() = true; - std::list l1; - get_boundary(interior_face->neighbor(1), interior_face->neighbor(1)->index(interior_face), l1); - vertices.splice(vertices.end(), l1); - } vertices.push_back(interior_face->vertex(2)); - if (interior_face->neighbor(0)->interior() && !interior_face->neighbor(0)->reconstructed()) { - interior_face->neighbor(0)->reconstructed() = true; - std::list l0; - get_boundary(interior_face->neighbor(0), interior_face->neighbor(0)->index(interior_face), l0); - vertices.splice(vertices.end(), l0); - } vertices.push_back(interior_face->vertex(1)); - - // Find cutting vertices - std::set visited_vertices, repeated_vertices; - for (auto const& current_vertex: vertices) { - if (!visited_vertices.insert(current_vertex).second) repeated_vertices.insert(current_vertex); - } visited_vertices.clear(); - - // Cut and join rings in the correct order - std::list> rings; - std::stack> chains_stack; - std::set vertices_where_chains_begin; - rings.push_back(std::list()); - for (auto const& current_vertex: vertices) { - - // New chain - if (repeated_vertices.count(current_vertex) > 0) { - // Closed by itself - if (rings.back().front() == current_vertex) { - // Degenerate (insufficient vertices to be valid) - if (rings.back().size() < 3) { - rings.back().clear(); - } else { - typename std::list::iterator second_element = rings.back().begin(); - ++second_element; - // Degenerate (zero area) - if (rings.back().back() == *second_element) { - rings.back().clear(); - } - // Valid - else { - rings.push_back(std::list()); - } - } - } - // Open by itself - else { - // Closed with others in stack - if (vertices_where_chains_begin.count(current_vertex)) { - - while (rings.back().front() != current_vertex) { - rings.back().splice(rings.back().begin(), chains_stack.top()); - chains_stack.pop(); - } vertices_where_chains_begin.erase(current_vertex); - // Degenerate (insufficient vertices to be valid) - if (rings.back().size() < 3) { - rings.back().clear(); - } else { - typename std::list::iterator second_element = rings.back().begin(); - ++second_element; - // Degenerate (zero area) - if (rings.back().back() == *second_element) { - rings.back().clear(); - } - // Valid - else { - rings.push_back(std::list()); - } - } - } - // Open - else { - // Not first chain - if (repeated_vertices.count(rings.back().front()) > 0) { - vertices_where_chains_begin.insert(rings.back().front()); - } - chains_stack.push(std::list()); - chains_stack.top().splice(chains_stack.top().begin(), rings.back()); - } - } - } rings.back().push_back(current_vertex); - } - // Final ring - while (chains_stack.size() > 0) { - rings.back().splice(rings.back().begin(), chains_stack.top()); - chains_stack.pop(); - } - // Degenerate (insufficient vertices to be valid) - if (rings.back().size() < 3) { - rings.back().clear(); - } else { - typename std::list::iterator second_element = rings.back().begin(); - ++second_element; - // Degenerate (zero area) - if (rings.back().back() == *second_element) { - rings.back().clear(); - } - } - - // Remove last ring if too small (or empty) - if (rings.back().size() < 3) { - rings.pop_back(); - } - - // Start rings at the lexicographically smallest vertex - for (auto& current_ring: rings) { - typename std::list::iterator smallest_vertex = current_ring.begin(); - for (typename std::list::iterator current_vertex = current_ring.begin(); current_vertex != current_ring.end(); ++current_vertex) { - if ((*current_vertex)->point() < (*smallest_vertex)->point()) smallest_vertex = current_vertex; - } if (current_ring.back() != *smallest_vertex) { - ++smallest_vertex; - current_ring.splice(current_ring.begin(), current_ring, smallest_vertex, current_ring.end()); - } - } - - // Make rings - if (rings.size() == 0) continue; - typename std::list> rings_for_polygon; - for (auto& current_ring: rings) { - rings_for_polygon.push_back(Polygon_2()); - for (typename std::list::reverse_iterator current_vertex = current_ring.rbegin(); current_vertex != current_ring.rend(); ++current_vertex) { - rings_for_polygon.back().push_back(typename Kernel::Point_2((*current_vertex)->point().x(), (*current_vertex)->point().y())); - } - } typename std::list>::iterator outer_ring; - for (typename std::list>::iterator current_ring = rings_for_polygon.begin(); current_ring != rings_for_polygon.end(); ++current_ring) { - if (current_ring->orientation() == COUNTERCLOCKWISE) { - outer_ring = current_ring; - break; - } - } Polygon_with_holes_2 new_polygon(*outer_ring); - rings_for_polygon.erase(outer_ring); - - for (auto const& current_ring: rings_for_polygon) new_polygon.add_hole(current_ring); - mp.add_polygon(new_polygon); - } } // Erases the triangulation. diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h index 5f8ad643bdf8..8e1b55455c8f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -16,9 +16,8 @@ namespace CGAL { template > class Triangulation_face_base_with_repair_info_2 : public FaceBase { - bool _processed; - bool _interior; - bool _reconstructed; + int _label; + bool _added_to_list; public: typedef typename FaceBase::Vertex_handle Vertex_handle; typedef typename FaceBase::Face_handle Face_handle; @@ -38,12 +37,10 @@ class Triangulation_face_base_with_repair_info_2 : public FaceBase { Face_handle n0, Face_handle n1, Face_handle n2 ) : FaceBase(v0, v1, v2, n0, n1, n2) {} - const bool& processed() const { return _processed; } - bool& processed() { return _processed; } - const bool& interior() const { return _interior; } - bool& interior() { return _interior; } - const bool& reconstructed() const { return _reconstructed; } - bool& reconstructed() { return _reconstructed; } + const bool& added_to_list() const { return _added_to_list; } + bool& added_to_list() { return _added_to_list; } + const int& label() const { return _label; } + int& label() { return _label; } }; } //namespace CGAL From 03cda70191de38d7d12fd3cba930280cff51c656 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 9 Jul 2023 17:49:12 +0200 Subject: [PATCH 080/520] Replace boost::optional with std::optional --- Orthtree/include/CGAL/Orthtree.h | 32 +++++++++++-------- .../CGAL/Orthtree/Traversal_iterator.h | 10 +++--- Orthtree/include/CGAL/Orthtree/Traversals.h | 23 ++++++------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 89b5eabd9f95..d685f2e608f8 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -113,7 +113,7 @@ class Orthtree { /*! * \brief Optional index of a node in the tree. */ - typedef boost::optional Maybe_node_index; + typedef std::optional Maybe_node_index; // todo: maybe this could be private? typedef Properties::Property_container Node_property_container; @@ -405,23 +405,23 @@ class Orthtree { // Skip if this neighbor is a direct sibling (it's guaranteed to be the same depth) // TODO: This check might be redundant, if it doesn't affect performance maybe I could remove it - if (parent(neighbor.get()) == parent(node)) + if (parent(*neighbor) == parent(node)) continue; // If it's already been split, skip it - if (!is_leaf(neighbor.get())) + if (!is_leaf(neighbor)) continue; // Check if the neighbor breaks our grading rule // TODO: could the rule be parametrized? - if ((depth(node) - depth(neighbor.get())) > 1) { + if ((depth(node) - depth(*neighbor)) > 1) { // Split the neighbor - split(neighbor.get()); + split(*neighbor); // Add newly created children to the queue for (int i = 0; i < Degree::value; ++i) { - leaf_nodes.push(child(neighbor.get(), i)); + leaf_nodes.push(child(*neighbor, i)); } } } @@ -478,6 +478,7 @@ class Orthtree { Node_index_range traverse_indices(Traversal traversal) const { Node_index first = traversal.first_index(); + std::cout << first << std::endl; auto next = [=](const Self& tree, Node_index index) -> Maybe_node_index { return traversal.next_index(index); @@ -696,6 +697,9 @@ class Orthtree { } std::size_t depth(Node_index n) const { +// std::cerr << n +// << " " << m_node_depths.size() +// << std::endl; return m_node_depths[n]; } @@ -724,12 +728,12 @@ class Orthtree { */ Node_index parent(Node_index node) const { CGAL_precondition (!is_root(node)); - return m_node_parents[node].get(); + return *m_node_parents[node]; } Node_index child(Node_index node, std::size_t i) const { CGAL_precondition (!is_leaf(node)); - return m_node_children[node].get() + i; + return *m_node_children[node] + i; } Node_index descendant(Node_index node, std::size_t i) { return child(node, i); } @@ -768,9 +772,9 @@ class Orthtree { auto up = Maybe_node_index{parent(n)}; while (up) { - if (next_sibling(up.get())) return {next_sibling(up.get())}; + if (next_sibling(*up)) return {next_sibling(*up)}; - up = is_root(up.get()) ? Maybe_node_index{} : Maybe_node_index{parent(up.get())}; + up = is_root(*up) ? Maybe_node_index{} : Maybe_node_index{parent(*up)}; } return {}; @@ -824,10 +828,10 @@ class Orthtree { m_node_children[n] = m_node_properties.emplace_group(Degree::value); for (std::size_t i = 0; i < Degree::value; i++) { - Node_index c = m_node_children[n].get() + i; + Node_index c = *m_node_children[n] + i; // Make sure the node isn't one of its own children - CGAL_assertion(n != m_node_children[n].get() + i); + CGAL_assertion(n != *m_node_children[n] + i); Local_coordinates local_coordinates{i}; for (int i = 0; i < Dimension::value; i++) @@ -985,11 +989,11 @@ class Orthtree { if (!adjacent_node_of_parent) return {}; // If the parent's adjacent node has no children, then it's this node's adjacent node - if (is_leaf(adjacent_node_of_parent.get())) + if (is_leaf(*adjacent_node_of_parent)) return adjacent_node_of_parent; // Return the nearest node of the parent by subtracting the offset instead of adding - return {child(adjacent_node_of_parent.get(), local_coordinates(n).to_ulong() - offset)}; + return {child(*adjacent_node_of_parent, local_coordinates(n).to_ulong() - offset)}; } /*! diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index e1426b26cf72..def3d5df3a47 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -14,6 +14,8 @@ #include +#include + #include #include #include @@ -118,7 +120,7 @@ class Index_traversal_iterator : public boost::iterator_facade< * * \todo */ - typedef std::function(const Tree&, std::size_t)> Traversal_function; + typedef std::function(const Tree&, std::size_t)> Traversal_function; typedef typename Tree::Node_index Node_index; @@ -160,18 +162,18 @@ class Index_traversal_iterator : public boost::iterator_facade< void increment() { // invoking increment on the sentinel is undefined behavior - m_index = m_next(*m_tree, m_index.get()); + m_index = m_next(*m_tree, *m_index); } Node_index dereference() const { - return m_index.get(); + return *m_index; } private: Traversal_function m_next; - boost::optional m_index = {}; + std::optional m_index; const Tree* m_tree = nullptr; }; diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 708faa435e07..595fa015d133 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -61,10 +61,10 @@ struct Preorder_traversal { const Node* next(const Node* n) const { if (n == nullptr || !next_index(m_orthtree.index(*n))) return nullptr; - return &m_orthtree[next_index(m_orthtree.index(*n)).get()]; + return &m_orthtree[*next_index(m_orthtree.index(*n))]; } - boost::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(typename Tree::Node_index n) const { if (m_orthtree.is_leaf(n)) { @@ -76,10 +76,7 @@ struct Preorder_traversal { return next; } else { - - // todo: this shouldn't be necessary, I'd prefer to directly get m_orthtree[n].m_children_index return m_orthtree.child(n, 0); - } } @@ -110,7 +107,7 @@ struct Postorder_traversal { } typename Tree::Node_index first_index() const { - return m_orthtree.index(first()).get(); + return *m_orthtree.index(first()); } const Node* next(const Node* n) const { @@ -123,7 +120,7 @@ struct Postorder_traversal { return next; } - boost::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(typename Tree::Node_index n) const { return m_orthtree.index(next(&m_orthtree[n])); } }; @@ -166,13 +163,13 @@ struct Leaves_traversal { return next; } - boost::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(typename Tree::Node_index n) const { if (m_orthtree.next_sibling(n)) - return m_orthtree.deepest_first_child(m_orthtree.next_sibling(n).get()); + return m_orthtree.deepest_first_child(*m_orthtree.next_sibling(n)); if (m_orthtree.next_sibling_up(n)) - return m_orthtree.deepest_first_child(m_orthtree.next_sibling_up(n).get()); + return m_orthtree.deepest_first_child(*m_orthtree.next_sibling_up(n)); return {}; } @@ -210,7 +207,7 @@ struct Level_traversal { typename Tree::Node_index first_index() const { // assumes the tree has at least one child at m_depth - return m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth).get(); + return *m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); } template @@ -231,7 +228,7 @@ struct Level_traversal { return next; } - boost::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(typename Tree::Node_index n) const { auto next = m_orthtree.next_sibling(n); @@ -243,7 +240,7 @@ struct Level_traversal { if (!m_orthtree.next_sibling_up(up)) return {}; - up = m_orthtree.next_sibling_up(up).get(); + up = *m_orthtree.next_sibling_up(up); next = m_orthtree.first_child_at_depth(up, m_depth); } while (!next); From dccda38424bf4f92c487e8d5fc339a335277fd5a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 9 Jul 2023 17:51:50 +0200 Subject: [PATCH 081/520] Reference type of traversal iterator is a Node_index (and not a reference to one) This resolves the issue with segfaults during traversal. In some circumstances, the index would go out of scope before being used for access. --- Orthtree/include/CGAL/Orthtree/Traversal_iterator.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index def3d5df3a47..93027dfe5b47 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -108,7 +108,8 @@ template class Index_traversal_iterator : public boost::iterator_facade< Index_traversal_iterator, const typename Tree::Node_index, - boost::forward_traversal_tag + boost::forward_traversal_tag, + const typename Tree::Node_index > { public: From 533f08855ba1b96673ea239a3a402c8a63bf170b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 10 Jul 2023 09:22:46 +0200 Subject: [PATCH 082/520] fix dump --- .../examples/Orthtree/octree_surface_mesh.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index 4539f384b09f..101437e6601d 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -22,29 +22,31 @@ void dump_as_polylines(const Octree& ot) continue; CGAL::Bbox_3 bb = ot.bbox(node); out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() - << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin(); + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() << "\n"; out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() - << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin(); - out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() - << " " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax(); - out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() - << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin(); + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() << "\n"; out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() - << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax(); + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() << "\n"; out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() - << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax(); + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() << "\n"; +// + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() << "\n"; out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin() - << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin(); + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax() << "\n"; out << "2 " << bb.xmax() << " " << bb.ymax() << " " << bb.zmin() - << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax(); + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax() << "\n"; +// out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() - << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax(); - out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() - << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmin(); - out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() - << " " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax(); + << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmax() + << " " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax() << "\n"; out << "2 " << bb.xmax() << " " << bb.ymin() << " " << bb.zmax() - << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax(); + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax() << "\n"; + out << "2 " << bb.xmin() << " " << bb.ymax() << " " << bb.zmax() + << " " << bb.xmax() << " " << bb.ymax() << " " << bb.zmax() << "\n"; } } From 173b91bba340134f0f4083bd6aa6a8e1310f4b4f Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 11 Jul 2023 19:34:22 -0600 Subject: [PATCH 083/520] polygon reconstruction + fixed labelling method --- .../Polygon_repair_2/repair_polygon_2.cpp | 14 +- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 134 ++++++++++++++---- ...iangulation_face_base_with_repair_info_2.h | 6 +- 3 files changed, 114 insertions(+), 40 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 76cd3bf677c5..e67b7ead2a70 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -16,17 +16,17 @@ int main(int argc, char* argv[]) { // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); // Square - // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon p(ps, ps+4); - // Multipolygon mp; - // mp.add_polygon(p); - - // Bowtie - Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; + Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; Polygon p(ps, ps+4); Multipolygon mp; mp.add_polygon(p); + // Bowtie + // Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; + // Polygon p(ps, ps+4); + // Multipolygon mp; + // mp.add_polygon(p); + // Overlapping edge // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; // Polygon p1(ps1, ps1+4); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 526a08387012..a8ac77e8fd12 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -68,6 +68,8 @@ Multipolygon_with_holes_2 repair(const Multipolygon_wi template > class Polygon_repair_2 { + int number_of_polygons, number_of_holes; + std::unordered_map hole_nesting; public: typedef CGAL::Triangulation_vertex_base_2 Vertex_base; typedef CGAL::Constrained_triangulation_face_base_2 Face_base; @@ -78,7 +80,7 @@ class Polygon_repair_2 { typedef Triangulation_with_odd_even_constraints_2 Triangulation; /// \name Creation - Polygon_repair_2() {} + Polygon_repair_2(): number_of_polygons(0), number_of_holes(0) {} /// \name Modifiers /// @{ @@ -111,74 +113,146 @@ class Polygon_repair_2 { std::list to_check_exterior_added_by; for (auto const face: t.all_face_handles()) { face->label() = 0; - face->added_to_list() = false; + face->processed() = false; } - // Mark exterior as added to list and get interior triangles adjacent to it + // Mark exterior as processed and put interior triangles adjacent to it in to_check_interior + // these are used as starting points for the reconstruction + // Note: exterior triangles are already labelled with 0 to_check_exterior.push_back(t.infinite_face()); - t.infinite_face()->added_to_list() = true; + t.infinite_face()->processed() = true; // processed means added to a list (to ensure elements are only added once) while (!to_check_exterior.empty()) { for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (!to_check_exterior.front()->neighbor(neighbour)->added_to_list()) { + if (!to_check_exterior.front()->neighbor(neighbour)->processed()) { if (!t.is_constrained(typename Triangulation::Edge(to_check_exterior.front(), neighbour))) { to_check_exterior.push_back(to_check_exterior.front()->neighbor(neighbour)); } else { to_check_interior.push_back(to_check_exterior.front()->neighbor(neighbour)); - } to_check_exterior.front()->neighbor(neighbour)->added_to_list() = true; + } to_check_exterior.front()->neighbor(neighbour)->processed() = true; } } to_check_exterior.pop_front(); } - // Label region of front element of interior and exterior lists - int current_polygon = 1, current_hole = -1; - std::unordered_map hole_nesting; + // Label region of front element of interior and exterior lists (alternating) while (!to_check_interior.empty() || !to_check_exterior.empty()) { - // Interior + // Interior triangle if (!to_check_interior.empty()) { - if (to_check_interior.front()->label() == 0) { + if (to_check_interior.front()->label() == 0) { // label = 0 means not labelled yet + to_check_interior.front()->label() = number_of_polygons+1; to_check.push_back(to_check_interior.front()); - to_check_interior.front()->added_to_list() = true; while (!to_check.empty()) { - to_check.front()->label() = current_polygon; for (int neighbour = 0; neighbour < 3; ++neighbour) { if (!t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { - to_check.push_back(to_check.front()->neighbor(neighbour)); - } else if (!to_check.front()->neighbor(neighbour)->added_to_list()) { - to_check_exterior.push_back(to_check.front()->neighbor(neighbour)); - to_check_exterior_added_by.push_back(current_polygon); - } to_check.front()->neighbor(neighbour)->added_to_list() = true; + if (to_check.front()->neighbor(neighbour)->label() == 0) { + to_check.front()->neighbor(neighbour)->label() = number_of_polygons+1; + to_check.push_back(to_check.front()->neighbor(neighbour)); + to_check.front()->neighbor(neighbour)->processed() = true; + } + } else { // constrained -> exterior or hole + if (!to_check.front()->neighbor(neighbour)->processed()) { // only add holes (not processed) to list once + to_check_exterior.push_back(to_check.front()->neighbor(neighbour)); + to_check.front()->neighbor(neighbour)->processed() = true; + to_check_exterior_added_by.push_back(number_of_polygons+1); + } + } } to_check.pop_front(); - } ++current_polygon; + } ++number_of_polygons; } to_check_interior.pop_front(); } - // Exterior + // Exterior triangle (hole) if (!to_check_exterior.empty()) { - if (to_check_exterior.front()->label() == 0) { - hole_nesting[current_hole] = to_check_exterior_added_by.front(); + if (to_check_exterior.front()->label() == 0) { // label = 0 means not labelled yet + to_check_exterior.front()->label() = -number_of_holes+1; to_check.push_back(to_check_exterior.front()); - to_check_exterior.front()->added_to_list() = true; + hole_nesting[-number_of_holes+1] = to_check_exterior_added_by.front(); // record nesting of current hole while (!to_check.empty()) { - to_check.front()->label() = current_hole; for (int neighbour = 0; neighbour < 3; ++neighbour) { if (!t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { - to_check.push_back(to_check.front()->neighbor(neighbour)); - } else if (!to_check.front()->neighbor(neighbour)->added_to_list()) { - to_check_interior.push_back(to_check.front()->neighbor(neighbour)); - } to_check.front()->neighbor(neighbour)->added_to_list() = true; + if (to_check.front()->neighbor(neighbour)->label() == 0) { + to_check.front()->neighbor(neighbour)->label() = number_of_polygons+1; + to_check.push_back(to_check.front()->neighbor(neighbour)); + to_check.front()->neighbor(neighbour)->processed() = true; + } + } else { // constrained -> interior + if (!to_check.front()->neighbor(neighbour)->processed()) { // interior triangles only added once + to_check_interior.push_back(to_check.front()->neighbor(neighbour)); + to_check.front()->neighbor(neighbour)->processed() = true; + } + } } to_check.pop_front(); - } --current_hole; + } ++number_of_holes; } to_check_exterior.pop_front(); to_check_exterior_added_by.pop_front(); } - } + } std::cout << number_of_polygons << " polygons with " << number_of_holes << " holes in triangulation" << std::endl; + } + + // Reconstruct ring boundary starting from an edge (face + opposite vertex) that is part of it + void reconstruct_ring(Polygon_2& ring, + typename Triangulation::Face_handle face_adjacent_to_boundary, int opposite_vertex) { + typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; + int current_opposite_vertex = opposite_vertex; + do { + typename Triangulation::Vertex_handle pivot_vertex = current_face->vertex(current_face->cw(current_opposite_vertex)); + // std::cout << "Adding point " << pivot_vertex->point() << std::endl; + ring.push_back(pivot_vertex->point()); + typename Triangulation::Face_circulator fc = t.incident_faces(pivot_vertex, current_face); + do { + ++fc; + } while (fc->label() != current_face->label()); + current_face = fc; + current_opposite_vertex = fc->cw(fc->index(pivot_vertex)); + } while (current_face != face_adjacent_to_boundary || + current_opposite_vertex != opposite_vertex); } // Reconstruct multipolygon based on the triangles labelled as inside the polygon void reconstruct_multipolygon() { mp.clear(); + std::vector> polygons, holes; + polygons.reserve(number_of_polygons); + holes.reserve(number_of_holes); + + for (auto const face: t.all_face_handles()) { + face->processed() = false; + } for (auto const &face: t.finite_face_handles()) { + if (face->label() == 0) continue; // exterior triangle + if (face->processed()) continue; // already reconstructed + for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { + if (face->label() != face->neighbor(opposite_vertex)->label()) { + + if (face->label() > 0) { + polygons.emplace_back(); + reconstruct_ring(polygons.back(), face, opposite_vertex); + } else { + holes.emplace_back(); + reconstruct_ring(holes.back(), face, opposite_vertex); + } + + std::list to_check; + to_check.push_back(face); + while (!to_check.empty()) { + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (to_check.front()->label() == to_check.front()->neighbor(neighbour)->label() && + !to_check.front()->neighbor(neighbour)->processed()) { + to_check.push_back(to_check.front()->neighbor(neighbour)); + } + } to_check.front()->processed() = true; + to_check.pop_front(); + } + + break; + } + } + } + + for (auto& polygon: polygons) { + // std::cout << "Adding polygon " << polygon << std::endl; + mp.add_polygon(Polygon_with_holes_2(polygon)); + } } // Erases the triangulation. diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h index 8e1b55455c8f..d755f7078cbc 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -17,7 +17,7 @@ namespace CGAL { template > class Triangulation_face_base_with_repair_info_2 : public FaceBase { int _label; - bool _added_to_list; + bool _processed; public: typedef typename FaceBase::Vertex_handle Vertex_handle; typedef typename FaceBase::Face_handle Face_handle; @@ -37,8 +37,8 @@ class Triangulation_face_base_with_repair_info_2 : public FaceBase { Face_handle n0, Face_handle n1, Face_handle n2 ) : FaceBase(v0, v1, v2, n0, n1, n2) {} - const bool& added_to_list() const { return _added_to_list; } - bool& added_to_list() { return _added_to_list; } + const bool& processed() const { return _processed; } + bool& processed() { return _processed; } const int& label() const { return _label; } int& label() { return _label; } }; From aefce52a0430a974de855ff0cd3996fc18dfe53a Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 14 Jul 2023 10:20:00 -0600 Subject: [PATCH 084/520] simplified labelling code + fixed bugs, corrected reconstruction of holes --- .../Polygon_repair_2/repair_polygon_2.cpp | 43 ++++- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 154 ++++++++---------- 2 files changed, 106 insertions(+), 91 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index e67b7ead2a70..37c02ed74587 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -3,12 +3,14 @@ #include #include +#include // #include typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::Point_2 Point; typedef CGAL::Polygon_2 Polygon; +typedef CGAL::Polygon_with_holes_2 Polygon_with_holes; typedef CGAL::Multipolygon_with_holes_2 Multipolygon; typedef CGAL::Polygon_repair_2::Polygon_repair_2 Polygon_repair; @@ -16,10 +18,10 @@ int main(int argc, char* argv[]) { // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); // Square - Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - Polygon p(ps, ps+4); - Multipolygon mp; - mp.add_polygon(p); + // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon p(ps, ps+4); + // Multipolygon mp; + // mp.add_polygon(p); // Bowtie // Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; @@ -54,11 +56,42 @@ int main(int argc, char* argv[]) { // mp.add_polygon(p1); // mp.add_polygon(p2); + // Square with hole + // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon_with_holes p(Polygon(ps1, ps1+4)); + // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0.25,0.75)}; + // Polygon h(ps2, ps2+4); + // p.add_hole(h); + // Multipolygon mp; + // mp.add_polygon(p); + + // Square with hole touching boundary + // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Polygon_with_holes p(Polygon(ps1, ps1+4)); + // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; + // Polygon h(ps2, ps2+4); + // p.add_hole(h); + // Multipolygon mp; + // mp.add_polygon(p); + + // Square with hole touching boundary (self-intersecting loop) + // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1), + // Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; + // Polygon p(ps, ps+8); + // std::cout << p << std::endl; + // Multipolygon mp; + // mp.add_polygon(p); + Polygon_repair pr; pr.add_to_triangulation(mp); pr.label_triangulation(); + + // for (auto const f: pr.triangulation().all_face_handles()) { + // std::cout << f->label() << " "; + // } std::cout << std::endl; + pr.reconstruct_multipolygon(); - std::cout << pr.multipolygon(); + std::cout << pr.multipolygon() << std::endl; // CGAL::Polygon_repair_2::repair(mp); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index a8ac77e8fd12..ff9e8c02688c 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -69,7 +69,7 @@ template > class Polygon_repair_2 { int number_of_polygons, number_of_holes; - std::unordered_map hole_nesting; + std::vector hole_nesting; public: typedef CGAL::Triangulation_vertex_base_2 Vertex_base; typedef CGAL::Constrained_triangulation_face_base_2 Face_base; @@ -80,7 +80,7 @@ class Polygon_repair_2 { typedef Triangulation_with_odd_even_constraints_2 Triangulation; /// \name Creation - Polygon_repair_2(): number_of_polygons(0), number_of_holes(0) {} + Polygon_repair_2() : number_of_polygons(0), number_of_holes(0) {} /// \name Modifiers /// @{ @@ -107,92 +107,70 @@ class Polygon_repair_2 { } } + // Label a region of adjacent triangles without passing through constraints + // adjacent triangles that involve passing through constraints are added to to_check + void label_region(typename Triangulation::Face_handle face, int label, + std::list& to_check, + std::list& to_check_added_by) { + std::cout << "Labelling region with " << label << std::endl; + std::list to_check_in_region; + face->label() = label; + to_check_in_region.push_back(face); + face->processed() = true; // processed means added to a list (to ensure elements are only added once) + + while (!to_check_in_region.empty()) { + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (!t.is_constrained(typename Triangulation::Edge(to_check_in_region.front(), neighbour))) { + if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { + to_check_in_region.front()->neighbor(neighbour)->label() = label; + to_check_in_region.push_back(to_check_in_region.front()->neighbor(neighbour)); + to_check_in_region.front()->neighbor(neighbour)->processed() = true; + } + } else { + if (!to_check_in_region.front()->neighbor(neighbour)->processed()) { + to_check.push_back(to_check_in_region.front()->neighbor(neighbour)); + to_check_added_by.push_back(label); + to_check_in_region.front()->neighbor(neighbour)->processed() = true; + } + } + } to_check_in_region.pop_front(); + } + } + // Label triangles in triangulation void label_triangulation() { - std::list to_check_exterior, to_check_interior, to_check; - std::list to_check_exterior_added_by; for (auto const face: t.all_face_handles()) { face->label() = 0; face->processed() = false; } - // Mark exterior as processed and put interior triangles adjacent to it in to_check_interior - // these are used as starting points for the reconstruction - // Note: exterior triangles are already labelled with 0 - to_check_exterior.push_back(t.infinite_face()); - t.infinite_face()->processed() = true; // processed means added to a list (to ensure elements are only added once) - while (!to_check_exterior.empty()) { - for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (!to_check_exterior.front()->neighbor(neighbour)->processed()) { - if (!t.is_constrained(typename Triangulation::Edge(to_check_exterior.front(), neighbour))) { - to_check_exterior.push_back(to_check_exterior.front()->neighbor(neighbour)); - } else { - to_check_interior.push_back(to_check_exterior.front()->neighbor(neighbour)); - } to_check_exterior.front()->neighbor(neighbour)->processed() = true; + // Mark exterior as processed and put interior triangles adjacent to it in to_check + std::list to_check; + std::list to_check_added_by; + label_region(t.infinite_face(), -1, to_check, to_check_added_by); + + // Label region of front element to_check list + while (!to_check.empty()) { + + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), number_of_polygons+1, to_check, to_check_added_by); + ++number_of_polygons; + } else { + hole_nesting.push_back(to_check_added_by.front()); // record nesting of current hole + label_region(to_check.front(), -(number_of_holes+2), to_check, to_check_added_by); + ++number_of_holes; } - } to_check_exterior.pop_front(); - } - - // Label region of front element of interior and exterior lists (alternating) - while (!to_check_interior.empty() || !to_check_exterior.empty()) { - - // Interior triangle - if (!to_check_interior.empty()) { - if (to_check_interior.front()->label() == 0) { // label = 0 means not labelled yet - to_check_interior.front()->label() = number_of_polygons+1; - to_check.push_back(to_check_interior.front()); - while (!to_check.empty()) { - for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (!t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { - if (to_check.front()->neighbor(neighbour)->label() == 0) { - to_check.front()->neighbor(neighbour)->label() = number_of_polygons+1; - to_check.push_back(to_check.front()->neighbor(neighbour)); - to_check.front()->neighbor(neighbour)->processed() = true; - } - } else { // constrained -> exterior or hole - if (!to_check.front()->neighbor(neighbour)->processed()) { // only add holes (not processed) to list once - to_check_exterior.push_back(to_check.front()->neighbor(neighbour)); - to_check.front()->neighbor(neighbour)->processed() = true; - to_check_exterior_added_by.push_back(number_of_polygons+1); - } - } - } to_check.pop_front(); - } ++number_of_polygons; - } to_check_interior.pop_front(); - } - - // Exterior triangle (hole) - if (!to_check_exterior.empty()) { - if (to_check_exterior.front()->label() == 0) { // label = 0 means not labelled yet - to_check_exterior.front()->label() = -number_of_holes+1; - to_check.push_back(to_check_exterior.front()); - hole_nesting[-number_of_holes+1] = to_check_exterior_added_by.front(); // record nesting of current hole - while (!to_check.empty()) { - for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (!t.is_constrained(typename Triangulation::Edge(to_check.front(), neighbour))) { - if (to_check.front()->neighbor(neighbour)->label() == 0) { - to_check.front()->neighbor(neighbour)->label() = number_of_polygons+1; - to_check.push_back(to_check.front()->neighbor(neighbour)); - to_check.front()->neighbor(neighbour)->processed() = true; - } - } else { // constrained -> interior - if (!to_check.front()->neighbor(neighbour)->processed()) { // interior triangles only added once - to_check_interior.push_back(to_check.front()->neighbor(neighbour)); - to_check.front()->neighbor(neighbour)->processed() = true; - } - } - } to_check.pop_front(); - } ++number_of_holes; - } to_check_exterior.pop_front(); - to_check_exterior_added_by.pop_front(); - } + } to_check.pop_front(); + to_check_added_by.pop_front(); } std::cout << number_of_polygons << " polygons with " << number_of_holes << " holes in triangulation" << std::endl; } // Reconstruct ring boundary starting from an edge (face + opposite vertex) that is part of it - void reconstruct_ring(Polygon_2& ring, - typename Triangulation::Face_handle face_adjacent_to_boundary, int opposite_vertex) { + void reconstruct_ring(std::list& ring, + typename Triangulation::Face_handle face_adjacent_to_boundary, + int opposite_vertex) { typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; int current_opposite_vertex = opposite_vertex; do { @@ -212,24 +190,29 @@ class Polygon_repair_2 { // Reconstruct multipolygon based on the triangles labelled as inside the polygon void reconstruct_multipolygon() { mp.clear(); - std::vector> polygons, holes; - polygons.reserve(number_of_polygons); - holes.reserve(number_of_holes); + std::vector> polygons; + std::vector>> holes; + for (int i = 0; i < number_of_polygons; ++i) { + polygons.emplace_back(); + holes.emplace_back(); + } for (auto const face: t.all_face_handles()) { face->processed() = false; } for (auto const &face: t.finite_face_handles()) { - if (face->label() == 0) continue; // exterior triangle + if (face->label() == -1) continue; // exterior triangle if (face->processed()) continue; // already reconstructed for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { if (face->label() != face->neighbor(opposite_vertex)->label()) { + std::list ring; + reconstruct_ring(ring, face, opposite_vertex); if (face->label() > 0) { - polygons.emplace_back(); - reconstruct_ring(polygons.back(), face, opposite_vertex); + polygons[face->label()-1].insert(polygons[face->label()-1].vertices_end(), + ring.begin(), ring.end()); } else { - holes.emplace_back(); - reconstruct_ring(holes.back(), face, opposite_vertex); + // std::cout << "Label: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << hole_nesting[-face->label()-2] << std::endl; + holes[hole_nesting[-face->label()-2]-1].emplace_back(ring.begin(), ring.end()); } std::list to_check; @@ -249,9 +232,8 @@ class Polygon_repair_2 { } } - for (auto& polygon: polygons) { - // std::cout << "Adding polygon " << polygon << std::endl; - mp.add_polygon(Polygon_with_holes_2(polygon)); + for (int i = 0; i < polygons.size(); ++i) { + mp.add_polygon(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); } } From 3517005129427caf60c178a36e39c73a1b6f6145 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 14 Jul 2023 17:36:33 -0600 Subject: [PATCH 085/520] drawing multipolygon with holes --- .../examples/Polygon_repair_2/CMakeLists.txt | 6 +- .../Polygon_repair_2/repair_polygon_2.cpp | 11 +- .../draw_multipolygon_with_holes_2.h | 203 ++++++++++++++++++ 3 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt index 7e634d7d73e5..c4626d9d2556 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Examples) -find_package(CGAL REQUIRED) +find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) # create a target per cppfile file( @@ -14,3 +14,7 @@ file( foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") endforeach() + +if(CGAL_Qt5_FOUND) + target_link_libraries(repair_polygon_2 PUBLIC CGAL::CGAL_Basic_viewer) +endif() \ No newline at end of file diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 37c02ed74587..5bbb5e59df9e 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include // #include @@ -24,10 +24,10 @@ int main(int argc, char* argv[]) { // mp.add_polygon(p); // Bowtie - // Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; - // Polygon p(ps, ps+4); - // Multipolygon mp; - // mp.add_polygon(p); + Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; + Polygon p(ps, ps+4); + Multipolygon mp; + mp.add_polygon(p); // Overlapping edge // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; @@ -92,6 +92,7 @@ int main(int argc, char* argv[]) { pr.reconstruct_multipolygon(); std::cout << pr.multipolygon() << std::endl; + CGAL::draw(pr.multipolygon()); // CGAL::Polygon_repair_2::repair(mp); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h new file mode 100644 index 000000000000..8519699f0579 --- /dev/null +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h @@ -0,0 +1,203 @@ +// Copyright (c) 2023 GeometryFactory. All rights reserved. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H +#define CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H + +#include + +#ifdef DOXYGEN_RUNNING +namespace CGAL { + +/*! + * \ingroup PkgDrawMultipolygonWithHoles2 + * + * opens a new window and draws `aph`, an instance of the + * `CGAL::Multipolygon_with_holes_2` class. A call to this function is blocking, that + * is the program continues as soon as the user closes the window. This function + * requires `CGAL_Qt5`, and is only available if the macro + * `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target + * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition + * `CGAL_USE_BASIC_VIEWER`. + * \tparam PH an instance of the `CGAL::Multipolygon_with_holes_2` class. + * \param aph the polygon with holes to draw. + */ + +template +void draw(const PH& aph); + +} /* namespace CGAL */ + +#endif + +#ifdef CGAL_USE_BASIC_VIEWER + +#include +#include + +namespace CGAL { + +// Viewer class for Multipolygon_with_holes_2 +template +class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { + using Base = Basic_viewer_qt; + using Mpwh = Multipolygon; + using Pwh = typename Mpwh::Polygon_with_holes_2; + using Pgn = typename Mpwh::Polygon_2; + using Pnt = typename Pgn::Point_2; + +public: + /// Construct the viewer. + /// @param parent the active window to draw + /// @param mpwh the multipolygon to view + /// @param title the title of the window + Mpwh_2_basic_viewer_qt(QWidget* parent, const Mpwh& mpwh, + const char* title = "Basic Multipolygon_with_holes_2 Viewer") : + Base(parent, title, true, true, true, false, false), + m_mpwh(mpwh) { + if (mpwh.number_of_polygons() == 0) return; + + // mimic the computation of Camera::pixelGLRatio() + auto bbox = bounding_box(); + CGAL::qglviewer::Vec minv(bbox.xmin(), bbox.ymin(), 0); + CGAL::qglviewer::Vec maxv(bbox.xmax(), bbox.ymax(), 0); + auto diameter = (maxv - minv).norm(); + m_pixel_ratio = diameter / m_height; + } + + /*! Intercept the resizing of the window. + */ + virtual void resizeGL(int width, int height) { + CGAL::QGLViewer::resizeGL(width, height); + m_width = width; + m_height = height; + CGAL::qglviewer::Vec p; + auto ratio = camera()->pixelGLRatio(p); + m_pixel_ratio = ratio; + add_elements(); + } + + /*! Obtain the pixel ratio. + */ + double pixel_ratio() const { return m_pixel_ratio; } + + /*! Compute the bounding box. + */ + CGAL::Bbox_2 bounding_box() { + Bbox_2 bbox; + for (auto const& pwh: m_mpwh.polygons()) { + bbox += pwh.outer_boundary().bbox(); + } return bbox; + } + + /*! Compute the elements of a multipolygon with holes. + */ + void add_elements() { + clear(); + CGAL::IO::Color c(75,160,255); + + for (auto const& p: m_mpwh.polygons()) { + face_begin(c); + + const Pnt* point_in_face; + const auto& outer_boundary = p.outer_boundary(); + compute_loop(outer_boundary, false); + point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); + + for (auto it = p.holes_begin(); it != p.holes_end(); ++it) { + compute_loop(*it, true); + add_point_in_face(*point_in_face); + } + + face_end(); + } + } + +protected: + /*! Compute the face + */ + void compute_loop(const Pgn& p, bool hole) { + if (hole) add_point_in_face(p.vertex(p.size()-1)); + + auto prev = p.vertices_begin(); + auto it = prev; + add_point(*it); + add_point_in_face(*it); + for (++it; it != p.vertices_end(); ++it) { + add_segment(*prev, *it); // add segment with previous point + add_point(*it); + add_point_in_face(*it); // add point in face + prev = it; + } + + // Add the last segment between the last point and the first one + add_segment(*prev, *(p.vertices_begin())); + } + + virtual void keyPressEvent(QKeyEvent* e) { + // Test key pressed: + // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } + + // Call: * add_elements() if the model changed, followed by + // * redraw() if some viewing parameters changed that implies some + // modifications of the buffers + // (eg. type of normal, color/mono) + // * update() just to update the drawing + + // Call the base method to process others/classicals key + Base::keyPressEvent(e); + } + +private: + //! The window width in pixels. + int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; + + //! The window height in pixels. + int m_height = CGAL_BASIC_VIEWER_INIT_SIZE_Y; + + //! The ratio between pixel and opengl units (in world coordinate system). + double m_pixel_ratio = 1; + + //! The polygon with holes to draw. + const Mpwh& m_mpwh; +}; + +// Specialization of draw function. +template +void draw(const CGAL::Multipolygon_with_holes_2& mpwh, + const char* title = "Multipolygon_with_holes_2 Basic Viewer") +{ +#if defined(CGAL_TEST_SUITE) + bool cgal_test_suite = true; +#else + bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); +#endif + + if (! cgal_test_suite) { + using Mpwh = CGAL::Multipolygon_with_holes_2; + using Viewer = Mpwh_2_basic_viewer_qt; + CGAL::Qt::init_ogl_context(4,3); + int argc = 1; + const char* argv[2] = {"t2_viewer", nullptr}; + QApplication app(argc, const_cast(argv)); + Viewer mainwindow(app.activeWindow(), mpwh, title); + mainwindow.add_elements(); + mainwindow.show(); + app.exec(); + } +} + +} // End namespace CGAL + +#endif // CGAL_USE_BASIC_VIEWER + +#endif // CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H From b296459fb72efbf5ae6ca3b24d46bb92a69a48f4 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 14 Jul 2023 17:58:28 -0600 Subject: [PATCH 086/520] interior face iterators --- .../Polygon_repair_2/repair_polygon_2.cpp | 6 +-- ...riangulation_with_odd_even_constraints_2.h | 51 ++++++++++--------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 5bbb5e59df9e..ad34a77d06ff 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -86,9 +86,9 @@ int main(int argc, char* argv[]) { pr.add_to_triangulation(mp); pr.label_triangulation(); - // for (auto const f: pr.triangulation().all_face_handles()) { - // std::cout << f->label() << " "; - // } std::cout << std::endl; + for (auto f = pr.triangulation().interior_faces_begin(); f != pr.triangulation().interior_faces_end(); ++f) { + std::cout << f->label() << " "; + } std::cout << std::endl; pr.reconstruct_multipolygon(); std::cout << pr.multipolygon() << std::endl; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 5a4a83ed0f77..0063e6af2516 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -27,40 +27,40 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { /// \name Definition /// @{ - /// the triangulation class. typedef Triangulation_ Base_triangulation; - - /// Point type typedef typename Triangulation_::Point Point; - - /// Edge type typedef typename Triangulation_::Edge Edge; - - /// handle to a vertex typedef typename Triangulation_::Vertex_handle Vertex_handle; - - /// handle to a face typedef typename Triangulation_::Face_handle Face_handle; - - /// list of edges typedef typename Triangulation_::List_edges List_edges; - - /// list of faces typedef typename Triangulation_::List_faces List_faces; - + typedef typename Triangulation_::All_faces_iterator All_faces_iterator; /// @} - - // iterator over interior faces. - class Interior_faces_iterator : public Base_triangulation::All_faces_iterator { - - Interior_faces_iterator operator++() { - - } - Interior_faces_iterator operator--() { + class Interior_tester { + const Triangulation_with_odd_even_constraints_2 *t; + public: + Interior_tester() {} + Interior_tester(const Triangulation_with_odd_even_constraints_2 *tr) : t(tr) {} + bool operator()(const All_faces_iterator & fit) const { + return fit->label() < 1; } }; + + // iterator over interior faces. + class Interior_faces_iterator : public Filter_iterator { + typedef Filter_iterator Base; + typedef Interior_faces_iterator Self; + public: + Interior_faces_iterator() : Base() {} + Interior_faces_iterator(const Base &b) : Base(b) {} + Self& operator++() { Base::operator++(); return *this; } + Self& operator--() { Base::operator--(); return *this; } + Self operator++(int) { Self tmp(*this); ++(*this); return tmp; } + Self operator--(int) { Self tmp(*this); --(*this); return tmp; } + operator Face_handle() const { return Base::base(); } + }; // Inserts point p in the triangulation and returns the corresponding vertex. Vertex_handle insert(const Point &p, Face_handle f = Face_handle()) { @@ -113,12 +113,15 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { // Starts at an arbitrary interior face Interior_faces_iterator interior_faces_begin() { - + return CGAL::filter_iterator(Base_triangulation::all_faces_end(), + Interior_tester(this), + Base_triangulation::all_faces_begin()); } // Past-the-end iterator Interior_faces_iterator interior_faces_end() { - + return CGAL::filter_iterator(Base_triangulation::all_faces_end(), + Interior_tester(this)); } }; From 2f1f793a591b57450410fe878005231caa097718 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 14 Jul 2023 18:42:20 -0600 Subject: [PATCH 087/520] fixed simple version --- .../Polygon_repair_2/repair_polygon_2.cpp | 22 +++++++++---------- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 10 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index ad34a77d06ff..5cf72b0c39c7 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -82,20 +82,20 @@ int main(int argc, char* argv[]) { // Multipolygon mp; // mp.add_polygon(p); - Polygon_repair pr; - pr.add_to_triangulation(mp); - pr.label_triangulation(); + // Polygon_repair pr; + // pr.add_to_triangulation(mp); + // pr.label_triangulation(); - for (auto f = pr.triangulation().interior_faces_begin(); f != pr.triangulation().interior_faces_end(); ++f) { - std::cout << f->label() << " "; - } std::cout << std::endl; + // for (auto f = pr.triangulation().interior_faces_begin(); f != pr.triangulation().interior_faces_end(); ++f) { + // std::cout << f->label() << " "; + // } std::cout << std::endl; - pr.reconstruct_multipolygon(); - std::cout << pr.multipolygon() << std::endl; - CGAL::draw(pr.multipolygon()); + // pr.reconstruct_multipolygon(); + // std::cout << pr.multipolygon() << std::endl; + // CGAL::draw(pr.multipolygon()); - - // CGAL::Polygon_repair_2::repair(mp); + std::cout << CGAL::Polygon_repair_2::repair(p) << std::endl; + std::cout << CGAL::Polygon_repair_2::repair(mp) << std::endl; return 0; } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index ff9e8c02688c..12a1afe837f6 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -34,7 +34,7 @@ Multipolygon_with_holes_2 repair(const Polygon_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); - pr.reconstruct_polygon(); + pr.reconstruct_multipolygon(); return pr.multipolygon(); } @@ -45,7 +45,7 @@ Multipolygon_with_holes_2 repair(const Polygon_with_ho CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); - pr.reconstruct_polygon(); + pr.reconstruct_multipolygon(); return pr.multipolygon(); } @@ -56,7 +56,7 @@ Multipolygon_with_holes_2 repair(const Multipolygon_wi CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(mp); pr.label_triangulation(); - pr.reconstruct_polygon(); + pr.reconstruct_multipolygon(); return pr.multipolygon(); } @@ -112,7 +112,7 @@ class Polygon_repair_2 { void label_region(typename Triangulation::Face_handle face, int label, std::list& to_check, std::list& to_check_added_by) { - std::cout << "Labelling region with " << label << std::endl; + // std::cout << "Labelling region with " << label << std::endl; std::list to_check_in_region; face->label() = label; to_check_in_region.push_back(face); @@ -164,7 +164,7 @@ class Polygon_repair_2 { } to_check.pop_front(); to_check_added_by.pop_front(); - } std::cout << number_of_polygons << " polygons with " << number_of_holes << " holes in triangulation" << std::endl; + } // std::cout << number_of_polygons << " polygons with " << number_of_holes << " holes in triangulation" << std::endl; } // Reconstruct ring boundary starting from an edge (face + opposite vertex) that is part of it From cfdb167e3528c98ab812285177fdfbd782faf825 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 18 Jul 2023 10:35:48 +0200 Subject: [PATCH 088/520] Fix a couple of small issues which came up during LCC conversion --- Orthtree/include/CGAL/Orthtree.h | 1 - Orthtree/include/CGAL/Orthtree_traits_point_2.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d685f2e608f8..9d36a4986f0a 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -478,7 +478,6 @@ class Orthtree { Node_index_range traverse_indices(Traversal traversal) const { Node_index first = traversal.first_index(); - std::cout << first << std::endl; auto next = [=](const Self& tree, Node_index index) -> Maybe_node_index { return traversal.next_index(index); diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index ff4030a85106..3788667d8faa 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -53,7 +53,7 @@ struct Orthtree_traits_point_2 { using Bbox_d = Bbox_2; using FT = typename GeomTraits::FT; using Point_d = typename GeomTraits::Point_2; - using Sphere_d = typename GeomTraits::Sphere_2; + using Sphere_d = typename GeomTraits::Circle_2; using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_2; using Array = std::array; // todo: This should have a more descriptive name From e1319a92889b704b66ff0fe1c11f4c3c1afc5791 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 18 Jul 2023 19:25:55 -0600 Subject: [PATCH 089/520] mostly fixed documentation --- .../Concepts/MultiPolygonWithHoles_2.h | 6 ++-- .../Polygon_repair_2/PackageDescription.txt | 32 ++++++++++--------- .../Multipolygon_with_holes_2.h | 17 ++++++---- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h index 9ffa99054673..b97ef40ecae1 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h @@ -3,7 +3,9 @@ * * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} * - * A model of this concept represents a multipolygon with holes. + * A model of this concept represents a multipolygon with holes. + * The concept requires the ability to store the polygons with holes + * that the multipolygon is composed of. * * \cgalHasModel `CGAL::Multipolygon_with_holes_2` */ @@ -14,7 +16,7 @@ class MultipolygonWithHoles_2 { /// \name Types /// @{ -//! the polygon type used to represent each connected component of the multipolygon. +//! the polygon type used to represent each polygon with holes of the multipolygon. typedef unspecified_type Polygon_with_holes_2; /*! a bidirectional iterator over the polygons. diff --git a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt index 054a066091a7..e74c1ef034f8 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt @@ -1,23 +1,23 @@ // PRETTY PACKAGE NAME should equal the project title in Doxyfile.in -/// \defgroup PkgPolygonRepair2 2D Polygon Repair Reference -/// \defgroup PkgPolygonRepair2Concepts Concepts -/// \ingroup PkgPolygonRepair2 - -/// \defgroup PkgPolygonRepair2AlgorithmClasses Algorithm Classes -/// \ingroup PkgPolygonRepair2 +/// \defgroup PkgPolygonRepair2Ref 2D Polygon Repair Reference -/// \defgroup PkgPolygonRepair2TraitsClasses Traits Classes -/// \ingroup PkgPolygonRepair2 +/// \defgroup PkgPolygonRepair2Concepts Concepts +/// \ingroup PkgPolygonRepair2Ref /// \defgroup PkgPolygonRepair2Functions Functions -/// \ingroup PkgPolygonRepair2 +/// \ingroup PkgPolygonRepair2Ref -/// \defgroup PkgPolygonRepair2Miscellaneous Miscellaneous -/// \ingroup PkgPolygonRepair2 +/*! + \code + #include + \endcode +*/ +/// \defgroup PkgDrawMultipolygonWithHoles2 Draw a 2D Multipolygon with holes +/// \ingroup PkgPolygonRepair2Ref /*! -\addtogroup PkgPolygonRepair2 +\addtogroup PkgPolygonRepair2Ref \todo check generated documentation \cgalPkgDescriptionBegin{2D Polygon Repair,PkgPolygonRepair2} @@ -25,8 +25,8 @@ \cgalPkgSummaryBegin \cgalPkgAuthors{Ken Arroyo Ohori} -\cgalPkgDesc{The package provides algorithms to repair 2D polygons. } -\cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepair2} +\cgalPkgDesc{The package provides a 2D multipolygon class and algorithms to repair 2D (multi)polygons. } +\cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepair2Ref} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin @@ -46,10 +46,12 @@ \cgalCRPSection{Classes} - `CGAL::Multipolygon_with_holes_2` -- `CGAL::Triangulation_with_odd_even_constraints_2` - `CGAL::Polygon_repair_2` \cgalCRPSection{Functions} - `CGAL::Polygon_repair_2::repair()` +\cgalCRPSection{Draw a Multipolygon_with_holes_2} +- \link PkgDrawMultipolygonWithHoles2 CGAL::draw() \endlink + */ diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index d3f92bb9d090..9432beaa16c2 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -18,12 +18,10 @@ namespace CGAL { /*! \ingroup PkgPolygonRepair2Ref * - * The class `Multipolygon_with_holes_2` models the concept - * `MultiPolygonWithHoles_2`. - * It is parameterized with a type `Polygon` used to define the exposed - * type `Polygon_with_holes_2`. This type represents each polygon. - * - * \tparam Polygon_ must have input and output operators. + * The class `Multipolygon_with_holes_2` models the concept `MultipolygonWithHoles_2`. + * It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate + * the types `Polygon_2` and `Polygon_with_holes_2`. + * The latter is used to represents each polygon with holes. The former is converted to the latter. * * \cgalModels `MultipolygonWithHoles_2` */ @@ -33,12 +31,16 @@ class Multipolygon_with_holes_2 { public: /// \name Definition + /// @{ + /// polygon type typedef CGAL::Polygon_2 Polygon_2; /// polygon with holes type typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + /// @} + typedef std::deque Polygons_container; typedef typename Polygons_container::iterator Polygon_iterator; @@ -46,9 +48,10 @@ class Multipolygon_with_holes_2 { typedef unsigned int Size; - + /*! %Default constructor. */ Multipolygon_with_holes_2() {} + /*! Constructor from polygons. */ template Multipolygon_with_holes_2(PolygonsInputIterator p_begin, PolygonsInputIterator p_end) : From 0cc9b43729b5aa7f09b0f179ae430eb8979f187b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 18 Jul 2023 19:32:07 -0600 Subject: [PATCH 090/520] fix cmake target name --- Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt index 7e634d7d73e5..d01986d94de1 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt @@ -2,7 +2,7 @@ # This is the CMake script for compiling a CGAL application. cmake_minimum_required(VERSION 3.1...3.23) -project(Polygon_repair_2_Examples) +project(Polygon_repair_2_Tests) find_package(CGAL REQUIRED) From 31ad1cb1fb445621aafd50bd320b9585fec3626c Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 18 Jul 2023 19:45:17 -0600 Subject: [PATCH 091/520] added licence line --- .../include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h | 4 +++- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 4 +++- .../Triangulation_face_base_with_repair_info_2.h | 4 +++- .../Triangulation_with_odd_even_constraints_2.h | 4 +++- .../CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index 9432beaa16c2..71f2b789e24d 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023 GeometryFactory. All rights reserved. +// Copyright (c) 2023 GeometryFactory. // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -12,6 +12,8 @@ #ifndef CGAL_MULTIPOLYGON_WITH_HOLES_2_H #define CGAL_MULTIPOLYGON_WITH_HOLES_2_H +#include + #include namespace CGAL { diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 12a1afe837f6..5f16c2a7c11a 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023 GeometryFactory. All rights reserved. +// Copyright (c) 2023 GeometryFactory. // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -12,6 +12,8 @@ #ifndef CGAL_POLYGON_REPAIR_2_H #define CGAL_POLYGON_REPAIR_2_H +#include + #include #include #include diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h index d755f7078cbc..20a4898c240e 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023 GeometryFactory. All rights reserved. +// Copyright (c) 2023 GeometryFactory. // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -12,6 +12,8 @@ #ifndef CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H #define CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H +#include + namespace CGAL { template > diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 0063e6af2516..e5af07d57ffe 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023 GeometryFactory. All rights reserved. +// Copyright (c) 2023 GeometryFactory. // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -12,6 +12,8 @@ #ifndef CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H #define CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H +#include + namespace CGAL { /*! \ingroup PkgPolygonRepair2Ref diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h index 8519699f0579..14df7088e1dc 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023 GeometryFactory. All rights reserved. +// Copyright (c) 2023 GeometryFactory. // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -12,6 +12,8 @@ #ifndef CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H #define CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H +#include + #include #ifdef DOXYGEN_RUNNING From 5998fc0ea296729f9b76e5a8246a6287c2d7f358 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 18 Jul 2023 19:55:56 -0600 Subject: [PATCH 092/520] trimmed trailing whitespace --- .../Concepts/MultiPolygonWithHoles_2.h | 4 ++-- .../examples/Polygon_repair_2/repair_polygon_2.cpp | 2 +- .../Polygon_repair_2/Multipolygon_with_holes_2.h | 8 ++++---- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 14 +++++++------- .../Triangulation_with_odd_even_constraints_2.h | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h index b97ef40ecae1..1054217ddce7 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h @@ -3,8 +3,8 @@ * * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} * - * A model of this concept represents a multipolygon with holes. - * The concept requires the ability to store the polygons with holes + * A model of this concept represents a multipolygon with holes. + * The concept requires the ability to store the polygons with holes * that the multipolygon is composed of. * * \cgalHasModel `CGAL::Multipolygon_with_holes_2` diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 5cf72b0c39c7..1469b73124bb 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -75,7 +75,7 @@ int main(int argc, char* argv[]) { // mp.add_polygon(p); // Square with hole touching boundary (self-intersecting loop) - // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1), + // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1), // Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; // Polygon p(ps, ps+8); // std::cout << p << std::endl; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index 71f2b789e24d..528c144378c3 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -40,16 +40,16 @@ class Multipolygon_with_holes_2 { /// polygon with holes type typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; - + /// @} - + typedef std::deque Polygons_container; typedef typename Polygons_container::iterator Polygon_iterator; typedef typename Polygons_container::const_iterator Polygon_const_iterator; typedef unsigned int Size; - + /*! %Default constructor. */ Multipolygon_with_holes_2() {} @@ -97,7 +97,7 @@ respectively. The modifier `set_pretty_mode()` can be used to allow for (a few) structuring comments in the output. Otherwise, the output would be free of comments. The default for writing is \ascii without comments. -The number of polygons is exported followed by the polygons. For each polygon, +The number of polygons is exported followed by the polygons. For each polygon, the number of points of the outer boundary is exported followed by the points themselves in counterclockwise order. Then, the number of holes is exported, and for each hole, the number of points on its outer diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 5f16c2a7c11a..176678243ca7 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -133,7 +133,7 @@ class Polygon_repair_2 { to_check.push_back(to_check_in_region.front()->neighbor(neighbour)); to_check_added_by.push_back(label); to_check_in_region.front()->neighbor(neighbour)->processed() = true; - } + } } } to_check_in_region.pop_front(); } @@ -144,7 +144,7 @@ class Polygon_repair_2 { for (auto const face: t.all_face_handles()) { face->label() = 0; face->processed() = false; - } + } // Mark exterior as processed and put interior triangles adjacent to it in to_check std::list to_check; @@ -171,7 +171,7 @@ class Polygon_repair_2 { // Reconstruct ring boundary starting from an edge (face + opposite vertex) that is part of it void reconstruct_ring(std::list& ring, - typename Triangulation::Face_handle face_adjacent_to_boundary, + typename Triangulation::Face_handle face_adjacent_to_boundary, int opposite_vertex) { typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; int current_opposite_vertex = opposite_vertex; @@ -215,7 +215,7 @@ class Polygon_repair_2 { } else { // std::cout << "Label: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << hole_nesting[-face->label()-2] << std::endl; holes[hole_nesting[-face->label()-2]-1].emplace_back(ring.begin(), ring.end()); - } + } std::list to_check; to_check.push_back(face); @@ -224,10 +224,10 @@ class Polygon_repair_2 { if (to_check.front()->label() == to_check.front()->neighbor(neighbour)->label() && !to_check.front()->neighbor(neighbour)->processed()) { to_check.push_back(to_check.front()->neighbor(neighbour)); - } + } } to_check.front()->processed() = true; to_check.pop_front(); - } + } break; } @@ -258,7 +258,7 @@ class Polygon_repair_2 { } /// @} - + protected: Triangulation t; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index e5af07d57ffe..3118c8974928 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -49,7 +49,7 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { return fit->label() < 1; } }; - + // iterator over interior faces. class Interior_faces_iterator : public Filter_iterator { typedef Filter_iterator Base; From 6d9579a258e29201ec9e9303f3b5c8fd58c13ba4 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 20 Jul 2023 08:54:32 +0100 Subject: [PATCH 093/520] The word 'Polygon' is reserved by Microsoft --- .../Polygon_repair_2/repair_polygon_2.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 1469b73124bb..0b9b41bdb9b5 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -9,7 +9,7 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::Point_2 Point; -typedef CGAL::Polygon_2 Polygon; +typedef CGAL::Polygon_2 Polygon_2; typedef CGAL::Polygon_with_holes_2 Polygon_with_holes; typedef CGAL::Multipolygon_with_holes_2 Multipolygon; typedef CGAL::Polygon_repair_2::Polygon_repair_2 Polygon_repair; @@ -19,39 +19,39 @@ int main(int argc, char* argv[]) { // Square // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon p(ps, ps+4); + // Polygon_2 p(ps, ps+4); // Multipolygon mp; // mp.add_polygon(p); // Bowtie Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; - Polygon p(ps, ps+4); + Polygon_2 p(ps, ps+4); Multipolygon mp; mp.add_polygon(p); // Overlapping edge // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon p1(ps1, ps1+4); + // Polygon_2 p1(ps1, ps1+4); // Point ps2[] = {Point(1,0), Point(2,0), Point(2,1), Point(1,1)}; - // Polygon p2(ps2, ps2+4); + // Polygon_2 p2(ps2, ps2+4); // Multipolygon mp; // mp.add_polygon(p1); // mp.add_polygon(p2); // Edge partly overlapping (start) // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon p1(ps1, ps1+4); + // Polygon_2 p1(ps1, ps1+4); // Point ps2[] = {Point(1,0), Point(2,0), Point(2,0.5), Point(1,0.5)}; - // Polygon p2(ps2, ps2+4); + // Polygon_2 p2(ps2, ps2+4); // Multipolygon mp; // mp.add_polygon(p1); // mp.add_polygon(p2); // Edge partly overlapping (middle) // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon p1(ps1, ps1+4); + // Polygon_2 p1(ps1, ps1+4); // Point ps2[] = {Point(1,0.25), Point(2,0.25), Point(2,0.75), Point(1,0.75)}; - // Polygon p2(ps2, ps2+4); + // Polygon_2 p2(ps2, ps2+4); // Multipolygon mp; // mp.add_polygon(p1); // mp.add_polygon(p2); @@ -60,7 +60,7 @@ int main(int argc, char* argv[]) { // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; // Polygon_with_holes p(Polygon(ps1, ps1+4)); // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0.25,0.75)}; - // Polygon h(ps2, ps2+4); + // Polygon_2 h(ps2, ps2+4); // p.add_hole(h); // Multipolygon mp; // mp.add_polygon(p); @@ -69,7 +69,7 @@ int main(int argc, char* argv[]) { // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; // Polygon_with_holes p(Polygon(ps1, ps1+4)); // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; - // Polygon h(ps2, ps2+4); + // Polygon_2 h(ps2, ps2+4); // p.add_hole(h); // Multipolygon mp; // mp.add_polygon(p); @@ -77,7 +77,7 @@ int main(int argc, char* argv[]) { // Square with hole touching boundary (self-intersecting loop) // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1), // Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; - // Polygon p(ps, ps+8); + // Polygon_2 p(ps, ps+8); // std::cout << p << std::endl; // Multipolygon mp; // mp.add_polygon(p); From 46bd5dd9b52d74a68fdc02279fb8aa2a3ccf48d7 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 20 Jul 2023 09:39:25 +0100 Subject: [PATCH 094/520] Add needed #include of base class --- .../Triangulation_face_base_with_repair_info_2.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h index 20a4898c240e..b78a51323627 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -14,6 +14,8 @@ #include +#include + namespace CGAL { template > @@ -47,4 +49,4 @@ class Triangulation_face_base_with_repair_info_2 : public FaceBase { } //namespace CGAL -#endif // CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H \ No newline at end of file +#endif // CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H From 24ca8b4eb4c96f3cd061a12cb92d5b22c558f80e Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 20 Jul 2023 09:45:16 +0100 Subject: [PATCH 095/520] Add needed #includes --- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 1 + .../Triangulation_with_odd_even_constraints_2.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 176678243ca7..5facc1f4a90f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -14,6 +14,7 @@ #include +#include #include #include #include diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 3118c8974928..ac290d70cb1f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -13,6 +13,7 @@ #define CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H #include +#include namespace CGAL { @@ -129,4 +130,4 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { } // namespace CGAL -#endif // CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H \ No newline at end of file +#endif // CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H From 879b7ea1399bd460df61f506a446726e51fc5d3f Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 20 Jul 2023 10:51:51 +0100 Subject: [PATCH 096/520] Add WKT support for Multipolygon_with_holes --- .../Polygon_repair_2/repair_polygon_2.cpp | 1 - .../examples/Stream_support/Polygon_WKT.cpp | 12 +++++++++++- Stream_support/include/CGAL/IO/WKT.h | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 0b9b41bdb9b5..74e484462831 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -4,7 +4,6 @@ #include #include #include - // #include typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; diff --git a/Stream_support/examples/Stream_support/Polygon_WKT.cpp b/Stream_support/examples/Stream_support/Polygon_WKT.cpp index f417e4a48ee2..7c7acf1b95d6 100644 --- a/Stream_support/examples/Stream_support/Polygon_WKT.cpp +++ b/Stream_support/examples/Stream_support/Polygon_WKT.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -11,7 +12,7 @@ int main(int argc, char* argv[]) { typedef CGAL::Polygon_with_holes_2 Polygon; typedef std::deque MultiPolygon; - + typedef CGAL::Multipolygon_with_holes_2 Multipolygon_with_holes_2; { std::ifstream is((argc>1)?argv[1]:"data/polygons.wkt"); std::list polys; @@ -33,5 +34,14 @@ int main(int argc, char* argv[]) for(Polygon p : mp) std::cout<2)?argv[2]:"data/multipolygon.wkt"); + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(is, mp); + std::cout << mp << std::endl; + CGAL::IO::write_multi_polygon_WKT(std::cout, mp); + std::cout << std::endl; + } return 0; } diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index 266939338fde..303f793e0ff4 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -345,6 +346,22 @@ bool read_multi_polygon_WKT(std::istream& in, return !in.fail(); } + +template +bool read_multi_polygon_WKT(std::istream& in, + Multipolygon_with_holes_2& mp) +{ + return read_multi_polygon_WKT(in, mp.polygons()); +} + + +template +std::ostream& write_multi_polygon_WKT(std::ostream& out, + Multipolygon_with_holes_2& mp) +{ + return write_multi_polygon_WKT(out, mp.polygons()); +} + //! \ingroup PkgStreamSupportIoFuncsWKT //! //! \brief writes `point` into a WKT stream. From 12d420f827b8914ca70e01104e7c2b8c4458470d Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 20 Jul 2023 11:00:21 +0100 Subject: [PATCH 097/520] Fix Stream_support/dependencies --- Stream_support/package_info/Stream_support/dependencies | 1 + 1 file changed, 1 insertion(+) diff --git a/Stream_support/package_info/Stream_support/dependencies b/Stream_support/package_info/Stream_support/dependencies index 86fd53b13ace..33648bd70333 100644 --- a/Stream_support/package_info/Stream_support/dependencies +++ b/Stream_support/package_info/Stream_support/dependencies @@ -7,6 +7,7 @@ Kernel_23 Modular_arithmetic Number_types Polygon +Polygon_repair_2 Profiling_tools Property_map STL_Extension From 6c85e541f7d39278d6abbc796b4d54f2b642c50c Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 20 Jul 2023 11:25:25 +0100 Subject: [PATCH 098/520] Fix doc typos --- Stream_support/include/CGAL/IO/WKT.h | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index 303f793e0ff4..e91af554199e 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -68,7 +68,7 @@ void pop_back_if_equal_to_front(CGAL::Polygon_with_holes_2& pwh) //! //! \tparam Point can be a `CGAL::Point_2` or `CGAL::Point_3`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` //! \see `CGAL::Point_3` @@ -113,10 +113,10 @@ bool read_point_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes the same point type, //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` //! \see `CGAL::Point_3` @@ -160,10 +160,10 @@ bool read_multi_point_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes a `CGAL::Point_2`. //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` template @@ -204,10 +204,10 @@ bool read_linestring_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes a `Linestring`, //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` template @@ -259,7 +259,7 @@ bool read_multi_linestring_WKT(std::istream& in, //! //! \tparam Polygon is a `CGAL::General_polygon_with_holes_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::General_polygon_with_holes_2` template @@ -303,10 +303,10 @@ bool read_polygon_WKT(std::istream& in, //! and have: //! - a function `push_back()` that takes a `CGAL::General_polygon_with_holes_2`, //! - a function `clear()`, -//! - a function `resize()` that takes an `size_type` +//! - a function `resize()` that takes a `size_type` //! - an `operator[]()` that takes a `size_type`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::General_polygon_with_holes_2` template @@ -368,7 +368,7 @@ std::ostream& write_multi_polygon_WKT(std::ostream& out, //! //! \tparam Point is a `CGAL::Point_2` //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::Point_2` template @@ -388,7 +388,7 @@ std::ostream& write_point_WKT(std::ostream& out, //! //! \tparam Polygon must be a `CGAL::General_polygon_with_holes_2` //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::General_polygon_with_holes_2` template @@ -408,7 +408,7 @@ std::ostream& write_polygon_WKT(std::ostream& out, //! //! \tparam LineString must be a `RandomAccessRange` of `CGAL::Point_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //!\see `CGAL::Point_2` template @@ -429,7 +429,7 @@ std::ostream& write_linestring_WKT(std::ostream& out, //! //! \tparam MultiPoint must be a `RandomAccessRange` of `CGAL::Point_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //!\see `CGAL::Point_2` template @@ -450,7 +450,7 @@ std::ostream& write_multi_point_WKT(std::ostream& out, //! //! \tparam MultiPolygon must be a `RandomAccessRange` of `CGAL::General_polygon_with_holes_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //!\see `CGAL::General_polygon_with_holes_2` template @@ -471,7 +471,7 @@ std::ostream& write_multi_polygon_WKT(std::ostream& out, //! //! \tparam MultiLineString must be a `RandomAccessRange` of `LineString`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::IO::write_linestring_WKT()` template @@ -506,7 +506,7 @@ std::ostream& write_multi_linestring_WKT(std::ostream& out, //! \tparam MultiLineString must be a `RandomAccessRange` of `Linestring`. //! \tparam MultiPolygon must be a model of `RandomAccessRange` of `CGAL::General_polygon_with_holes_2`. //! -//! \attention Only Cartesian Kernels with double or float as `FT` are supported. +//! \attention Only %Cartesian Kernels with double or float as `FT` are supported. //! //! \see `CGAL::IO::read_linestring_WKT()` template Date: Thu, 20 Jul 2023 18:32:04 -0600 Subject: [PATCH 099/520] deterministic order from lexicographically smallest vertex, hole and polygon --- .../Polygon_repair_2/repair_polygon_2.cpp | 23 +++++-- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 65 ++++++++++++++++++- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 74e484462831..88785f6ac1a6 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -4,7 +4,7 @@ #include #include #include -// #include +#include typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::Point_2 Point; @@ -57,7 +57,7 @@ int main(int argc, char* argv[]) { // Square with hole // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon_with_holes p(Polygon(ps1, ps1+4)); + // Polygon_with_holes p(Polygon_2(ps1, ps1+4)); // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0.25,0.75)}; // Polygon_2 h(ps2, ps2+4); // p.add_hole(h); @@ -66,7 +66,7 @@ int main(int argc, char* argv[]) { // Square with hole touching boundary // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon_with_holes p(Polygon(ps1, ps1+4)); + // Polygon_with_holes p(Polygon_2(ps1, ps1+4)); // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; // Polygon_2 h(ps2, ps2+4); // p.add_hole(h); @@ -93,8 +93,21 @@ int main(int argc, char* argv[]) { // std::cout << pr.multipolygon() << std::endl; // CGAL::draw(pr.multipolygon()); - std::cout << CGAL::Polygon_repair_2::repair(p) << std::endl; - std::cout << CGAL::Polygon_repair_2::repair(mp) << std::endl; + // CGAL::IO::write_polygon_WKT(std::cout, p); + // CGAL::IO::write_multi_polygon_WKT(std::cout, mp); + + Multipolygon rmp = CGAL::Polygon_repair_2::repair(p); + + CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); + + // std::cout << "Orientations good?" << std::endl; + // for (auto const& polygon: rmp.polygons()) { + // std::cout << (polygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE) << std::endl; + // for (auto const &hole: polygon.holes()) { + // std::cout << (hole.orientation() == CGAL::CLOCKWISE) << std::endl; + // } + // } + return 0; } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 5facc1f4a90f..84594c79027d 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -82,6 +82,47 @@ class Polygon_repair_2 { typedef CGAL::Constrained_Delaunay_triangulation_2 Constrained_Delaunay_triangulation; typedef Triangulation_with_odd_even_constraints_2 Triangulation; + struct Polygon_compare { + bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { + typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); + typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); + while (va != pa.vertices_end() && vb != pb.vertices_end()) { + if (*va != *vb) return *va < *vb; + ++va; + ++vb; + } if (vb == pb.vertices_end()) return false; + return true; + } + }; + + struct Polygon_with_holes_compare { + bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { + typename Polygon_2::Vertex_iterator va = pa.outer_boundary().vertices_begin(); + typename Polygon_2::Vertex_iterator vb = pb.outer_boundary().vertices_begin(); + while (va != pa.outer_boundary().vertices_end() && vb != pb.outer_boundary().vertices_end()) { + if (*va != *vb) return *va < *vb; + ++va; + ++vb; + } if (vb != pb.outer_boundary().vertices_end()) return true; + else if (va != pa.outer_boundary().vertices_end()) return false; + typename Polygon_with_holes_2::Hole_const_iterator ha = pa.holes_begin(); + typename Polygon_with_holes_2::Hole_const_iterator hb = pb.holes_begin(); + while (ha != pa.holes_end() && hb != pb.holes_end()) { + va = ha->vertices_begin(); + vb = hb->vertices_begin(); + while (va != ha->vertices_end() && vb != hb->vertices_end()) { + if (*va != *vb) return *va < *vb; + ++va; + ++vb; + } if (vb != hb->vertices_end()) return true; + else if (va != ha->vertices_end()) return false; + ++ha; + ++hb; + } if (hb == pb.holes_end()) return false; + return true; + } + }; + /// \name Creation Polygon_repair_2() : number_of_polygons(0), number_of_holes(0) {} @@ -174,6 +215,8 @@ class Polygon_repair_2 { void reconstruct_ring(std::list& ring, typename Triangulation::Face_handle face_adjacent_to_boundary, int opposite_vertex) { + + // Create ring typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; int current_opposite_vertex = opposite_vertex; do { @@ -188,13 +231,22 @@ class Polygon_repair_2 { current_opposite_vertex = fc->cw(fc->index(pivot_vertex)); } while (current_face != face_adjacent_to_boundary || current_opposite_vertex != opposite_vertex); + + // Start at lexicographically smallest vertex + typename std::list::iterator smallest_vertex = ring.begin(); + for (typename std::list::iterator current_vertex = ring.begin(); + current_vertex != ring.end(); ++current_vertex) { + if (*current_vertex < *smallest_vertex) smallest_vertex = current_vertex; + } if (ring.front() != *smallest_vertex) { + ring.splice(ring.begin(), ring, smallest_vertex, ring.end()); + } } // Reconstruct multipolygon based on the triangles labelled as inside the polygon void reconstruct_multipolygon() { mp.clear(); std::vector> polygons; - std::vector>> holes; + std::vector, Polygon_compare>> holes; // holes are ordered for (int i = 0; i < number_of_polygons; ++i) { polygons.emplace_back(); holes.emplace_back(); @@ -215,7 +267,9 @@ class Polygon_repair_2 { ring.begin(), ring.end()); } else { // std::cout << "Label: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << hole_nesting[-face->label()-2] << std::endl; - holes[hole_nesting[-face->label()-2]-1].emplace_back(ring.begin(), ring.end()); + ring.push_back(ring.front()); + ring.pop_front(); + holes[hole_nesting[-face->label()-2]-1].insert(Polygon_2(ring.rbegin(), ring.rend())); } std::list to_check; @@ -235,8 +289,13 @@ class Polygon_repair_2 { } } + // Create polygons with holes and put in multipolygon + std::set, Polygon_with_holes_compare> ordered_polygons; for (int i = 0; i < polygons.size(); ++i) { - mp.add_polygon(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); + ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); + } for (auto const& polygon: ordered_polygons) { + std::cout << "Adding polygon " << polygon << std::endl; + mp.add_polygon(polygon); } } From 65ea5a5b32109af6f20e04590389a52c262541cd Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 20 Jul 2023 21:58:52 -0600 Subject: [PATCH 100/520] random colours for each drawn polygon --- .../examples/Polygon_repair_2/repair_polygon_2.cpp | 3 +-- .../CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 88785f6ac1a6..b0126a9625f7 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -91,14 +91,13 @@ int main(int argc, char* argv[]) { // pr.reconstruct_multipolygon(); // std::cout << pr.multipolygon() << std::endl; - // CGAL::draw(pr.multipolygon()); // CGAL::IO::write_polygon_WKT(std::cout, p); // CGAL::IO::write_multi_polygon_WKT(std::cout, mp); Multipolygon rmp = CGAL::Polygon_repair_2::repair(p); - CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); + CGAL::draw(rmp); // std::cout << "Orientations good?" << std::endl; // for (auto const& polygon: rmp.polygons()) { diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h index 14df7088e1dc..2035c225c48d 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h @@ -104,9 +104,9 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { */ void add_elements() { clear(); - CGAL::IO::Color c(75,160,255); for (auto const& p: m_mpwh.polygons()) { + CGAL::IO::Color c(rand()%255,rand()%255,rand()%255); face_begin(c); const Pnt* point_in_face; From eeb441f369a2b0c6e57f344f1b22aec47a61b8b5 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 20 Jul 2023 21:59:37 -0600 Subject: [PATCH 101/520] add Guillaume since he was the author of drawing of polygons with holes --- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 84594c79027d..6088e68bcc9a 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -8,6 +8,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Ken Arroyo Ohori +// Guillaume Damiand #ifndef CGAL_POLYGON_REPAIR_2_H #define CGAL_POLYGON_REPAIR_2_H From 53614997090569835e7a4bec1144f69b40b4019e Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 24 Jul 2023 15:29:14 -0600 Subject: [PATCH 102/520] using instead of typedefs --- .../Polygon_repair_2/repair_polygon_2.cpp | 64 +++++++++---------- .../Multipolygon_with_holes_2.h | 12 ++-- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 18 +++--- ...iangulation_face_base_with_repair_info_2.h | 8 +-- ...riangulation_with_odd_even_constraints_2.h | 20 +++--- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index b0126a9625f7..62b94eb40375 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -6,82 +6,82 @@ #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::Point_2 Point; -typedef CGAL::Polygon_2 Polygon_2; -typedef CGAL::Polygon_with_holes_2 Polygon_with_holes; -typedef CGAL::Multipolygon_with_holes_2 Multipolygon; -typedef CGAL::Polygon_repair_2::Polygon_repair_2 Polygon_repair; +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); // Square - // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Point_2 ps[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; // Polygon_2 p(ps, ps+4); - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p); // Bowtie - Point ps[] = {Point(0,0), Point(1,1), Point(1,0), Point(0,1)}; + Point_2 ps[] = {Point_2(0,0), Point_2(1,1), Point_2(1,0), Point_2(0,1)}; Polygon_2 p(ps, ps+4); - Multipolygon mp; + Multipolygon_with_holes_2 mp; mp.add_polygon(p); // Overlapping edge - // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; // Polygon_2 p1(ps1, ps1+4); - // Point ps2[] = {Point(1,0), Point(2,0), Point(2,1), Point(1,1)}; + // Point_2 ps2[] = {Point_2(1,0), Point_2(2,0), Point_2(2,1), Point_2(1,1)}; // Polygon_2 p2(ps2, ps2+4); - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p1); // mp.add_polygon(p2); // Edge partly overlapping (start) - // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; // Polygon_2 p1(ps1, ps1+4); - // Point ps2[] = {Point(1,0), Point(2,0), Point(2,0.5), Point(1,0.5)}; + // Point_2 ps2[] = {Point_2(1,0), Point_2(2,0), Point_2(2,0.5), Point_2(1,0.5)}; // Polygon_2 p2(ps2, ps2+4); - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p1); // mp.add_polygon(p2); // Edge partly overlapping (middle) - // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; + // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; // Polygon_2 p1(ps1, ps1+4); - // Point ps2[] = {Point(1,0.25), Point(2,0.25), Point(2,0.75), Point(1,0.75)}; + // Point_2 ps2[] = {Point_2(1,0.25), Point_2(2,0.25), Point_2(2,0.75), Point_2(1,0.75)}; // Polygon_2 p2(ps2, ps2+4); - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p1); // mp.add_polygon(p2); // Square with hole - // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon_with_holes p(Polygon_2(ps1, ps1+4)); - // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0.25,0.75)}; + // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; + // Polygon_with_holes_2 p(Polygon_2(ps1, ps1+4)); + // Point_2 ps2[] = {Point_2(0.25,0.25), Point_2(0.75,0.25), Point_2(0.75,0.75), Point_2(0.25,0.75)}; // Polygon_2 h(ps2, ps2+4); // p.add_hole(h); - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p); // Square with hole touching boundary - // Point ps1[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1)}; - // Polygon_with_holes p(Polygon_2(ps1, ps1+4)); - // Point ps2[] = {Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; + // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; + // Polygon_with_holes_2 p(Polygon_2(ps1, ps1+4)); + // Point_2 ps2[] = {Point_2(0.25,0.25), Point_2(0.75,0.25), Point_2(0.75,0.75), Point_2(0,1)}; // Polygon_2 h(ps2, ps2+4); // p.add_hole(h); - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p); // Square with hole touching boundary (self-intersecting loop) - // Point ps[] = {Point(0,0), Point(1,0), Point(1,1), Point(0,1), - // Point(0.25,0.25), Point(0.75,0.25), Point(0.75,0.75), Point(0,1)}; + // Point_2 ps[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1), + // Point_2(0.25,0.25), Point_2(0.75,0.25), Point_2(0.75,0.75), Point_2(0,1)}; // Polygon_2 p(ps, ps+8); // std::cout << p << std::endl; - // Multipolygon mp; + // Multipolygon_with_holes_2 mp; // mp.add_polygon(p); - // Polygon_repair pr; + // Polygon_repair_2 pr; // pr.add_to_triangulation(mp); // pr.label_triangulation(); @@ -95,7 +95,7 @@ int main(int argc, char* argv[]) { // CGAL::IO::write_polygon_WKT(std::cout, p); // CGAL::IO::write_multi_polygon_WKT(std::cout, mp); - Multipolygon rmp = CGAL::Polygon_repair_2::repair(p); + Multipolygon_with_holes_2 rmp = CGAL::Polygon_repair_2::repair(p); CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); CGAL::draw(rmp); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index 528c144378c3..ba0e883773ba 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -36,19 +36,19 @@ class Multipolygon_with_holes_2 { /// @{ /// polygon type - typedef CGAL::Polygon_2 Polygon_2; + using Polygon_2 = CGAL::Polygon_2; /// polygon with holes type - typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; /// @} - typedef std::deque Polygons_container; + using Polygons_container = std::deque; - typedef typename Polygons_container::iterator Polygon_iterator; - typedef typename Polygons_container::const_iterator Polygon_const_iterator; + using Polygon_iterator = typename Polygons_container::iterator; + using Polygon_const_iterator = typename Polygons_container::const_iterator; - typedef unsigned int Size; + using Size = unsigned int; /*! %Default constructor. */ Multipolygon_with_holes_2() {} diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 6088e68bcc9a..4d95aedecfad 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -75,15 +75,15 @@ class Polygon_repair_2 { int number_of_polygons, number_of_holes; std::vector hole_nesting; public: - typedef CGAL::Triangulation_vertex_base_2 Vertex_base; - typedef CGAL::Constrained_triangulation_face_base_2 Face_base; - typedef CGAL::Triangulation_face_base_with_repair_info_2 Face_base_with_repair_info; - typedef CGAL::Triangulation_data_structure_2 Triangulation_data_structure; - typedef CGAL::Exact_predicates_tag Tag; // assumed for now - typedef CGAL::Constrained_Delaunay_triangulation_2 Constrained_Delaunay_triangulation; - typedef Triangulation_with_odd_even_constraints_2 Triangulation; - - struct Polygon_compare { + using Vertex_base = CGAL::Triangulation_vertex_base_2; + using Face_base = CGAL::Constrained_triangulation_face_base_2; + using Face_base_with_repair_info = CGAL::Triangulation_face_base_with_repair_info_2; + using Triangulation_data_structure = CGAL::Triangulation_data_structure_2; + using Tag = CGAL::Exact_predicates_tag; // assumed for now + using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; + using Triangulation = Triangulation_with_odd_even_constraints_2; + + struct Polygon_less { bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h index b78a51323627..770b332d86b9 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -23,13 +23,13 @@ class Triangulation_face_base_with_repair_info_2 : public FaceBase { int _label; bool _processed; public: - typedef typename FaceBase::Vertex_handle Vertex_handle; - typedef typename FaceBase::Face_handle Face_handle; + using Vertex_handle = typename FaceBase::Vertex_handle; + using Face_handle = typename FaceBase::Face_handle; template struct Rebind_TDS { - typedef typename FaceBase::template Rebind_TDS::Other FaceBase2; - typedef Triangulation_face_base_with_repair_info_2 Other; + using FaceBase2 = typename FaceBase::template Rebind_TDS::Other; + using Other = Triangulation_face_base_with_repair_info_2; }; Triangulation_face_base_with_repair_info_2() : FaceBase() {} diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index ac290d70cb1f..957faf869c88 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -30,14 +30,14 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { /// \name Definition /// @{ - typedef Triangulation_ Base_triangulation; - typedef typename Triangulation_::Point Point; - typedef typename Triangulation_::Edge Edge; - typedef typename Triangulation_::Vertex_handle Vertex_handle; - typedef typename Triangulation_::Face_handle Face_handle; - typedef typename Triangulation_::List_edges List_edges; - typedef typename Triangulation_::List_faces List_faces; - typedef typename Triangulation_::All_faces_iterator All_faces_iterator; + using Base_triangulation = Triangulation_; + using Point = typename Triangulation_::Point; + using Edge = typename Triangulation_::Edge; + using Vertex_handle = typename Triangulation_::Vertex_handle; + using Face_handle = typename Triangulation_::Face_handle; + using List_edges = typename Triangulation_::List_edges; + using List_faces = typename Triangulation_::List_faces; + using All_faces_iterator = typename Triangulation_::All_faces_iterator; /// @} class Interior_tester { @@ -53,8 +53,8 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { // iterator over interior faces. class Interior_faces_iterator : public Filter_iterator { - typedef Filter_iterator Base; - typedef Interior_faces_iterator Self; + using Base = Filter_iterator; + using Self = Interior_faces_iterator; public: Interior_faces_iterator() : Base() {} Interior_faces_iterator(const Base &b) : Base(b) {} From d79c53d05a83d90b6949cfa8241924c728e3135e Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 24 Jul 2023 15:29:59 -0600 Subject: [PATCH 103/520] less instead of compare, reuse polygon less --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 4d95aedecfad..98dc589f02c8 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -96,29 +96,16 @@ class Polygon_repair_2 { } }; - struct Polygon_with_holes_compare { + struct Polygon_with_holes_less { + Polygon_less pl; bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { - typename Polygon_2::Vertex_iterator va = pa.outer_boundary().vertices_begin(); - typename Polygon_2::Vertex_iterator vb = pb.outer_boundary().vertices_begin(); - while (va != pa.outer_boundary().vertices_end() && vb != pb.outer_boundary().vertices_end()) { - if (*va != *vb) return *va < *vb; - ++va; - ++vb; - } if (vb != pb.outer_boundary().vertices_end()) return true; - else if (va != pa.outer_boundary().vertices_end()) return false; + if (pl(pa.outer_boundary(), pb.outer_boundary())) return true; + if (pl(pb.outer_boundary(), pa.outer_boundary())) return false; typename Polygon_with_holes_2::Hole_const_iterator ha = pa.holes_begin(); typename Polygon_with_holes_2::Hole_const_iterator hb = pb.holes_begin(); while (ha != pa.holes_end() && hb != pb.holes_end()) { - va = ha->vertices_begin(); - vb = hb->vertices_begin(); - while (va != ha->vertices_end() && vb != hb->vertices_end()) { - if (*va != *vb) return *va < *vb; - ++va; - ++vb; - } if (vb != hb->vertices_end()) return true; - else if (va != ha->vertices_end()) return false; - ++ha; - ++hb; + if (pl(*ha, *hb)) return true; + if (pl(*hb, *ha)) return false; } if (hb == pb.holes_end()) return false; return true; } @@ -247,7 +234,7 @@ class Polygon_repair_2 { void reconstruct_multipolygon() { mp.clear(); std::vector> polygons; - std::vector, Polygon_compare>> holes; // holes are ordered + std::vector, Polygon_less>> holes; // holes are ordered for (int i = 0; i < number_of_polygons; ++i) { polygons.emplace_back(); holes.emplace_back(); @@ -291,7 +278,7 @@ class Polygon_repair_2 { } // Create polygons with holes and put in multipolygon - std::set, Polygon_with_holes_compare> ordered_polygons; + std::set, Polygon_with_holes_less> ordered_polygons; for (int i = 0; i < polygons.size(); ++i) { ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); } for (auto const& polygon: ordered_polygons) { From 59433d9edb9e633800d5cad08fab4e4e3c883c0d Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 24 Jul 2023 18:04:51 -0600 Subject: [PATCH 104/520] shorter definitions with using --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 98dc589f02c8..0a3f9859371f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -84,9 +84,10 @@ class Polygon_repair_2 { using Triangulation = Triangulation_with_odd_even_constraints_2; struct Polygon_less { - bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { - typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); - typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); + using Polygon_2 = Polygon_2; + bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { + typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); + typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); while (va != pa.vertices_end() && vb != pb.vertices_end()) { if (*va != *vb) return *va < *vb; ++va; @@ -97,12 +98,13 @@ class Polygon_repair_2 { }; struct Polygon_with_holes_less { + using Polygon_with_holes_2 = Polygon_with_holes_2; Polygon_less pl; - bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { + bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { if (pl(pa.outer_boundary(), pb.outer_boundary())) return true; if (pl(pb.outer_boundary(), pa.outer_boundary())) return false; - typename Polygon_with_holes_2::Hole_const_iterator ha = pa.holes_begin(); - typename Polygon_with_holes_2::Hole_const_iterator hb = pb.holes_begin(); + typename Polygon_with_holes_2::Hole_const_iterator ha = pa.holes_begin(); + typename Polygon_with_holes_2::Hole_const_iterator hb = pb.holes_begin(); while (ha != pa.holes_end() && hb != pb.holes_end()) { if (pl(*ha, *hb)) return true; if (pl(*hb, *ha)) return false; From 0461ded921ee0c0d266db74747cc4423884d3322 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 24 Jul 2023 18:41:14 -0600 Subject: [PATCH 105/520] use wkt reader instead of manually constructed examples --- .../Polygon_repair_2/repair_polygon_2.cpp | 89 ++++++------------- 1 file changed, 26 insertions(+), 63 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 62b94eb40375..9a8175614559 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -17,84 +18,46 @@ int main(int argc, char* argv[]) { // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); // Square - // Point_2 ps[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; - // Polygon_2 p(ps, ps+4); - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p); + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0))"); + // Polygon_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); // Bowtie - Point_2 ps[] = {Point_2(0,0), Point_2(1,1), Point_2(1,0), Point_2(0,1)}; - Polygon_2 p(ps, ps+4); - Multipolygon_with_holes_2 mp; - mp.add_polygon(p); + std::istringstream iss("POLYGON((0 0,1 1,1 0,0 1,0 0))"); + Polygon_2 p; + CGAL::IO::read_polygon_WKT(iss, p); // Overlapping edge - // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; - // Polygon_2 p1(ps1, ps1+4); - // Point_2 ps2[] = {Point_2(1,0), Point_2(2,0), Point_2(2,1), Point_2(1,1)}; - // Polygon_2 p2(ps2, ps2+4); - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p1); - // mp.add_polygon(p2); + // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 1,1 1,1 0)))"); + // Multipolygon_with_holes_2 p; + // CGAL::IO::read_multi_polygon_WKT(iss, p); // Edge partly overlapping (start) - // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; - // Polygon_2 p1(ps1, ps1+4); - // Point_2 ps2[] = {Point_2(1,0), Point_2(2,0), Point_2(2,0.5), Point_2(1,0.5)}; - // Polygon_2 p2(ps2, ps2+4); - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p1); - // mp.add_polygon(p2); + // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 0.5,1 0.5,1 0)))"); + // Multipolygon_with_holes_2 p; + // CGAL::IO::read_multi_polygon_WKT(iss, p); // Edge partly overlapping (middle) - // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; - // Polygon_2 p1(ps1, ps1+4); - // Point_2 ps2[] = {Point_2(1,0.25), Point_2(2,0.25), Point_2(2,0.75), Point_2(1,0.75)}; - // Polygon_2 p2(ps2, ps2+4); - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p1); - // mp.add_polygon(p2); + // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0.25,2 0.25,2 0.75,1 0.75,1 0)))"); + // Multipolygon_with_holes_2 p; + // CGAL::IO::read_multi_polygon_WKT(iss, p); // Square with hole - // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; - // Polygon_with_holes_2 p(Polygon_2(ps1, ps1+4)); - // Point_2 ps2[] = {Point_2(0.25,0.25), Point_2(0.75,0.25), Point_2(0.75,0.75), Point_2(0.25,0.75)}; - // Polygon_2 h(ps2, ps2+4); - // p.add_hole(h); - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p); + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25))"); + // Polygon_with_holes_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); // Square with hole touching boundary - // Point_2 ps1[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; - // Polygon_with_holes_2 p(Polygon_2(ps1, ps1+4)); - // Point_2 ps2[] = {Point_2(0.25,0.25), Point_2(0.75,0.25), Point_2(0.75,0.75), Point_2(0,1)}; - // Polygon_2 h(ps2, ps2+4); - // p.add_hole(h); - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p); + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0 1,0.25 0.25))"); + // Polygon_with_holes_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); // Square with hole touching boundary (self-intersecting loop) - // Point_2 ps[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1), - // Point_2(0.25,0.25), Point_2(0.75,0.25), Point_2(0.75,0.75), Point_2(0,1)}; - // Polygon_2 p(ps, ps+8); - // std::cout << p << std::endl; - // Multipolygon_with_holes_2 mp; - // mp.add_polygon(p); - - // Polygon_repair_2 pr; - // pr.add_to_triangulation(mp); - // pr.label_triangulation(); - - // for (auto f = pr.triangulation().interior_faces_begin(); f != pr.triangulation().interior_faces_end(); ++f) { - // std::cout << f->label() << " "; - // } std::cout << std::endl; - - // pr.reconstruct_multipolygon(); - // std::cout << pr.multipolygon() << std::endl; - - // CGAL::IO::write_polygon_WKT(std::cout, p); - // CGAL::IO::write_multi_polygon_WKT(std::cout, mp); + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0.25 0.25,0.75 0.25,0.75 0.75,0 1,0 0))"); + // Polygon_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); + std::cout << p << std::endl; Multipolygon_with_holes_2 rmp = CGAL::Polygon_repair_2::repair(p); CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); CGAL::draw(rmp); From 8dea133a07469c6ecf3ffee43e88973c179a2d87 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 24 Jul 2023 22:05:30 -0600 Subject: [PATCH 106/520] more examples --- .../Polygon_repair_2/repair_polygon_2.cpp | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 9a8175614559..686324c4d0af 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -23,9 +25,9 @@ int main(int argc, char* argv[]) { // CGAL::IO::read_polygon_WKT(iss, p); // Bowtie - std::istringstream iss("POLYGON((0 0,1 1,1 0,0 1,0 0))"); - Polygon_2 p; - CGAL::IO::read_polygon_WKT(iss, p); + // std::istringstream iss("POLYGON((0 0,1 1,1 0,0 1,0 0))"); + // Polygon_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); // Overlapping edge // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 1,1 1,1 0)))"); @@ -52,12 +54,37 @@ int main(int argc, char* argv[]) { // Polygon_with_holes_2 p; // CGAL::IO::read_polygon_WKT(iss, p); + // Square with hole touching boundary twice + std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,1 0,0.75 0.75,0 1,0.25 0.25))"); + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + // Square with hole touching boundary (self-intersecting loop) // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0.25 0.25,0.75 0.25,0.75 0.75,0 1,0 0))"); // Polygon_2 p; // CGAL::IO::read_polygon_WKT(iss, p); - std::cout << p << std::endl; + // Square with hole using bridge edge + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25,0.25 0.75,0 1,0 0))"); + // Polygon_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); + + // Square with hole outside + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25))"); + // Polygon_with_holes_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); + + // Square with hole overlapping outer boundary + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0.5,1 0.5,1 1,0.5 1,0.5 0.5))"); + // Polygon_with_holes_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); + + // Square with hole partly outside + // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.75 0.75,1.25 0.75,1.25 1.25,0.75 1.25,0.75 0.75))"); + // Polygon_with_holes_2 p; + // CGAL::IO::read_polygon_WKT(iss, p); + + CGAL::draw(p); Multipolygon_with_holes_2 rmp = CGAL::Polygon_repair_2::repair(p); CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); CGAL::draw(rmp); From 3df83bc224f810138bb66b2995d619bd53ab945c Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 25 Jul 2023 15:17:50 -0600 Subject: [PATCH 107/520] new code for hole nesting --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 0a3f9859371f..ce9f720b3819 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -38,6 +38,7 @@ Multipolygon_with_holes_2 repair(const Polygon_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); + pr.compute_hole_nesting(); pr.reconstruct_multipolygon(); return pr.multipolygon(); } @@ -49,6 +50,7 @@ Multipolygon_with_holes_2 repair(const Polygon_with_ho CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); + pr.compute_hole_nesting(); pr.reconstruct_multipolygon(); return pr.multipolygon(); } @@ -60,6 +62,7 @@ Multipolygon_with_holes_2 repair(const Multipolygon_wi CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(mp); pr.label_triangulation(); + pr.compute_hole_nesting(); pr.reconstruct_multipolygon(); return pr.multipolygon(); } @@ -72,8 +75,6 @@ Multipolygon_with_holes_2 repair(const Multipolygon_wi template > class Polygon_repair_2 { - int number_of_polygons, number_of_holes; - std::vector hole_nesting; public: using Vertex_base = CGAL::Triangulation_vertex_base_2; using Face_base = CGAL::Constrained_triangulation_face_base_2; @@ -155,13 +156,13 @@ class Polygon_repair_2 { while (!to_check_in_region.empty()) { for (int neighbour = 0; neighbour < 3; ++neighbour) { if (!t.is_constrained(typename Triangulation::Edge(to_check_in_region.front(), neighbour))) { - if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { + if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { // unlabelled to_check_in_region.front()->neighbor(neighbour)->label() = label; to_check_in_region.push_back(to_check_in_region.front()->neighbor(neighbour)); to_check_in_region.front()->neighbor(neighbour)->processed() = true; } - } else { - if (!to_check_in_region.front()->neighbor(neighbour)->processed()) { + } else { // constrained + if (!to_check_in_region.front()->neighbor(neighbour)->processed()) { // not added to to_check to_check.push_back(to_check_in_region.front()->neighbor(neighbour)); to_check_added_by.push_back(label); to_check_in_region.front()->neighbor(neighbour)->processed() = true; @@ -178,7 +179,8 @@ class Polygon_repair_2 { face->processed() = false; } - // Mark exterior as processed and put interior triangles adjacent to it in to_check + // Label exterior with label -1, marking it as processed and + // putting interior triangles adjacent to it in to_check std::list to_check; std::list to_check_added_by; label_region(t.infinite_face(), -1, to_check, to_check_added_by); @@ -191,7 +193,6 @@ class Polygon_repair_2 { label_region(to_check.front(), number_of_polygons+1, to_check, to_check_added_by); ++number_of_polygons; } else { - hole_nesting.push_back(to_check_added_by.front()); // record nesting of current hole label_region(to_check.front(), -(number_of_holes+2), to_check, to_check_added_by); ++number_of_holes; } @@ -232,6 +233,24 @@ class Polygon_repair_2 { } } + void compute_hole_nesting() { + for (int i = 0; i < number_of_holes; ++i) { + nesting.emplace_back(); + } for (auto const &face: t.finite_face_handles()) { + if (face->label() >= -1) continue; // skip non-hole triangles + for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { + if (face->label() == face->neighbor(opposite_vertex)->label()) continue; + nesting[-face->label()-2].insert(face->neighbor(opposite_vertex)->label()); + } + } int hole_label = -2; + for (auto const &hole: nesting) { + std::cout << "Hole " << hole_label-- << " contained in polygon(s): "; + for (auto const &polygon: hole) { + std::cout << polygon << " "; + } std::cout << std::endl; + } + } + // Reconstruct multipolygon based on the triangles labelled as inside the polygon void reconstruct_multipolygon() { mp.clear(); @@ -246,6 +265,7 @@ class Polygon_repair_2 { face->processed() = false; } for (auto const &face: t.finite_face_handles()) { if (face->label() == -1) continue; // exterior triangle + if (face->label() < -1 && nesting[-face->label()-2].size() > 1) continue; // exterior triangle if (face->processed()) continue; // already reconstructed for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { if (face->label() != face->neighbor(opposite_vertex)->label()) { @@ -256,10 +276,11 @@ class Polygon_repair_2 { polygons[face->label()-1].insert(polygons[face->label()-1].vertices_end(), ring.begin(), ring.end()); } else { - // std::cout << "Label: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << hole_nesting[-face->label()-2] << std::endl; + int hole_nesting = *(nesting[-face->label()-2].begin()); + // std::cout << "Hole: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << hole_nesting << std::endl; ring.push_back(ring.front()); ring.pop_front(); - holes[hole_nesting[-face->label()-2]-1].insert(Polygon_2(ring.rbegin(), ring.rend())); + holes[hole_nesting-1].insert(Polygon_2(ring.rbegin(), ring.rend())); } std::list to_check; @@ -313,6 +334,8 @@ class Polygon_repair_2 { protected: Triangulation t; Multipolygon_with_holes_2 mp; + int number_of_polygons, number_of_holes; + std::vector> nesting; // note: holes are surrounded by exactly one polygon }; } // namespace Polygon_repair_2 From 842bd857cc5a3f368e936274ef0c35709de9128f Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 25 Jul 2023 17:29:55 -0600 Subject: [PATCH 108/520] Testing using reference files --- .../Polygon_repair_2/repair_polygon_2.cpp | 109 +++++------------- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 22 ++-- .../test/Polygon_repair_2/data/in/bowtie.wkt | 1 + .../Polygon_repair_2/data/in/bridge-edge.wkt | 1 + .../Polygon_repair_2/data/in/hole-as-loop.wkt | 1 + .../Polygon_repair_2/data/in/hole-carved.wkt | 1 + .../Polygon_repair_2/data/in/hole-filled.wkt | 1 + .../Polygon_repair_2/data/in/hole-outside.wkt | 1 + .../data/in/hole-partly-outside.wkt | 1 + .../data/in/hole-touching-once.wkt | 1 + .../data/in/hole-touching-twice.wkt | 1 + .../test/Polygon_repair_2/data/in/hole.wkt | 1 + .../data/in/overlapping-edge-inside.wkt | 1 + .../data/in/overlapping-edge-partial.wkt | 1 + .../data/in/overlapping-edge.wkt | 1 + .../data/in/spike-boundary.wkt | 1 + .../Polygon_repair_2/data/in/spike-in.wkt | 1 + .../Polygon_repair_2/data/in/spike-out.wkt | 1 + .../test/Polygon_repair_2/data/in/square.wkt | 1 + .../test/Polygon_repair_2/data/ref/bowtie.wkt | 1 + .../Polygon_repair_2/data/ref/bridge-edge.wkt | 1 + .../data/ref/hole-as-loop.wkt | 1 + .../Polygon_repair_2/data/ref/hole-carved.wkt | 1 + .../Polygon_repair_2/data/ref/hole-filled.wkt | 1 + .../data/ref/hole-outside.wkt | 1 + .../data/ref/hole-partly-outside.wkt | 1 + .../data/ref/hole-touching-once.wkt | 1 + .../data/ref/hole-touching-twice.wkt | 1 + .../test/Polygon_repair_2/data/ref/hole.wkt | 1 + .../data/ref/overlapping-edge-inside.wkt | 1 + .../data/ref/overlapping-edge-partial.wkt | 1 + .../data/ref/overlapping-edge.wkt | 1 + .../data/ref/spike-boundary.wkt | 1 + .../Polygon_repair_2/data/ref/spike-in.wkt | 1 + .../Polygon_repair_2/data/ref/spike-out.wkt | 1 + .../test/Polygon_repair_2/data/ref/square.wkt | 1 + .../repair_polygon_2_test.cpp | 75 ++++++++++-- 37 files changed, 142 insertions(+), 98 deletions(-) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/bowtie.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/bridge-edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-as-loop.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-carved.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-filled.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-outside.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-partly-outside.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-twice.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-inside.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-partial.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spike-boundary.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spike-in.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spike-out.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/square.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/bowtie.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/bridge-edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-as-loop.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-carved.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-filled.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-outside.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-partly-outside.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-once.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-twice.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-inside.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-boundary.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-in.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-out.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/square.wkt diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 686324c4d0af..b7ea6b42b1df 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -17,86 +18,36 @@ using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - // std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); - // Square - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0))"); - // Polygon_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Bowtie - // std::istringstream iss("POLYGON((0 0,1 1,1 0,0 1,0 0))"); - // Polygon_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Overlapping edge - // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 1,1 1,1 0)))"); - // Multipolygon_with_holes_2 p; - // CGAL::IO::read_multi_polygon_WKT(iss, p); - - // Edge partly overlapping (start) - // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 0.5,1 0.5,1 0)))"); - // Multipolygon_with_holes_2 p; - // CGAL::IO::read_multi_polygon_WKT(iss, p); - - // Edge partly overlapping (middle) - // std::istringstream iss("MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0.25,2 0.25,2 0.75,1 0.75,1 0)))"); - // Multipolygon_with_holes_2 p; - // CGAL::IO::read_multi_polygon_WKT(iss, p); - - // Square with hole - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25))"); - // Polygon_with_holes_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole touching boundary - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0 1,0.25 0.25))"); - // Polygon_with_holes_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole touching boundary twice - std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,1 0,0.75 0.75,0 1,0.25 0.25))"); - Polygon_with_holes_2 p; - CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole touching boundary (self-intersecting loop) - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0.25 0.25,0.75 0.25,0.75 0.75,0 1,0 0))"); - // Polygon_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole using bridge edge - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25,0.25 0.75,0 1,0 0))"); - // Polygon_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole outside - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25))"); - // Polygon_with_holes_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole overlapping outer boundary - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0.5,1 0.5,1 1,0.5 1,0.5 0.5))"); - // Polygon_with_holes_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - // Square with hole partly outside - // std::istringstream iss("POLYGON((0 0,1 0,1 1,0 1,0 0),(0.75 0.75,1.25 0.75,1.25 1.25,0.75 1.25,0.75 0.75))"); - // Polygon_with_holes_2 p; - // CGAL::IO::read_polygon_WKT(iss, p); - - CGAL::draw(p); - Multipolygon_with_holes_2 rmp = CGAL::Polygon_repair_2::repair(p); - CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); - CGAL::draw(rmp); - - // std::cout << "Orientations good?" << std::endl; - // for (auto const& polygon: rmp.polygons()) { - // std::cout << (polygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE) << std::endl; - // for (auto const &hole: polygon.holes()) { - // std::cout << (hole.orientation() == CGAL::CLOCKWISE) << std::endl; - // } - // } - + for (const auto& file: std::__fs::filesystem::directory_iterator("../../test/Polygon_repair_2/data/in")) { + std::cout << "Reading " << file.path().filename() << "..." << std::endl; + + // if (file.path().filename() != "hole-outside.wkt") continue; + + std::string in; + std::getline(std::ifstream(file.path()), in); + std::istringstream iss(in); + std::size_t is_polygon = in.find("POLYGON"); + std::size_t is_multipolygon = in.find("MULTIPOLYGON"); + Multipolygon_with_holes_2 rmp; + if (is_polygon == 0) { + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + CGAL::draw(p); + rmp = CGAL::Polygon_repair_2::repair(p); + } else if (is_multipolygon == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + CGAL::draw(mp); + rmp = CGAL::Polygon_repair_2::repair(mp); + } std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + std::cout << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + CGAL::draw(rmp); + + } return 0; } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index ce9f720b3819..cc0535d37cd2 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -179,7 +179,7 @@ class Polygon_repair_2 { face->processed() = false; } - // Label exterior with label -1, marking it as processed and + // Label exterior with label -1, marking it as processed and // putting interior triangles adjacent to it in to_check std::list to_check; std::list to_check_added_by; @@ -225,7 +225,7 @@ class Polygon_repair_2 { // Start at lexicographically smallest vertex typename std::list::iterator smallest_vertex = ring.begin(); - for (typename std::list::iterator current_vertex = ring.begin(); + for (typename std::list::iterator current_vertex = ring.begin(); current_vertex != ring.end(); ++current_vertex) { if (*current_vertex < *smallest_vertex) smallest_vertex = current_vertex; } if (ring.front() != *smallest_vertex) { @@ -242,13 +242,15 @@ class Polygon_repair_2 { if (face->label() == face->neighbor(opposite_vertex)->label()) continue; nesting[-face->label()-2].insert(face->neighbor(opposite_vertex)->label()); } - } int hole_label = -2; - for (auto const &hole: nesting) { - std::cout << "Hole " << hole_label-- << " contained in polygon(s): "; - for (auto const &polygon: hole) { - std::cout << polygon << " "; - } std::cout << std::endl; - } + } + + // int hole_label = -2; + // for (auto const &hole: nesting) { + // std::cout << "Hole " << hole_label-- << " contained in polygon(s): "; + // for (auto const &polygon: hole) { + // std::cout << polygon << " "; + // } std::cout << std::endl; + // } } // Reconstruct multipolygon based on the triangles labelled as inside the polygon @@ -305,7 +307,7 @@ class Polygon_repair_2 { for (int i = 0; i < polygons.size(); ++i) { ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); } for (auto const& polygon: ordered_polygons) { - std::cout << "Adding polygon " << polygon << std::endl; + // std::cout << "Adding polygon " << polygon << std::endl; mp.add_polygon(polygon); } } diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/bowtie.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/bowtie.wkt new file mode 100644 index 000000000000..3005eb3e3f60 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/bowtie.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,1 0,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/bridge-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/bridge-edge.wkt new file mode 100644 index 000000000000..1e86cf795061 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/bridge-edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25,0.25 0.75,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-as-loop.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-as-loop.wkt new file mode 100644 index 000000000000..8c5a4a1c74c9 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-as-loop.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.25 0.25,0.75 0.25,0.75 0.75,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-carved.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-carved.wkt new file mode 100644 index 000000000000..7b20d27fcfb9 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-carved.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0.5,1 0.5,1 1,0.5 1,0.5 0.5)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-filled.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-filled.wkt new file mode 100644 index 000000000000..ca7167347792 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-filled.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25)),((0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-outside.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-outside.wkt new file mode 100644 index 000000000000..9d16d9c87cae --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-outside.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-partly-outside.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-partly-outside.wkt new file mode 100644 index 000000000000..da99d6a31e50 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-partly-outside.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.75 0.75,1.25 0.75,1.25 1.25,0.75 1.25,0.75 0.75)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt new file mode 100644 index 000000000000..587930ced4c6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0 1,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-twice.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-twice.wkt new file mode 100644 index 000000000000..54f1083af911 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-twice.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,1 0,0.75 0.75,0 1,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole.wkt new file mode 100644 index 000000000000..6c0e1bfe42b2 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.25 0.75,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-inside.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-inside.wkt new file mode 100644 index 000000000000..646ca0d5b5ad --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-inside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0.25,2 0.25,2 0.75,1 0.75,1 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-partial.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-partial.wkt new file mode 100644 index 000000000000..2dc63c238221 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-partial.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 0.5,1 0.5,1 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge.wkt new file mode 100644 index 000000000000..6e59f2e83e58 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1 0,2 0,2 1,1 1,1 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-boundary.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-boundary.wkt new file mode 100644 index 000000000000..477315fcf87d --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-boundary.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-in.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-in.wkt new file mode 100644 index 000000000000..49f02f9d1e48 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-in.wkt @@ -0,0 +1 @@ +POLYGON((0 0,0.5 0.5,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-out.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-out.wkt new file mode 100644 index 000000000000..7a95b726a2f8 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-out.wkt @@ -0,0 +1 @@ +POLYGON((0 0,-0.5 -0.5,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/square.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/square.wkt new file mode 100644 index 000000000000..d97ff8d423bf --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/square.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/bowtie.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/bowtie.wkt new file mode 100644 index 000000000000..0ca7c0da217b --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/bowtie.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,0.5 0.5,0 1,0 0)),((0.5 0.5,1 0,1 1,0.5 0.5))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/bridge-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/bridge-edge.wkt new file mode 100644 index 000000000000..99b3f4c1cbbf --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/bridge-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-as-loop.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-as-loop.wkt new file mode 100644 index 000000000000..b5ee1d022877 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-as-loop.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0 1,0.75 0.75,0.75 0.25,0.25 0.25,0 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-carved.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-carved.wkt new file mode 100644 index 000000000000..d6e9a18d49e8 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-carved.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 0.5,0.5 0.5,0.5 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-filled.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-filled.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-filled.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-outside.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-outside.wkt new file mode 100644 index 000000000000..1d003ce0e92d --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-outside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-partly-outside.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-partly-outside.wkt new file mode 100644 index 000000000000..503189d5d256 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-partly-outside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 0.75,0.75 0.75,0.75 1,0 1,0 0)),((0.75 1,1 1,1 0.75,1.25 0.75,1.25 1.25,0.75 1.25,0.75 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-once.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-once.wkt new file mode 100644 index 000000000000..b5ee1d022877 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-once.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0 1,0.75 0.75,0.75 0.25,0.25 0.25,0 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-twice.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-twice.wkt new file mode 100644 index 000000000000..bc530227b8fb --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-twice.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,0.25 0.25,0 1,0 0)),((0 1,0.75 0.75,1 0,1 1,0 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole.wkt new file mode 100644 index 000000000000..99b3f4c1cbbf --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-inside.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-inside.wkt new file mode 100644 index 000000000000..f801b7ff6ecc --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-inside.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 0.25,2 0.25,2 0.75,1 0.75,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt new file mode 100644 index 000000000000..f7372fef3b31 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,2 0,2 0.5,1 0.5,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt new file mode 100644 index 000000000000..33b6059e3993 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,2 0,2 1,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-boundary.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-boundary.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-boundary.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-in.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-in.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-in.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-out.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-out.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-out.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/square.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/square.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/square.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index 20b2c9694a81..c011815ca57d 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -1,19 +1,76 @@ #include #include +#include +#include #include -#include -#include -#include +#include #include +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Polygon_2 Polygon_2; -typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; +int main(int argc, char* argv[]) { + + for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { + std::cout << "Testing " << file.path().filename() << "... "; + + // Read test file + std::string in; + std::getline(std::ifstream(file.path()), in); + + // Load test file and repair to create output + std::istringstream iss(in); + std::size_t is_polygon = in.find("POLYGON"); + std::size_t is_multipolygon = in.find("MULTIPOLYGON"); + Multipolygon_with_holes_2 rmp; + if (is_polygon == 0) { + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + rmp = CGAL::Polygon_repair_2::repair(p); + } else if (is_multipolygon == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + rmp = CGAL::Polygon_repair_2::repair(mp); + } std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + + // Read reference file + std::string ref_path = "data/ref/"; + ref_path += file.path().filename(); + std::ifstream ref_ifs(ref_path); + if (ref_ifs.fail()) { + std::cout << std::endl << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + std::cout << "\tno reference output -> skipped" << std::endl; + continue; + } std::string ref; + std::getline(ref_ifs, ref); + ref += "\n"; + + // Compare output with reference file + if (ref == out) { + std::cout << "ok" << std::endl; + } else { + std::cout << "fail" << std::endl; + std::cout << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + std::cout << "\tref: " << ref; + } CGAL_assertion(ref == out); + + // Test orientations + for (auto const& polygon: rmp.polygons()) { + CGAL_assertion(polygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE); + for (auto const &hole: polygon.holes()) { + CGAL_assertion(hole.orientation() == CGAL::CLOCKWISE); + } + } + } -int main(int argc, char* argv[]) -{ - std::ifstream ifs( (argc==1)?"data/polygon.wkt":argv[1]); return 0; } From 61a29f0fdb560baff2d01805540a5dd84a9580c9 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Jul 2023 09:57:44 +0200 Subject: [PATCH 109/520] Fix an issue where side-per-depth wasn't updated when a node was manually split --- Orthtree/include/CGAL/Orthtree.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9d36a4986f0a..31daf3a441cd 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -214,8 +214,7 @@ class Orthtree { \param enlarge_ratio ratio to which the bounding box should be enlarged. \param traits the traits object. */ - Orthtree(Traits traits, - const FT enlarge_ratio = 1.2) : + explicit Orthtree(Traits traits, const FT enlarge_ratio = 1.2) : m_traits(traits), m_node_points(m_node_properties.add_property("points")), m_node_depths(m_node_properties.add_property("depths", 0)), @@ -325,13 +324,6 @@ class Orthtree { // Check if this node needs to be processed if (split_predicate(current, *this)) { - // Check if we've reached a new max depth - if (depth(current) == depth()) { - - // Update the side length map - m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); - } - // Split the node, redistributing its points to its children split(current); @@ -839,6 +831,12 @@ class Orthtree { m_node_parents[c] = n; } + // Check if we've reached a new max depth + if (depth(n) + 1 == m_side_per_depth.size()) { + // Update the side length map + m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); + } + // Find the point around which the node is split Point center = barycenter(n); From 7027d5b4694b6925a1a2563548868ff3db3314e8 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Jul 2023 10:48:04 +0200 Subject: [PATCH 110/520] Adapt several examples to use the new interface --- .../Orthtree/octree_build_from_point_set.cpp | 6 +++--- .../Orthtree/octree_build_from_point_vector.cpp | 6 +++--- .../Orthtree/octree_build_with_custom_split.cpp | 17 ++++++++++------- .../Orthtree/octree_find_nearest_neighbor.cpp | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index 4ef69692131e..38bbd1e02d2c 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -20,7 +20,7 @@ int main(int argc, char **argv) { Point_set points; // Load points from a file. - std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/cube.pwn")); + std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/cube.xyz")); stream >> points; if (0 == points.number_of_points()) { @@ -30,13 +30,13 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Build the octree with a small bucket size, using a more verbose method octree.refine(CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers(5, 10)); // Print out the tree - std::cout << octree; + std::cout << octree << std::endl; return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp index 71f1393df32b..e12a895cfc8b 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp @@ -6,7 +6,7 @@ // Type Declarations typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_3 Point; -typedef std::list Point_vector; +typedef std::vector Point_vector; // todo: this was changed to std::list at some point; why? typedef CGAL::Octree Octree; @@ -24,10 +24,10 @@ int main() { points.emplace_back(-1, 1, 1); // Create an octree from the points - Octree octree(points); + Octree octree({points}); // Build the octree - octree.refine(10, 20); + octree.refine(10, 1); // Print out the tree std::cout << octree; diff --git a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp index 3202c6c9ade1..9b67d61e2a72 100644 --- a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp +++ b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp @@ -21,11 +21,14 @@ struct Split_by_ratio { std::size_t ratio; - Split_by_ratio(std::size_t ratio) : ratio(ratio) {} - - template - bool operator()(const Node &n) const { - return n.size() > (ratio * n.depth()); + explicit Split_by_ratio(std::size_t ratio) : ratio(ratio) {} + + template + bool operator()(Node_index i, const Tree &tree) const { + std::size_t num_points = tree.data(i).size(); + std::size_t depth = tree.depth(i); + std::cout << num_points << " " << depth << std::endl; + return num_points > (ratio * depth); } }; @@ -35,7 +38,7 @@ int main(int argc, char **argv) { Point_set points; // Load points from a file. - std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/cube.pwn")); + std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/kitten.off")); stream >> points; if (0 == points.number_of_points()) { @@ -45,7 +48,7 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points" << std::endl; // Create an octree from the points - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Build the octree using our custom split predicate octree.refine(Split_by_ratio(2)); diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index e94fca1ee206..dc6e5f415a37 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -32,7 +32,7 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points" << std::endl; // Create an octree from the points - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Build the octree octree.refine(10, 20); From 50006c97b45e258d5f2ac6453b5519d45f599cdc Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Jul 2023 11:04:03 +0200 Subject: [PATCH 111/520] Fix an issue with file loading where adding normals would cause additional points to be created --- Point_set_3/include/CGAL/Point_set_3.h | 33 ++++++++++++++----- Point_set_3/test/Point_set_3/CMakeLists.txt | 1 + .../test/Point_set_3/file_load_test.cpp | 33 +++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 Point_set_3/test/Point_set_3/file_load_test.cpp diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 40dda167e9b3..4da5ac0c522e 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -1110,19 +1110,29 @@ class Point_set_3 Point_set& ps; Property_map map; + Index index; public: - Property_back_inserter(Point_set& ps, Properties::Property_array& prop) : - ps(ps), map(prop) {} + Property_back_inserter( + Point_set& ps, + Properties::Property_array& prop, + Index ind = Index{0} + ) : ps(ps), map(prop), index(ind) {} Property_back_inserter& operator++() { return *this; } Property_back_inserter& operator++(int) { return *this; } Property_back_inserter& operator*() { return *this; } Property_back_inserter& operator= (const value_type& p) { - auto new_index = *ps.insert(); - put(map, new_index, p); + if(ps.size() <= index) + ps.insert(); + put(map, index, p); + ++index; return *this; + +// auto new_index = *ps.insert(); +// put(map, new_index, p); +// return *this; } }; @@ -1140,14 +1150,21 @@ class Point_set_3 Point_set& ps; Property_map map; + mutable Index index; - Push_property_map(Point_set& ps, Properties::Property_array& prop) : - ps(ps), map(prop) {} + Push_property_map( + Point_set& ps, + Properties::Property_array& prop, + Index ind = Index{0} + ) : ps(ps), map(prop) {} friend void put(const Push_property_map& pm, Index& i, const value_type& t) { - i = *pm.ps.insert(); - put(pm.map, i, t); + ++pm.index; + if(pm.ps.size() <= pm.index) + pm.ps.insert(); + put(pm.map, pm.index, t); + i = pm.index; } friend reference get (const Push_property_map& pm, const Index& i) diff --git a/Point_set_3/test/Point_set_3/CMakeLists.txt b/Point_set_3/test/Point_set_3/CMakeLists.txt index 512733089561..29a9dba31415 100644 --- a/Point_set_3/test/Point_set_3/CMakeLists.txt +++ b/Point_set_3/test/Point_set_3/CMakeLists.txt @@ -8,6 +8,7 @@ project(Point_set_3_Tests) find_package(CGAL REQUIRED) create_single_source_cgal_program("copy_construction_test.cpp") +create_single_source_cgal_program("file_load_test.cpp") create_single_source_cgal_program("point_insertion_test.cpp") create_single_source_cgal_program("point_set_test.cpp") create_single_source_cgal_program("point_set_test_join.cpp") diff --git a/Point_set_3/test/Point_set_3/file_load_test.cpp b/Point_set_3/test/Point_set_3/file_load_test.cpp new file mode 100644 index 000000000000..82d3eb34fbd4 --- /dev/null +++ b/Point_set_3/test/Point_set_3/file_load_test.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include + +#include + +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; + +typedef CGAL::Point_set_3 Point_set; + +int main() { + + std::ifstream stream_xyz{CGAL::data_file_path("points_3/cube.xyz")}; + Point_set points_xyz; + stream_xyz >> points_xyz; + assert(points_xyz.number_of_points() == 8); + assert(!points_xyz.has_normal_map()); + + std::ifstream stream_pwn{CGAL::data_file_path("points_3/cube.pwn")}; + Point_set points_pwn; + stream_pwn >> points_pwn; + assert(points_pwn.number_of_points() == 50000); + assert(points_pwn.has_normal_map()); + +} From 4998f7bc5747515de66459beb146ad3a87eb4c7c Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Jul 2023 13:48:36 +0200 Subject: [PATCH 112/520] Adapt remaining examples to use the generalized orthtree --- .../Orthtree/octree_build_from_point_set.cpp | 2 +- .../octree_build_with_custom_split.cpp | 3 +- .../Orthtree/octree_find_nearest_neighbor.cpp | 28 +++++++------- Orthtree/examples/Orthtree/octree_grade.cpp | 2 +- .../examples/Orthtree/octree_surface_mesh.cpp | 4 +- .../Orthtree/octree_traversal_custom.cpp | 37 ++++++++++++------- .../Orthtree/octree_traversal_manual.cpp | 29 ++++++++------- .../Orthtree/octree_traversal_preorder.cpp | 9 ++--- Orthtree/examples/Orthtree/orthtree_build.cpp | 7 ++-- .../quadtree_build_from_point_vector.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 10 ++++- .../include/CGAL/Orthtree_traits_point_d.h | 33 +++++++++++++++++ 12 files changed, 105 insertions(+), 61 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index 38bbd1e02d2c..e7f930fd9766 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -20,7 +20,7 @@ int main(int argc, char **argv) { Point_set points; // Load points from a file. - std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/cube.xyz")); + std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/cube.pwn")); stream >> points; if (0 == points.number_of_points()) { diff --git a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp index 9b67d61e2a72..1a84c914d394 100644 --- a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp +++ b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp @@ -27,7 +27,6 @@ struct Split_by_ratio { bool operator()(Node_index i, const Tree &tree) const { std::size_t num_points = tree.data(i).size(); std::size_t depth = tree.depth(i); - std::cout << num_points << " " << depth << std::endl; return num_points > (ratio * depth); } }; @@ -38,7 +37,7 @@ int main(int argc, char **argv) { Point_set points; // Load points from a file. - std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/kitten.off")); + std::ifstream stream((argc > 1) ? argv[1] : CGAL::data_file_path("points_3/cube.pwn")); stream >> points; if (0 == points.number_of_points()) { diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index dc6e5f415a37..5fafb06a8140 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -16,7 +16,7 @@ typedef Point_set::Point_map Point_map; typedef CGAL::Octree Octree; -int main(int argc, char **argv) { +int main(int argc, char** argv) { // Point set will be used to hold our points Point_set points; @@ -39,21 +39,19 @@ int main(int argc, char **argv) { // Find the nearest points to a few locations std::vector points_to_find = { - {0, 0, 0}, - {1, 1, 1}, - {-1, -1, -1}, - {-0.46026, -0.25353, 0.32051}, - {-0.460261, -0.253533, 0.320513} + {0, 0, 0}, + {1, 1, 1}, + {-1, -1, -1}, + {-0.46026, -0.25353, 0.32051}, + {-0.460261, -0.253533, 0.320513} }; - for (const Point& p : points_to_find) - octree.nearest_neighbors - (p, 1, // k=1 to find the single closest point - boost::make_function_output_iterator - ([&](const Point& nearest) - { - std::cout << "the nearest point to (" << p << - ") is (" << nearest << ")" << std::endl; - })); + for (const Point& p: points_to_find) + octree.nearest_neighbors( + p, 1, // k=1 to find the single closest point + boost::make_function_output_iterator([&](const Point_set::Index& nearest) { + std::cout << "the nearest point to (" << p << ") is (" << points.point(nearest) << ")" << std::endl; + }) + ); return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_grade.cpp b/Orthtree/examples/Orthtree/octree_grade.cpp index b00011045c68..f10dcfd1f40f 100644 --- a/Orthtree/examples/Orthtree/octree_grade.cpp +++ b/Orthtree/examples/Orthtree/octree_grade.cpp @@ -28,7 +28,7 @@ int main() { points.emplace_back(-1.0001, 1, 1); // Create an octree from the points - Octree octree(points); + Octree octree({points}); // Build the octree with a small bucket size, so we get a deep node octree.refine(10, 2); diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index 101437e6601d..aba87d2c6dfd 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -61,9 +61,7 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - OTraits otraits(mesh, mesh.points()); - - Octree tree(otraits); + Octree tree({mesh, mesh.points()}); OTraits::Split_predicate_node_min_extent sp(0.01); tree.refine(sp); diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index 3347b54ff996..4211b5bbb8ae 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -15,22 +16,29 @@ typedef Point_set::Point_map Point_map; typedef CGAL::Octree Octree; typedef Octree::Node Node; -struct First_branch_traversal -{ - Node first (Node root) const - { - return root; +template +struct First_branch_traversal { + + const Tree& m_orthtree; + + explicit First_branch_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + + typename Tree::Node_index first_index() const { + return m_orthtree.root(); } - Node next (Node n) const - { - if (n.is_leaf()) - return Node(); - return n[0]; + typename Tree::Maybe_node_index next_index(typename Tree::Node_index n) const { + + // Stop when we reach the base of the tree + if (m_orthtree.is_leaf(n)) + return {}; + + // Always descend the first child + return m_orthtree.child(n, 0); } }; -int main(int argc, char **argv) { +int main(int argc, char** argv) { // Point set will be used to hold our points Point_set points; @@ -46,14 +54,15 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Build the octree octree.refine(); // Print out the first branch using custom traversal - for (auto &node : octree.traverse()) - std::cout << node << std::endl; + for (auto node: octree.traverse_indices>()) { + std::cout << octree.to_string(node) << std::endl; + } return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp index dcbe8d0f15c0..94bdec8879bc 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp @@ -14,7 +14,7 @@ typedef Point_set::Point_map Point_map; typedef CGAL::Octree Octree; -int main(int argc, char **argv) { +int main(int argc, char** argv) { // Point set will be used to hold our points Point_set points; @@ -30,7 +30,7 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Build the octree using the default arguments octree.refine(); @@ -39,29 +39,30 @@ int main(int argc, char **argv) { std::cout << "Navigation relative to the root node" << std::endl; std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; std::cout << "the root node: " << std::endl; - std::cout << octree.root() << std::endl; + std::cout << octree.to_string(octree.root()) << std::endl; std::cout << "the first child of the root node: " << std::endl; - std::cout << octree.root()[0] << std::endl; + std::cout << octree.to_string(octree.child(octree.root(), 0)) << std::endl; std::cout << "the fifth child: " << std::endl; - std::cout << octree.root()[4] << std::endl; - std::cout << "the fifth child, accessed without the root keyword: " << std::endl; - std::cout << octree[4] << std::endl; + std::cout << octree.to_string(octree.child(octree.root(), 4)) << std::endl; + std::cout << "the fifth child, accessed without going through root: " << std::endl; + std::cout << octree.to_string(octree.node(4)) << std::endl; std::cout << "the second child of the fourth child: " << std::endl; - std::cout << octree.root()[4][1] << std::endl; - std::cout << "the second child of the fourth child, accessed without the root keyword: " << std::endl; - std::cout << octree[4][1] << std::endl; + std::cout << octree.to_string(octree.child(octree.child(octree.root(), 4), 1)) << std::endl; + std::cout << "the second child of the fourth child, accessed without going through root: " << std::endl; + std::cout << octree.to_string(octree.node(4, 1)) << std::endl; std::cout << std::endl; // Retrieve one of the deeper children - Octree::Node cur = octree[3][2]; + Octree::Node_index cur = octree.node(4, 3); std::cout << "Navigation relative to a child node" << std::endl; std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; std::cout << "the third child of the fourth child: " << std::endl; - std::cout << cur << std::endl; + std::cout << octree.to_string(cur) << std::endl; std::cout << "the third child: " << std::endl; - std::cout << cur.parent() << std::endl; + std::cout << octree.to_string(octree.parent(cur)) << std::endl; std::cout << "the next sibling of the third child of the fourth child: " << std::endl; - std::cout << cur.parent()[cur.local_coordinates().to_ulong() + 1] << std::endl; + std::cout << octree.to_string(octree.child(octree.parent(cur), octree.local_coordinates(cur).to_ulong() + 1)) + << std::endl; return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index 72587767a607..0ebc4ba03fc2 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -13,7 +13,7 @@ typedef CGAL::Point_set_3 Point_set; typedef Point_set::Point_map Point_map; typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; +typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; int main(int argc, char **argv) { @@ -31,15 +31,14 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); // Build the octree octree.refine(); // Print out the octree using preorder traversal - for (Octree::Node node : octree.traverse()) { - - std::cout << node << std::endl; + for (auto node : octree.traverse_indices()) { + std::cout << octree.to_string(node) << std::endl; } return EXIT_SUCCESS; diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 69d479e1a8bf..7d129cbde6fc 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -2,6 +2,7 @@ #include #include +#include #include // Type Declarations @@ -10,8 +11,8 @@ typedef CGAL::Epick_d Kernel; typedef Kernel::Point_d Point_d; typedef std::vector Point_vector; -typedef CGAL::Orthtree_traits_d Traits; -typedef CGAL::Orthtree Orthtree; +typedef CGAL::Orthtree_traits_point_d Traits; +typedef CGAL::Orthtree Orthtree; int main() { @@ -20,7 +21,7 @@ int main() Point_vector points_dd; for (std::size_t i = 0; i < 5; ++ i) { - std::array init; + std::array init{}; for (double& v : init) v = r.get_double(-1., 1.); points_dd.emplace_back (init.begin(), init.end()); diff --git a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp index 345652688572..db47c50b853d 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp @@ -19,7 +19,7 @@ int main() points_2d.emplace_back(r.get_double(-1., 1.), r.get_double(-1., 1.)); - Quadtree quadtree(points_2d); + Quadtree quadtree({points_2d}); quadtree.refine(10, 5); return EXIT_SUCCESS; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 31daf3a441cd..4098442a5820 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -401,7 +401,7 @@ class Orthtree { continue; // If it's already been split, skip it - if (!is_leaf(neighbor)) + if (!is_leaf(*neighbor)) continue; // Check if the neighbor breaks our grading rule @@ -487,7 +487,7 @@ class Orthtree { template Node_index_range traverse_indices(Args&& ...args) const { - return traverse_indices({*this, std::forward(args)...}); + return traverse_indices(Traversal{*this, std::forward(args)...}); } /*! @@ -1217,6 +1217,12 @@ class Orthtree { << box.xmax() << " " << box.ymax() << " " << box.zmax() << std::endl; } + std::string to_string(Node_index node) { + std::stringstream stream; + internal::print_orthtree_node(stream, node, *this); + return stream.str(); + } + friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { // Iterate over all nodes for (auto n: orthtree.traverse_indices(Orthtrees::Preorder_traversal(orthtree))) { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 243ff8fb29e0..8449852f3da6 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -190,6 +190,39 @@ struct Orthtree_traits_point_d { PointSet& m_point_set; PointMap m_point_map; + template + void reassign_points(Node_index n, Tree& tree, + const Point_d& center, Node_data points, + std::bitset coord = {}, + std::size_t dimension = 0) { + + // Root case: reached the last dimension + if (dimension == Dimension::value) { + tree.data(tree.child(n, coord.to_ulong())) = points; + return; + } + + // Split the point collection around the center point on this dimension + auto split_point = std::partition( + points.begin(), points.end(), + [&](const auto& p) -> bool { + // This should be done with cartesian iterator, + // but it seems complicated to do efficiently + return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); + } + ); + + // Further subdivide the first side of the split + std::bitset coord_left = coord; + coord_left[dimension] = false; + reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); + + // Further subdivide the second side of the split + std::bitset coord_right = coord; + coord_right[dimension] = true; + reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); + } + }; } From 5bc0962b814ed56cbf9887df98e0aedf000bb129 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Jul 2023 14:13:00 +0200 Subject: [PATCH 113/520] Eliminate non-index versions of split predicates & traversals --- Orthtree/include/CGAL/Orthtree.h | 43 ++++------- .../include/CGAL/Orthtree/Split_predicates.h | 26 ------- .../CGAL/Orthtree/Traversal_iterator.h | 75 +------------------ Orthtree/include/CGAL/Orthtree/Traversals.h | 72 +----------------- Orthtree/test/Orthtree/test_node_adjacent.cpp | 6 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 2 +- 6 files changed, 24 insertions(+), 200 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 4098442a5820..d70545a209cd 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -155,7 +155,6 @@ class Orthtree { typedef unspecified_type Node_range; typedef unspecified_type Node_index_range; #else - typedef boost::iterator_range> Node_range; typedef boost::iterator_range> Node_index_range; #endif @@ -439,33 +438,18 @@ class Orthtree { std::size_t depth() const { return m_side_per_depth.size() - 1; } /*! - \brief constructs a node range using a tree-traversal function. + \brief constructs a node index range using a tree-traversal function. - This method allows to iterate on the nodes of the tree with a + This method allows iteration over the nodes of the tree with a user-selected order (preorder, postorder, leaves-only, etc.). - todo: this should be removed, replaced with traverse_indices - \tparam Traversal model of `OrthtreeTraversal` that provides functions - compatible with the type of the orthree + compatible with the type of the orthtree \param traversal the instance of `Traversal` used - \return a forward input iterator over the nodes of the tree + \return a forward input iterator over the node indices of the tree */ - template - Node_range traverse(Traversal traversal) const { - - auto first = traversal.first_index(); - - auto next = [=](const Self& tree, Node_index index) -> Maybe_node_index { - return traversal.next_index(index); - }; - - return boost::make_iterator_range(Traversal_iterator(*this, first, next), - Traversal_iterator()); - } - template Node_index_range traverse_indices(Traversal traversal) const { @@ -479,12 +463,17 @@ class Orthtree { Index_traversal_iterator()); } - // todo: document this convenience function - template - Node_range traverse(Args&& ...args) const { - return traverse({*this, std::forward(args)...}); - } + /*! + \brief Convenience function for using a traversal without constructing it yourself + + \tparam Traversal model of `OrthtreeTraversal` that provides functions + compatible with the type of the orthtree + + \param args Arguments to to pass to the traversal's constructor, excluding the first (always an orthtree reference) + + \return a forward input iterator over the node indices of the tree + */ template Node_index_range traverse_indices(Args&& ...args) const { return traverse_indices(Traversal{*this, std::forward(args)...}); @@ -1168,8 +1157,8 @@ class Orthtree { /// \cond SKIP_IN_MANUAL void dump_to_polylines(std::ostream& os) const { - for (const Node& n: traverse()) - if (n.is_leaf()) { + for (const auto n: traverse_indices()) + if (is_leaf(n)) { Bbox box = bbox(n); dump_box_to_polylines(box, os); } diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index b1a38ee1c223..1e97bafd4205 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -38,14 +38,6 @@ class Maximum_number_of_inliers { Maximum_number_of_inliers(std::size_t bucket_size) : m_bucket_size(bucket_size) {} - /*! - \brief returns `true` if `n` should be split, `false` otherwise. - */ - template - bool operator()(const Node &n) const { - return (n.size() > m_bucket_size); - } - /*! \brief returns `true` if `i` should be split, `false` otherwise. */ @@ -73,14 +65,6 @@ class Maximum_depth { */ Maximum_depth(std::size_t max_depth) : m_max_depth(max_depth) {} - /*! - \brief returns `true` if `n` should be split, `false` otherwise. - */ - template - bool operator()(const Node &n) const { - return n.depth() < m_max_depth; - } - /*! \brief returns `true` if `i` should be split, `false` otherwise. */ @@ -117,16 +101,6 @@ class Maximum_depth_and_maximum_number_of_inliers { Maximum_depth_and_maximum_number_of_inliers(std::size_t max_depth, std::size_t bucket_size) : m_max_depth(max_depth), m_bucket_size(bucket_size) {} - /*! - \brief returns `true` if `n` should be split, `false` otherwise. - */ - template - bool operator()(const Node &n) const { - std::size_t num_points = n.size(); - std::size_t depth = n.depth(); - return (num_points > m_bucket_size && depth < m_max_depth); - } - /*! \brief returns `true` if `i` should be split, `false` otherwise. */ diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index 93027dfe5b47..c79965f3afc2 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -27,83 +27,12 @@ namespace CGAL { /*! * \ingroup PkgOrthtreeClasses * - * \brief + * \brief Wraps a traversal definition to produce an iterator which traverses the tree when incremented. * * \todo * - * \tparam Value + * \tparam Tree The orthtree type to iterate over */ -template -class Traversal_iterator - : public boost::iterator_facade, const typename Tree::Node, boost::forward_traversal_tag> { -public: - - /// \name Types - /// @{ - - /*! - * \brief - * - * \todo - */ - typedef std::function(const Tree&, std::size_t)> Traversal_function; - - typedef typename Tree::Node_index Node_index; - - /// @} - -public: - - /// \name Creation - /// @{ - - /*! - * \brief Default constructor, creates an end sentinel - * - * \todo - */ - Traversal_iterator() : m_next() {} - - /*! - * \brief - * - * \todo - * - * \param tree - * \param first - * \param next - */ - Traversal_iterator(const Tree& tree, Node_index first, const Traversal_function& next) : - m_tree(&tree), m_index(first), m_next(next) {} - - /// @} - -private: - - friend class boost::iterator_core_access; - - bool equal(Traversal_iterator const& other) const { - return m_index == other.m_index; - } - - void increment() { - // invoking increment on the sentinel is undefined behavior - m_index = m_next(*m_tree, m_index.get()); - } - - const typename Tree::Node& dereference() const { - return (*m_tree)[m_index.get()]; - } - -private: - - Traversal_function m_next; - - boost::optional m_index = {}; - const Tree* m_tree = nullptr; - -}; - template class Index_traversal_iterator : public boost::iterator_facade< Index_traversal_iterator, diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 595fa015d133..64d439e5df63 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -43,27 +43,16 @@ template struct Preorder_traversal { private: - using Node = typename Tree::Node; - const Tree& m_orthtree; public: Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - const Node* first() const { - return &m_orthtree[m_orthtree.root()]; - } - typename Tree::Node_index first_index() const { return m_orthtree.root(); } - const Node* next(const Node* n) const { - if (n == nullptr || !next_index(m_orthtree.index(*n))) return nullptr; - return &m_orthtree[*next_index(m_orthtree.index(*n))]; - } - std::optional next_index(typename Tree::Node_index n) const { if (m_orthtree.is_leaf(n)) { @@ -94,30 +83,14 @@ template struct Postorder_traversal { private: - using Node = typename Tree::Node; - const Tree& m_orthtree; public: Postorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - const Node* first() const { - return deepest_first_child(m_orthtree, m_orthtree.root()); - } - typename Tree::Node_index first_index() const { - return *m_orthtree.index(first()); - } - - const Node* next(const Node* n) const { - - auto next = deepest_first_child(m_orthtree, next_sibling(m_orthtree, n)); - - if (!next) - next = n->parent(); - - return next; + return m_orthtree.deepest_first_child(m_orthtree.root()); } std::optional next_index(typename Tree::Node_index n) const { @@ -137,30 +110,14 @@ template struct Leaves_traversal { private: - using Node = typename Tree::Node; - const Tree& m_orthtree; public: Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - const Node* first() const { - return m_orthtree.deepest_first_child(&m_orthtree.root()); - } - typename Tree::Node_index first_index() const { - return m_orthtree.deepest_first_child(0); - } - - const Node* next(const Node* n) const { - - auto next = m_orthtree.deepest_first_child(m_orthtree.next_sibling(n)); - - if (!next) - next = m_orthtree.deepest_first_child(m_orthtree.next_sibling_up(n)); - - return next; + return m_orthtree.deepest_first_child(m_orthtree.root()); } std::optional next_index(typename Tree::Node_index n) const { @@ -188,8 +145,6 @@ template struct Level_traversal { private: - using Node = typename Tree::Node; - const Tree& m_orthtree; const std::size_t m_depth; @@ -200,34 +155,11 @@ struct Level_traversal { */ Level_traversal(const Tree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} - template - const Node* first() const { - return m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); - } - typename Tree::Node_index first_index() const { // assumes the tree has at least one child at m_depth return *m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); } - template - const Node* next(const Node* n) const { - const Node* next = m_orthtree.next_sibling(n); - - if (!next) { - const Node* up = n; - do { - up = m_orthtree.next_sibling_up(up); - if (!up) - return nullptr; - - next = m_orthtree.first_child_at_depth(up, m_depth); - } while (!next); - } - - return next; - } - std::optional next_index(typename Tree::Node_index n) const { auto next = m_orthtree.next_sibling(n); diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 29f5b91dfdcd..84a28774fd11 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -53,11 +53,11 @@ int main(void) { auto left_top_back = octree.node(Traits::LEFT_TOP_BACK); assert(octree.node(Traits::RIGHT_TOP_BACK) == - octree.adjacent_node(left_top_back, Traits::RIGHT).get()); + *octree.adjacent_node(left_top_back, Traits::RIGHT)); assert(octree.node(Traits::LEFT_BOTTOM_BACK) == - octree.adjacent_node(left_top_back, Traits::DOWN).get()); + *octree.adjacent_node(left_top_back, Traits::DOWN)); assert(octree.node(Traits::LEFT_TOP_FRONT) == - octree.adjacent_node(left_top_back, Traits::FRONT)); + *octree.adjacent_node(left_top_back, Traits::FRONT)); assert(!octree.adjacent_node(left_top_back, Traits::LEFT)); assert(!octree.adjacent_node(left_top_back, Traits::UP)); assert(!octree.adjacent_node(left_top_back, Traits::BACK)); diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 9c0fed16e791..7bec427e037b 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -30,7 +30,7 @@ std::size_t count_jumps(Octree& octree) { if (!adjacent_node) continue; - if ((octree.depth(node) - octree.depth(adjacent_node.get())) > 1) + if ((octree.depth(node) - octree.depth(*adjacent_node)) > 1) jumps++; } } From c0135ea9ccd410c6350a11f76e4e4601d23417f2 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 15:08:48 -0600 Subject: [PATCH 114/520] reorganise examples and tests --- .../examples/Polygon_repair_2/CMakeLists.txt | 8 +- .../Polygon_repair_2/nasty_polygons.cpp | 82 +++++++++++++++++++ .../test/Polygon_repair_2/CMakeLists.txt | 6 +- .../Polygon_repair_2/draw_test_polygons.cpp} | 2 +- .../repair_polygon_2_test.cpp | 4 +- 5 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp rename Polygon_repair_2/{examples/Polygon_repair_2/repair_polygon_2.cpp => test/Polygon_repair_2/draw_test_polygons.cpp} (97%) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt index c4626d9d2556..73263ecace7c 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Examples) -find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) +find_package(CGAL REQUIRED) # create a target per cppfile file( @@ -13,8 +13,4 @@ file( ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") -endforeach() - -if(CGAL_Qt5_FOUND) - target_link_libraries(repair_polygon_2 PUBLIC CGAL::CGAL_Basic_viewer) -endif() \ No newline at end of file +endforeach() \ No newline at end of file diff --git a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp new file mode 100644 index 000000000000..d9fddbba8350 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; + +int main(int argc, char* argv[]) { + + // std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); + // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); + + std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); + std::ofstream ofs("/Users/ken/Downloads/2.geojson"); + + std::string in; + std::getline(ifs, in); + std::istringstream iss(in); + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + Polygon_repair_2 pr; + pr.add_to_triangulation(p); + pr.label_triangulation(); + + + + pr.compute_hole_nesting(); + pr.reconstruct_multipolygon(); + Multipolygon_with_holes_2 rmp = pr.multipolygon(); + std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + + ofs << std::fixed; + ofs << std::setprecision(15); + + ofs << "{" << std::endl; + ofs << "\t\"type\": \"MultiPolygon\"," << std::endl; + ofs << "\t\"coordinates\": [" << std::endl; + for (int i = 0; i < rmp.polygons().size(); ++i) { + ofs << "\t\t[" << std::endl; + + ofs << "\t\t\t[" << std::endl; + for (int j = 0; j < rmp.polygons()[i].outer_boundary().size(); ++j) { + ofs << "\t\t\t\t[" << rmp.polygons()[i].outer_boundary()[j].x() << + ", " << rmp.polygons()[i].outer_boundary()[j].y() << "]"; + if (j < rmp.polygons()[i].outer_boundary().size()-1) ofs << ","; + ofs << std::endl; + } ofs << "\t\t\t]"; + if (rmp.polygons()[i].number_of_holes() > 0) ofs << ","; + ofs << std::endl; + + for (int j = 0; j < rmp.polygons()[i].holes().size(); ++j) { + ofs << "\t\t\t[" << std::endl; + for (int k = 0; k < rmp.polygons()[i].holes()[j].size(); ++k) { + ofs << "\t\t\t\t[" << rmp.polygons()[i].holes()[j][k].x() << + ", " << rmp.polygons()[i].holes()[j][k].y() << "]"; + if (k < rmp.polygons()[i].holes()[j].size()-1) ofs << ","; + ofs << std::endl; + } + ofs << "\t\t\t]"; + if (j < rmp.polygons()[i].holes().size()-1) ofs << ","; + ofs << std::endl; + } + + ofs << "\t\t]"; + if (i < rmp.polygons().size()-1) ofs << ","; + ofs << std::endl; + } ofs << "\t]" << std::endl; + ofs << "}" << std::endl; + + return 0; +} diff --git a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt index d01986d94de1..365fba1a84c2 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Tests) -find_package(CGAL REQUIRED) +find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) # create a target per cppfile file( @@ -14,3 +14,7 @@ file( foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") endforeach() + +if(CGAL_Qt5_FOUND) + target_link_libraries(draw_test_polygons PUBLIC CGAL::CGAL_Basic_viewer) +endif() \ No newline at end of file diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp similarity index 97% rename from Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp rename to Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp index b7ea6b42b1df..4ae03ef51a31 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp @@ -19,7 +19,7 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - for (const auto& file: std::__fs::filesystem::directory_iterator("../../test/Polygon_repair_2/data/in")) { + for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { std::cout << "Reading " << file.path().filename() << "..." << std::endl; // if (file.path().filename() != "hole-outside.wkt") continue; diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index c011815ca57d..73bf756b311c 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -15,7 +15,7 @@ using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - + for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { std::cout << "Testing " << file.path().filename() << "... "; @@ -52,7 +52,7 @@ int main(int argc, char* argv[]) { } std::string ref; std::getline(ref_ifs, ref); ref += "\n"; - + // Compare output with reference file if (ref == out) { std::cout << "ok" << std::endl; From 9a28bcbdf8d2542fcc8f8ee484a2f27d376fcb23 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 15:09:05 -0600 Subject: [PATCH 115/520] resize for vectors --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index cc0535d37cd2..abf8ab1f29a9 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -234,15 +234,14 @@ class Polygon_repair_2 { } void compute_hole_nesting() { - for (int i = 0; i < number_of_holes; ++i) { - nesting.emplace_back(); - } for (auto const &face: t.finite_face_handles()) { + nesting.resize(number_of_holes); + for (auto const &face: t.finite_face_handles()) { if (face->label() >= -1) continue; // skip non-hole triangles for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { if (face->label() == face->neighbor(opposite_vertex)->label()) continue; nesting[-face->label()-2].insert(face->neighbor(opposite_vertex)->label()); } - } + } // int hole_label = -2; // for (auto const &hole: nesting) { @@ -258,10 +257,8 @@ class Polygon_repair_2 { mp.clear(); std::vector> polygons; std::vector, Polygon_less>> holes; // holes are ordered - for (int i = 0; i < number_of_polygons; ++i) { - polygons.emplace_back(); - holes.emplace_back(); - } + polygons.resize(number_of_polygons); + holes.resize(number_of_polygons); for (auto const face: t.all_face_handles()) { face->processed() = false; From 6286ef8f5aafa2c6eca794651593fea185a5817b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 15:13:28 -0600 Subject: [PATCH 116/520] direct comparison to detect polygon/multipolygon --- .../test/Polygon_repair_2/repair_polygon_2_test.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index 73bf756b311c..6e33bd296713 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -25,14 +25,12 @@ int main(int argc, char* argv[]) { // Load test file and repair to create output std::istringstream iss(in); - std::size_t is_polygon = in.find("POLYGON"); - std::size_t is_multipolygon = in.find("MULTIPOLYGON"); Multipolygon_with_holes_2 rmp; - if (is_polygon == 0) { + if (in.find("POLYGON") == 0) { Polygon_with_holes_2 p; CGAL::IO::read_polygon_WKT(iss, p); rmp = CGAL::Polygon_repair_2::repair(p); - } else if (is_multipolygon == 0) { + } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); rmp = CGAL::Polygon_repair_2::repair(mp); From 0ff6dcc66801f356375620c955e39dbfaac4704a Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 15:19:14 -0600 Subject: [PATCH 117/520] comments in American English + fix typo --- .../include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h | 2 +- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index ba0e883773ba..a584d088cab8 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -23,7 +23,7 @@ namespace CGAL { * The class `Multipolygon_with_holes_2` models the concept `MultipolygonWithHoles_2`. * It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate * the types `Polygon_2` and `Polygon_with_holes_2`. - * The latter is used to represents each polygon with holes. The former is converted to the latter. + * The latter is used to represent each polygon with holes. The former is converted to the latter. * * \cgalModels `MultipolygonWithHoles_2` */ diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index abf8ab1f29a9..6769a39bf1ce 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -252,7 +252,7 @@ class Polygon_repair_2 { // } } - // Reconstruct multipolygon based on the triangles labelled as inside the polygon + // Reconstruct multipolygon based on the triangles labeled as inside the polygon void reconstruct_multipolygon() { mp.clear(); std::vector> polygons; From db0cb2af4f6fb2e37cadf712e07e5c357cd2507b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 15:24:06 -0600 Subject: [PATCH 118/520] spread code a bit after } --- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 12 ++++++++---- .../draw_multipolygon_with_holes_2.h | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 6769a39bf1ce..69bbac932ad2 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -93,7 +93,8 @@ class Polygon_repair_2 { if (*va != *vb) return *va < *vb; ++va; ++vb; - } if (vb == pb.vertices_end()) return false; + } + if (vb == pb.vertices_end()) return false; return true; } }; @@ -109,7 +110,8 @@ class Polygon_repair_2 { while (ha != pa.holes_end() && hb != pb.holes_end()) { if (pl(*ha, *hb)) return true; if (pl(*hb, *ha)) return false; - } if (hb == pb.holes_end()) return false; + } + if (hb == pb.holes_end()) return false; return true; } }; @@ -228,7 +230,8 @@ class Polygon_repair_2 { for (typename std::list::iterator current_vertex = ring.begin(); current_vertex != ring.end(); ++current_vertex) { if (*current_vertex < *smallest_vertex) smallest_vertex = current_vertex; - } if (ring.front() != *smallest_vertex) { + } + if (ring.front() != *smallest_vertex) { ring.splice(ring.begin(), ring, smallest_vertex, ring.end()); } } @@ -303,7 +306,8 @@ class Polygon_repair_2 { std::set, Polygon_with_holes_less> ordered_polygons; for (int i = 0; i < polygons.size(); ++i) { ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); - } for (auto const& polygon: ordered_polygons) { + } + for (auto const& polygon: ordered_polygons) { // std::cout << "Adding polygon " << polygon << std::endl; mp.add_polygon(polygon); } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h index 2035c225c48d..3a28356873bb 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h @@ -97,7 +97,8 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { Bbox_2 bbox; for (auto const& pwh: m_mpwh.polygons()) { bbox += pwh.outer_boundary().bbox(); - } return bbox; + } + return bbox; } /*! Compute the elements of a multipolygon with holes. From 53ae5f8abd5962e36f9493f60d69e7a7d1ebf4a8 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 19:07:41 -0600 Subject: [PATCH 119/520] bugfixes, empty output works now --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 35 ++++++++++--------- .../draw_multipolygon_with_holes_2.h | 2 +- .../Polygon_repair_2/draw_test_polygons.cpp | 1 + .../repair_polygon_2_test.cpp | 1 + 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 69bbac932ad2..5ad3d31b15dc 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -37,10 +37,11 @@ template Multipolygon_with_holes_2 repair(const Polygon_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(p); - pr.label_triangulation(); - pr.compute_hole_nesting(); - pr.reconstruct_multipolygon(); - return pr.multipolygon(); + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation(); + pr.compute_hole_nesting(); + pr.reconstruct_multipolygon(); + } return pr.multipolygon(); } /// \ingroup PkgPolygonRepair2Functions @@ -49,10 +50,11 @@ template Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(p); - pr.label_triangulation(); - pr.compute_hole_nesting(); - pr.reconstruct_multipolygon(); - return pr.multipolygon(); + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation(); + pr.compute_hole_nesting(); + pr.reconstruct_multipolygon(); + } return pr.multipolygon(); } /// \ingroup PkgPolygonRepair2Functions @@ -61,10 +63,11 @@ template Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; pr.add_to_triangulation(mp); - pr.label_triangulation(); - pr.compute_hole_nesting(); - pr.reconstruct_multipolygon(); - return pr.multipolygon(); + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation(); + pr.compute_hole_nesting(); + pr.reconstruct_multipolygon(); + } return pr.multipolygon(); } /*! \ingroup PkgPolygonRepair2Ref @@ -93,7 +96,7 @@ class Polygon_repair_2 { if (*va != *vb) return *va < *vb; ++va; ++vb; - } + } if (vb == pb.vertices_end()) return false; return true; } @@ -110,7 +113,7 @@ class Polygon_repair_2 { while (ha != pa.holes_end() && hb != pb.holes_end()) { if (pl(*ha, *hb)) return true; if (pl(*hb, *ha)) return false; - } + } if (hb == pb.holes_end()) return false; return true; } @@ -230,7 +233,7 @@ class Polygon_repair_2 { for (typename std::list::iterator current_vertex = ring.begin(); current_vertex != ring.end(); ++current_vertex) { if (*current_vertex < *smallest_vertex) smallest_vertex = current_vertex; - } + } if (ring.front() != *smallest_vertex) { ring.splice(ring.begin(), ring, smallest_vertex, ring.end()); } @@ -306,7 +309,7 @@ class Polygon_repair_2 { std::set, Polygon_with_holes_less> ordered_polygons; for (int i = 0; i < polygons.size(); ++i) { ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); - } + } for (auto const& polygon: ordered_polygons) { // std::cout << "Adding polygon " << polygon << std::endl; mp.add_polygon(polygon); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h index 3a28356873bb..d8c9d47736ad 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h @@ -97,7 +97,7 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { Bbox_2 bbox; for (auto const& pwh: m_mpwh.polygons()) { bbox += pwh.outer_boundary().bbox(); - } + } return bbox; } diff --git a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp index 4ae03ef51a31..e75a0ecdb646 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp @@ -20,6 +20,7 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { + if (file.path().filename().extension() != ".wkt") continue; std::cout << "Reading " << file.path().filename() << "..." << std::endl; // if (file.path().filename() != "hole-outside.wkt") continue; diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index 6e33bd296713..545c19943daa 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -17,6 +17,7 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { + if (file.path().filename().extension() != ".wkt") continue; std::cout << "Testing " << file.path().filename() << "... "; // Read test file From 61fe1f43ac24721e47a9e7779f1341a527a553f7 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 19:08:37 -0600 Subject: [PATCH 120/520] more unit tests --- .../test/Polygon_repair_2/data/in/back-and-forth.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt | 1 + .../test/Polygon_repair_2/data/in/hole-touching-edge.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt | 1 + .../test/Polygon_repair_2/data/ref/back-and-forth.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt | 1 + .../test/Polygon_repair_2/data/ref/hole-touching-edge.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt | 1 + 15 files changed, 15 insertions(+) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/back-and-forth.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/back-and-forth.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-edge.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/back-and-forth.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/back-and-forth.wkt new file mode 100644 index 000000000000..3911b8f5203a --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/back-and-forth.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,0 0,1 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt new file mode 100644 index 000000000000..66839d345315 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt new file mode 100644 index 000000000000..34482d54ef09 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-edge.wkt new file mode 100644 index 000000000000..4953794c84a0 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0.5 1,0.25 0.75,0.25 0.25)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt new file mode 100644 index 000000000000..142ea6dc746f --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt new file mode 100644 index 000000000000..ce5c6b3dbf05 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt new file mode 100644 index 000000000000..ddecaff4c21b --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt @@ -0,0 +1 @@ +POLYGON((0 0,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt new file mode 100644 index 000000000000..1c9d31654c2e --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt @@ -0,0 +1 @@ +POLYGON((0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/back-and-forth.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/back-and-forth.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/back-and-forth.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-edge.wkt new file mode 100644 index 000000000000..4d3bb2343970 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-edge.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0.5 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.5 1,0.75 0.75,0.75 0.25,0.25 0.25))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file From 4c3cbc864e08ed87b3f179b7238dd3a894c1b54e Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 26 Jul 2023 20:26:26 -0600 Subject: [PATCH 121/520] unit tests with empty input (breaks wkt reader) --- .../test/Polygon_repair_2/data/in/empty-multipolygon.wkt | 1 + .../test/Polygon_repair_2/data/in/empty-polygon.wkt | 1 + .../test/Polygon_repair_2/data/in/hole-touching-once.wkt | 2 +- .../test/Polygon_repair_2/data/ref/empty-multipolygon.wkt | 1 + .../test/Polygon_repair_2/data/ref/empty-polygon.wkt | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/empty-multipolygon.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/empty-polygon.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-multipolygon.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-polygon.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-multipolygon.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-multipolygon.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-multipolygon.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-polygon.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-polygon.wkt new file mode 100644 index 000000000000..aec93ace4b9b --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-polygon.wkt @@ -0,0 +1 @@ +POLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt index 587930ced4c6..e9e070b92244 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt @@ -1 +1 @@ -POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.75 0.25,0.75 0.75,0 1,0.25 0.25)) \ No newline at end of file +POLYGON((0 0,1 0,1 1,0 1,0 0),(0 1,0.75 0.75,0.75 0.25,0.25 0.25,0 1)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-multipolygon.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-multipolygon.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-multipolygon.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-polygon.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-polygon.wkt new file mode 100644 index 000000000000..21e6672095b6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-polygon.wkt @@ -0,0 +1 @@ +MULTIPOLYGON() \ No newline at end of file From 8fe57f5adf29cbda5f7a7953df6a05efc9379c85 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 27 Jul 2023 09:49:20 +0200 Subject: [PATCH 122/520] Rename traverse_indices to traverse (now that it's the only traversal function) --- Orthtree/examples/Orthtree/octree_surface_mesh.cpp | 2 +- .../examples/Orthtree/octree_traversal_custom.cpp | 2 +- .../Orthtree/octree_traversal_preorder.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 14 +++++++------- .../Orthtree/test_octree_custom_properties.cpp | 2 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 2 +- Orthtree/test/Orthtree/test_octree_traverse.cpp | 8 ++++---- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index aba87d2c6dfd..c2acd05791e6 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -16,7 +16,7 @@ void dump_as_polylines(const Octree& ot) { // SL: I cheated for this part and looked at the implementation std::ofstream out("octree.polylines.txt"); - for (Octree::Node_index node : ot.traverse_indices(CGAL::Orthtrees::Leaves_traversal(ot))) + for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) { if (!ot.is_leaf(node)) continue; diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index 4211b5bbb8ae..dd6de668c8b0 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -60,7 +60,7 @@ int main(int argc, char** argv) { octree.refine(); // Print out the first branch using custom traversal - for (auto node: octree.traverse_indices>()) { + for (auto node: octree.traverse>()) { std::cout << octree.to_string(node) << std::endl; } diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index 0ebc4ba03fc2..fbda2f8db8ff 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -37,7 +37,7 @@ int main(int argc, char **argv) { octree.refine(); // Print out the octree using preorder traversal - for (auto node : octree.traverse_indices()) { + for (auto node : octree.traverse()) { std::cout << octree.to_string(node) << std::endl; } diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d70545a209cd..acc045bc0e7f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -369,7 +369,7 @@ class Orthtree { // Collect all the leaf nodes std::queue leaf_nodes; - for (Node_index leaf: traverse_indices(Orthtrees::Leaves_traversal(*this))) { + for (Node_index leaf: traverse(Orthtrees::Leaves_traversal(*this))) { leaf_nodes.push(leaf); } @@ -451,7 +451,7 @@ class Orthtree { \return a forward input iterator over the node indices of the tree */ template - Node_index_range traverse_indices(Traversal traversal) const { + Node_index_range traverse(Traversal traversal) const { Node_index first = traversal.first_index(); @@ -465,7 +465,7 @@ class Orthtree { /*! - \brief Convenience function for using a traversal without constructing it yourself + \brief Convenience method for using a traversal without constructing it yourself \tparam Traversal model of `OrthtreeTraversal` that provides functions compatible with the type of the orthtree @@ -475,8 +475,8 @@ class Orthtree { \return a forward input iterator over the node indices of the tree */ template - Node_index_range traverse_indices(Args&& ...args) const { - return traverse_indices(Traversal{*this, std::forward(args)...}); + Node_index_range traverse(Args&& ...args) const { + return traverse(Traversal{*this, std::forward(args)...}); } /*! @@ -1157,7 +1157,7 @@ class Orthtree { /// \cond SKIP_IN_MANUAL void dump_to_polylines(std::ostream& os) const { - for (const auto n: traverse_indices()) + for (const auto n: traverse()) if (is_leaf(n)) { Bbox box = bbox(n); dump_box_to_polylines(box, os); @@ -1214,7 +1214,7 @@ class Orthtree { friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { // Iterate over all nodes - for (auto n: orthtree.traverse_indices(Orthtrees::Preorder_traversal(orthtree))) { + for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { // Show the depth for (int i = 0; i < orthtree.depth(n); ++i) os << ". "; diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index cb1139a7c042..8398175dbfd0 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -35,7 +35,7 @@ int main(void) { // Expanding the tree; new nodes should be assigned the default value tree.refine(10, 1); - for (auto n : tree.traverse_indices>()) { + for (auto n : tree.traverse>()) { // Everything but the root will have the default value if (!tree.is_root(n)) assert(node_int_property[n] == 5); } diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 7bec427e037b..ef39e2f89b9d 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -21,7 +21,7 @@ std::size_t count_jumps(Octree& octree) { std::size_t jumps = 0; - for (auto node: octree.traverse_indices()) { + for (auto node: octree.traverse()) { for (int direction = 0; direction < 6; ++direction) { diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 0d442051faf5..b0e2823ee781 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -26,7 +26,7 @@ bool test_preorder_1_node() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse_indices(); + auto nodes = octree.traverse(); // Check each item in the range auto iter = nodes.begin(); @@ -47,7 +47,7 @@ bool test_preorder_9_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse_indices(); + auto nodes = octree.traverse(); // Check each item in the range auto iter = nodes.begin(); @@ -72,7 +72,7 @@ bool test_level_9_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse_indices(static_cast(1)); + auto nodes = octree.traverse(static_cast(1)); // Check each item in the range auto iter = nodes.begin(); @@ -98,7 +98,7 @@ bool test_preorder_25_nodes() { octree.refine(10, 1); // Create the range - auto nodes = octree.traverse_indices(); + auto nodes = octree.traverse(); // Check each item in the range auto iter = nodes.begin(); From 6f31cbddd60526f61656fcd3511c53861c790ce4 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 27 Jul 2023 10:13:49 +0200 Subject: [PATCH 123/520] Point traits now share the generic reassign_points method --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 2 + .../include/CGAL/Orthtree_traits_point_2.h | 37 +--------- .../include/CGAL/Orthtree_traits_point_3.h | 37 +--------- .../include/CGAL/Orthtree_traits_point_d.h | 71 ++++++++++--------- 4 files changed, 45 insertions(+), 102 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index b32701ce072e..2bf07fd4e57e 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -99,6 +99,8 @@ class OrthtreeTraits * \brief Initializes the contained elements for the root node. * * Typically produces a `Node_data` which contains all the elements in the tree. + * e.g. For a tree where each node contains a set of points, + * root_node_contents() will produce the list of all points. * * @return The `Node_data` instance to be contained by the root node */ diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index 3788667d8faa..057472820e0c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -19,6 +19,8 @@ #include #include +#include + namespace CGAL { /*! @@ -185,7 +187,7 @@ struct Orthtree_traits_point_2 { template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); - reassign_points(n, tree, center, tree.data(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); } Point_d get_element(const Node_data_element& index) const { @@ -199,39 +201,6 @@ struct Orthtree_traits_point_2 { PointSet& m_point_set; PointMap m_point_map; - template - void reassign_points(Node_index n, Tree& tree, - const Point_d& center, Node_data points, - std::bitset coord = {}, - std::size_t dimension = 0) { - - // Root case: reached the last dimension - if (dimension == Dimension::value) { - tree.data(tree.child(n, coord.to_ulong())) = points; - return; - } - - // Split the point collection around the center point on this dimension - auto split_point = std::partition( - points.begin(), points.end(), - [&](const auto& p) -> bool { - // This should be done with cartesian iterator, - // but it seems complicated to do efficiently - return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); - } - ); - - // Further subdivide the first side of the split - std::bitset coord_left = coord; - coord_left[dimension] = false; - reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); - - // Further subdivide the second side of the split - std::bitset coord_right = coord; - coord_right[dimension] = true; - reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); - } - }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index 0698b93728b4..c14f56346421 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -19,6 +19,8 @@ #include #include +#include + namespace CGAL { /*! @@ -202,7 +204,7 @@ struct Orthtree_traits_point_3 { template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); - reassign_points(n, tree, center, tree.data(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); } Point_d get_element(const Node_data_element& index) const { @@ -216,39 +218,6 @@ struct Orthtree_traits_point_3 { PointSet& m_point_set; PointMap m_point_map; - template - void reassign_points(Node_index n, Tree& tree, - const Point_d& center, Node_data points, - std::bitset coord = {}, - std::size_t dimension = 0) { - - // Root case: reached the last dimension - if (dimension == Dimension::value) { - tree.data(tree.child(n, coord.to_ulong())) = points; - return; - } - - // Split the point collection around the center point on this dimension - auto split_point = std::partition( - points.begin(), points.end(), - [&](const auto& p) -> bool { - // This should be done with cartesian iterator, - // but it seems complicated to do efficiently - return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); - } - ); - - // Further subdivide the first side of the split - std::bitset coord_left = coord; - coord_left[dimension] = false; - reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); - - // Further subdivide the second side of the split - std::bitset coord_right = coord; - coord_right[dimension] = true; - reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); - } - }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 8449852f3da6..406ef377e371 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -21,6 +21,42 @@ namespace CGAL { +// todo: should this go in its own header & namespace? +template +void reassign_points( + Tree& tree, PointMap& point_map, + typename Tree::Node_index n, const typename Tree::Point& center, typename Tree::Node_data points, + std::bitset coord = {}, std::size_t dimension = 0 +) { + + // Root case: reached the last dimension + if (dimension == Tree::Dimension::value) { + tree.data(tree.child(n, coord.to_ulong())) = points; + return; + } + + // Split the point collection around the center point on this dimension + auto split_point = std::partition( + points.begin(), points.end(), + [&](const auto& p) -> bool { + // This should be done with cartesian iterator, + // but it seems complicated to do efficiently + return (get(point_map, p)[int(dimension)] < center[int(dimension)]); + } + ); + + // Further subdivide the first side of the split + std::bitset coord_left = coord; + coord_left[dimension] = false; + reassign_points(tree, point_map, n, center, {points.begin(), split_point}, coord_left, dimension + 1); + + // Further subdivide the second side of the split + std::bitset coord_right = coord; + coord_right[dimension] = true; + reassign_points(tree, point_map, n, center, {split_point, points.end()}, coord_right, dimension + 1); +} + + /*! \ingroup PkgOrthtreeTraits @@ -176,7 +212,7 @@ struct Orthtree_traits_point_d { template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); - reassign_points(n, tree, center, tree.data(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); } Point_d get_element(const Node_data_element& index) const { @@ -190,39 +226,6 @@ struct Orthtree_traits_point_d { PointSet& m_point_set; PointMap m_point_map; - template - void reassign_points(Node_index n, Tree& tree, - const Point_d& center, Node_data points, - std::bitset coord = {}, - std::size_t dimension = 0) { - - // Root case: reached the last dimension - if (dimension == Dimension::value) { - tree.data(tree.child(n, coord.to_ulong())) = points; - return; - } - - // Split the point collection around the center point on this dimension - auto split_point = std::partition( - points.begin(), points.end(), - [&](const auto& p) -> bool { - // This should be done with cartesian iterator, - // but it seems complicated to do efficiently - return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); - } - ); - - // Further subdivide the first side of the split - std::bitset coord_left = coord; - coord_left[dimension] = false; - reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); - - // Further subdivide the second side of the split - std::bitset coord_right = coord; - coord_right[dimension] = true; - reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); - } - }; } From 282180d627ef0d2d9ebd80af3f8db8897692111a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 27 Jul 2023 12:15:51 +0200 Subject: [PATCH 124/520] Add an example which builds a tree that doesn't contain anything --- Orthtree/examples/Orthtree/CMakeLists.txt | 1 + .../Orthtree/quadtree_build_manually.cpp | 86 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 Orthtree/examples/Orthtree/quadtree_build_manually.cpp diff --git a/Orthtree/examples/Orthtree/CMakeLists.txt b/Orthtree/examples/Orthtree/CMakeLists.txt index 9e5dc642c418..ee2c7502a8da 100644 --- a/Orthtree/examples/Orthtree/CMakeLists.txt +++ b/Orthtree/examples/Orthtree/CMakeLists.txt @@ -16,6 +16,7 @@ create_single_source_cgal_program("octree_traversal_preorder.cpp") create_single_source_cgal_program("octree_grade.cpp") create_single_source_cgal_program("quadtree_build_from_point_vector.cpp") create_single_source_cgal_program("octree_surface_mesh.cpp") +create_single_source_cgal_program("quadtree_build_manually.cpp") find_package(Eigen3 3.1.91 QUIET) #(requires 3.1.91 or greater) include(CGAL_Eigen_support) diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp new file mode 100644 index 000000000000..37bc9e010cce --- /dev/null +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include + +#include + +using Kernel = CGAL::Simple_cartesian; + +namespace CGAL { + +struct empty_type{}; + +template +struct Orthtree_traits_empty_2 { + + using Self = Orthtree_traits_empty_2; + using Tree = Orthtree; + + // todo: All of this could go in an Orthtree_traits_2_base class + using Dimension = Dimension_tag<2>; + using Bbox_d = Bbox_2; + using FT = typename K::FT; + using Point_d = typename K::Point_2; + using Sphere_d = typename K::Circle_2; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; + using Array = std::array; + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP + }; + + using Node_data = std::array; + using Node_data_element = empty_type; + + Orthtree_traits_empty_2(Bbox_d bbox) : m_bbox(bbox) {}; + + decltype(auto) construct_point_d_from_array_object() const { + return [](const Array& array) -> Point_d { return {array[0], array[1]}; }; + } + using Construct_point_d_from_array = std::invoke_result_t; + + decltype(auto) construct_bbox_d_object() const { + return [](const Array& min, const Array& max) -> Bbox_d { + return {min[0], min[1], max[0], max[1]}; + }; + } + using Construct_bbox_d = std::invoke_result_t; + + std::pair root_node_bbox() const { + return {{m_bbox.xmax(), m_bbox.ymax()}, + {m_bbox.xmax(), m_bbox.ymax()}}; + } + + Node_data root_node_contents() const { return {}; } + + template // todo: this shouldn't be necessary, but I think there's a dependency loop somehow + void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) {} + + empty_type get_element(const Node_data_element& index) const { return {}; } + +private: + + Bbox_d m_bbox; + +}; +} + +using EmptyQuadtree = CGAL::Orthtree>; + +int main() { + + // Build an empty quadtree which covers the domain (-1, -1) to (1, 1) + EmptyQuadtree quadtree{{{-1, -1, 1, 1}}}; + + // Split several nodes of the tree + quadtree.split(quadtree.root()); + quadtree.split(quadtree.node(0)); + quadtree.split(quadtree.node(3)); + quadtree.split(quadtree.node(3, 0)); + + std::cout << quadtree << std::endl; + return EXIT_SUCCESS; +} From b950c6c49492f49f08ecf28063a62e87b4c6fe97 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 27 Jul 2023 13:52:10 +0200 Subject: [PATCH 125/520] Add base traits classes with common typedefs for convenience --- .../Orthtree/quadtree_build_manually.cpp | 30 ++--- .../include/CGAL/Orthtree_traits_2_base.h | 95 +++++++++++++++ .../include/CGAL/Orthtree_traits_3_base.h | 112 ++++++++++++++++++ .../include/CGAL/Orthtree_traits_d_base.h | 85 +++++++++++++ .../include/CGAL/Orthtree_traits_point_2.h | 76 +++--------- .../include/CGAL/Orthtree_traits_point_3.h | 94 +++------------ .../include/CGAL/Orthtree_traits_point_d.h | 63 +++------- 7 files changed, 346 insertions(+), 209 deletions(-) create mode 100644 Orthtree/include/CGAL/Orthtree_traits_2_base.h create mode 100644 Orthtree/include/CGAL/Orthtree_traits_3_base.h create mode 100644 Orthtree/include/CGAL/Orthtree_traits_d_base.h diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 37bc9e010cce..0dbb9feef4c5 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -4,6 +4,7 @@ #include #include +#include using Kernel = CGAL::Simple_cartesian; @@ -12,44 +13,29 @@ namespace CGAL { struct empty_type{}; template -struct Orthtree_traits_empty_2 { +struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { using Self = Orthtree_traits_empty_2; using Tree = Orthtree; - // todo: All of this could go in an Orthtree_traits_2_base class - using Dimension = Dimension_tag<2>; - using Bbox_d = Bbox_2; - using FT = typename K::FT; - using Point_d = typename K::Point_2; - using Sphere_d = typename K::Circle_2; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; - using Array = std::array; - enum Adjacency { - LEFT, - RIGHT, - DOWN, - UP - }; - using Node_data = std::array; using Node_data_element = empty_type; - Orthtree_traits_empty_2(Bbox_d bbox) : m_bbox(bbox) {}; + Orthtree_traits_empty_2(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; decltype(auto) construct_point_d_from_array_object() const { - return [](const Array& array) -> Point_d { return {array[0], array[1]}; }; + return [](const typename Self::Array& array) -> typename Self::Point_d { return {array[0], array[1]}; }; } using Construct_point_d_from_array = std::invoke_result_t; decltype(auto) construct_bbox_d_object() const { - return [](const Array& min, const Array& max) -> Bbox_d { + return [](const typename Self::Array& min, const typename Self::Array& max) -> typename Self::Bbox_d { return {min[0], min[1], max[0], max[1]}; }; } using Construct_bbox_d = std::invoke_result_t; - std::pair root_node_bbox() const { + std::pair root_node_bbox() const { return {{m_bbox.xmax(), m_bbox.ymax()}, {m_bbox.xmax(), m_bbox.ymax()}}; } @@ -57,13 +43,13 @@ struct Orthtree_traits_empty_2 { Node_data root_node_contents() const { return {}; } template // todo: this shouldn't be necessary, but I think there's a dependency loop somehow - void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) {} + void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) {} empty_type get_element(const Node_data_element& index) const { return {}; } private: - Bbox_d m_bbox; + typename Self::Bbox_d m_bbox; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h new file mode 100644 index 000000000000..84cfc33b59ad --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_2_base.h @@ -0,0 +1,95 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H +#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H + +#include + +#include +#include +#include +#include + +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_2_base` can be subclassed for easier implementation of a 2d OrthtreeTraits concept. + + \tparam GeomTraits model of `Kernel`. + + \cgalModels `OrthtreeTraits` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_point_2` +*/ +template +struct Orthtree_traits_2_base { +public: + + /// \name Types + /// @{ + + using Dimension = Dimension_tag<2>; + using Bbox_d = Bbox_2; + using FT = typename K::FT; + using Point_d = typename K::Point_2; + using Sphere_d = typename K::Circle_2; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; + using Array = std::array; // todo: This should have a more descriptive name + + /*! + * \brief Two directions along each axis in Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 2-bit integers. + * + * The first bit indicates the axis (0 = x, 1 = y), + * the second bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram may be a useful reference: + * + * 3 * + * | + * | y+ + * | * + * 0 *------+------* 1 | + * | | + * | +-----* x+ + * | + * * 2 + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 00 | 0 | LEFT | + * | `+x` | 01 | 1 | RIGHT | + * | `-y` | 10 | 2 | DOWN | + * | `+y` | 11 | 3 | UP | + */ + enum Adjacency + { + LEFT, + RIGHT, + DOWN, + UP + }; + + /// @} + +}; + +} + +#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h new file mode 100644 index 000000000000..1702da407745 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -0,0 +1,112 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_3_BASE_H +#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_3_BASE_H + +#include + +#include +#include +#include +#include + +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_3_base` can be subclassed for easier implementation of a 3d OrthtreeTraits concept. + + \tparam GeomTraits model of `Kernel`. + + \cgalModels `OrthtreeTraits` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_point_3` +*/ +template +struct Orthtree_traits_3_base { +public: + + /// \name Types + /// @{ + + using Dimension = Dimension_tag<3>; + using Bbox_d = Bbox_3; + using FT = typename K::FT; + using Point_d = typename K::Point_3; + using Sphere_d = typename K::Circle_3; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; + using Array = std::array; // todo: This should have a more descriptive name + + /*! + * \brief Two directions along each axis in Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 3-bit integers, + * though the numbers 6 and 7 are not used because there are only 6 different directions. + * + * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), + * the third bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram may be a useful reference: + * + * 3 * + * | * 5 + * | / y+ + * |/ * z+ + * 0 *------+------* 1 | * + * /| |/ + * / | +-----* x+ + * 4 * | + * * 2 + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 000 | 0 | LEFT | + * | `+x` | 001 | 1 | RIGHT | + * | `-y` | 010 | 2 | DOWN | + * | `+y` | 011 | 3 | UP | + * | `-z` | 100 | 4 | BACK | + * | `+z` | 101 | 5 | FRONT | + */ + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP, + BACK, + FRONT + }; + + /// \cond SKIP_IN_MANUAL + enum Child { + LEFT_BOTTOM_BACK, + RIGHT_BOTTOM_BACK, + LEFT_TOP_BACK, + RIGHT_TOP_BACK, + LEFT_BOTTOM_FRONT, + RIGHT_BOTTOM_FRONT, + LEFT_TOP_FRONT, + RIGHT_TOP_FRONT + }; + /// \endcond + + /// @} + +}; + +} + +#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h new file mode 100644 index 000000000000..82b9268d9009 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_d_base.h @@ -0,0 +1,85 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_D_BASE_H +#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_D_BASE_H + +#include + +#include +#include +#include +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_d_base` can be subclassed for easier implementation of a dd OrthtreeTraits concept. + + \tparam GeomTraits model of `Kernel`. + + \cgalModels `OrthtreeTraits` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_point_d` +*/ +template +struct Orthtree_traits_d_base { +public: + + /// \name Types + /// @{ + + using Dimension = DimensionTag; + using FT = typename K::FT; + using Point_d = typename K::Point_d; + using Sphere_d = typename K::Sphere_d; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; + using Array = std::array; + +#ifdef DOXYGEN_RUNNING + typedef unspecified_type Bbox_d; ///< Bounding box type. +#else + + class Bbox_d { + Point_d m_min, m_max; + public: + + Bbox_d(const Point_d& pmin, const Point_d& pmax) + : m_min(pmin), m_max(pmax) {} + + const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_min; } + + const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_max; } + }; + +#endif + + /*! + Adjacency type. + + \note This type is used to identify adjacency directions with + easily understandable keywords (left, right, up, etc.) and is thus + mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In + higher dimensions, such keywords do not exist and this type is + simply an integer. Conversions from this integer to bitsets still + work but do not provide any easier API for adjacency selection. + */ + using Adjacency = int; + + /// @} + +}; + +} + +#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index 057472820e0c..5a4db5041f52 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -20,6 +20,7 @@ #include #include +#include namespace CGAL { @@ -43,7 +44,7 @@ template < typename PointSet, typename PointMap = Identity_property_map > -struct Orthtree_traits_point_2 { +struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { public: /// \name Types @@ -51,55 +52,10 @@ struct Orthtree_traits_point_2 { using Self = Orthtree_traits_point_2; - using Dimension = Dimension_tag<2>; - using Bbox_d = Bbox_2; - using FT = typename GeomTraits::FT; - using Point_d = typename GeomTraits::Point_2; - using Sphere_d = typename GeomTraits::Circle_2; - using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_2; - using Array = std::array; // todo: This should have a more descriptive name - // todo: looking for better names using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 2-bit integers. - * - * The first bit indicates the axis (0 = x, 1 = y), - * the second bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | - * | y+ - * | * - * 0 *------+------* 1 | - * | | - * | +-----* x+ - * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 00 | 0 | LEFT | - * | `+x` | 01 | 1 | RIGHT | - * | `-y` | 10 | 2 | DOWN | - * | `+y` | 11 | 3 | UP | - */ - enum Adjacency - { - LEFT, - RIGHT, - DOWN, - UP - }; - #ifdef DOXYGEN_RUNNING /*! Functor with an operator to construct a `Point_d` from an `Array` object. @@ -108,9 +64,9 @@ struct Orthtree_traits_point_2 { #else struct Construct_point_d_from_array { - Point_d operator() (const Array& array) const + typename Self::Point_d operator() (const typename Self::Array& array) const { - return Point_d (array[0], array[1]); + return typename Self::Point_d (array[0], array[1]); } }; #endif @@ -124,10 +80,10 @@ struct Orthtree_traits_point_2 { #else struct Construct_bbox_d { - Bbox_d operator() (const Array& min, - const Array& max) const + typename Self::Bbox_d operator() (const typename Self::Array& min, + const typename Self::Array& max) const { - return Bbox_d (min[0], min[1], max[0], max[1]); + return typename Self::Bbox_d (min[0], min[1], max[0], max[1]); } }; #endif @@ -152,17 +108,17 @@ struct Orthtree_traits_point_2 { */ Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - std::pair root_node_bbox() const { + std::pair root_node_bbox() const { - Array bbox_min; - Array bbox_max; + typename Self::Array bbox_min; + typename Self::Array bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found { - const Point_d& point = get(m_point_map, *(m_point_set.begin())); + const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); std::size_t i = 0; - for (const FT& x: cartesian_range(point)) { + for (const typename Self::FT& x: cartesian_range(point)) { bbox_min[i] = x; bbox_max[i] = x; ++i; @@ -170,9 +126,9 @@ struct Orthtree_traits_point_2 { } // Expand bbox to contain all points for (const auto& p: m_point_set) { - const Point_d& point = get(m_point_map, p); + const typename Self::Point_d& point = get(m_point_map, p); std::size_t i = 0; - for (const FT& x: cartesian_range(point)) { + for (const typename Self::FT& x: cartesian_range(point)) { bbox_min[i] = (std::min)(x, bbox_min[i]); bbox_max[i] = (std::max)(x, bbox_max[i]); ++i; @@ -185,12 +141,12 @@ struct Orthtree_traits_point_2 { Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } template - void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { + void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); reassign_points(tree, m_point_map, n, center, tree.data(n)); } - Point_d get_element(const Node_data_element& index) const { + typename Self::Point_d get_element(const Node_data_element& index) const { return get(m_point_map, index); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index c14f56346421..be0717a87117 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -20,6 +20,7 @@ #include #include +#include namespace CGAL { @@ -43,7 +44,7 @@ template < typename PointSet, typename PointMap = Identity_property_map > -struct Orthtree_traits_point_3 { +struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { public: /// \name Types @@ -51,73 +52,10 @@ struct Orthtree_traits_point_3 { using Self = Orthtree_traits_point_3; - using Dimension = Dimension_tag<3>; - using Bbox_d = Bbox_3; - using FT = typename GeomTraits::FT; - using Point_d = typename GeomTraits::Point_3; - using Sphere_d = typename GeomTraits::Sphere_3; - using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_3; - using Array = std::array; // todo: This should have a more descriptive name - // todo: looking for better names using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 3-bit integers, - * though the numbers 6 and 7 are not used because there are only 6 different directions. - * - * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), - * the third bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | * 5 - * | / y+ - * |/ * z+ - * 0 *------+------* 1 | * - * /| |/ - * / | +-----* x+ - * 4 * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 000 | 0 | LEFT | - * | `+x` | 001 | 1 | RIGHT | - * | `-y` | 010 | 2 | DOWN | - * | `+y` | 011 | 3 | UP | - * | `-z` | 100 | 4 | BACK | - * | `+z` | 101 | 5 | FRONT | - */ - enum Adjacency { - LEFT, - RIGHT, - DOWN, - UP, - BACK, - FRONT - }; - - /// \cond SKIP_IN_MANUAL - enum Child { - LEFT_BOTTOM_BACK, - RIGHT_BOTTOM_BACK, - LEFT_TOP_BACK, - RIGHT_TOP_BACK, - LEFT_BOTTOM_FRONT, - RIGHT_BOTTOM_FRONT, - LEFT_TOP_FRONT, - RIGHT_TOP_FRONT - }; - /// \endcond - #ifdef DOXYGEN_RUNNING /*! Functor with an operator to construct a `Point_d` from an `Array` object. @@ -126,8 +64,8 @@ struct Orthtree_traits_point_3 { #else struct Construct_point_d_from_array { - Point_d operator()(const Array& array) const { - return Point_d(array[0], array[1], array[2]); + typename Self::Point_d operator()(const typename Self::Array& array) const { + return typename Self::Point_d(array[0], array[1], array[2]); } }; @@ -141,8 +79,8 @@ struct Orthtree_traits_point_3 { #else struct Construct_bbox_d { - Bbox_d operator()(const Array& min, - const Array& max) const { + typename Self::Bbox_d operator()(const typename Self::Array& min, + const typename Self::Array& max) const { return Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); } }; @@ -169,17 +107,17 @@ struct Orthtree_traits_point_3 { */ Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - std::pair root_node_bbox() const { + std::pair root_node_bbox() const { - Array bbox_min; - Array bbox_max; + typename Self::Array bbox_min; + typename Self::Array bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found { - const Point_d& point = get(m_point_map, *(m_point_set.begin())); + const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); std::size_t i = 0; - for (const FT& x: cartesian_range(point)) { + for (const typename Self::FT& x: cartesian_range(point)) { bbox_min[i] = x; bbox_max[i] = x; ++i; @@ -187,9 +125,9 @@ struct Orthtree_traits_point_3 { } // Expand bbox to contain all points for (const auto& p: m_point_set) { - const Point_d& point = get(m_point_map, p); + const typename Self::Point_d& point = get(m_point_map, p); std::size_t i = 0; - for (const FT& x: cartesian_range(point)) { + for (const typename Self::FT& x: cartesian_range(point)) { bbox_min[i] = (std::min)(x, bbox_min[i]); bbox_max[i] = (std::max)(x, bbox_max[i]); ++i; @@ -199,15 +137,15 @@ struct Orthtree_traits_point_3 { return {bbox_min, bbox_max}; } - Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + typename Self::Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } template - void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { + void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); reassign_points(tree, m_point_map, n, center, tree.data(n)); } - Point_d get_element(const Node_data_element& index) const { + typename Self::Point_d get_element(const Node_data_element& index) const { return get(m_point_map, index); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 406ef377e371..1937b96e28e8 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -19,6 +19,8 @@ #include #include +#include + namespace CGAL { // todo: should this go in its own header & namespace? @@ -79,7 +81,7 @@ template < typename PointSet, typename PointMap = Identity_property_map > -struct Orthtree_traits_point_d { +struct Orthtree_traits_point_d : public Orthtree_traits_d_base { public: /// \name Types @@ -87,46 +89,9 @@ struct Orthtree_traits_point_d { using Self = Orthtree_traits_point_d; - using Dimension = DimensionTag; - using FT = typename GeomTraits::FT; - using Point_d = typename GeomTraits::Point_d; - using Sphere_d = typename GeomTraits::Sphere_d; - using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_d; - using Array = std::array; - using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; -#ifdef DOXYGEN_RUNNING - typedef unspecified_type Bbox_d; ///< Bounding box type. -#else - - class Bbox_d { - Point_d m_min, m_max; - public: - - Bbox_d(const Point_d& pmin, const Point_d& pmax) - : m_min(pmin), m_max(pmax) {} - - const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_min; } - - const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_max; } - }; - -#endif - - /*! - Adjacency type. - - \note This type is used to identify adjacency directions with - easily understandable keywords (left, right, up, etc.) and is thus - mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In - higher dimensions, such keywords do not exist and this type is - simply an integer. Conversions from this integer to bitsets still - works but do not provide any easier API for adjacency selection. - */ - using Adjacency = int; - #ifdef DOXYGEN_RUNNING /*! Functor with an operator to construct a `Point_d` from an `Array` object. @@ -135,8 +100,8 @@ struct Orthtree_traits_point_d { #else struct Construct_point_d_from_array { - Point_d operator()(const Array& array) const { - return Point_d(array.begin(), array.end()); + typename Self::Point_d operator()(const typename Self::Array& array) const { + return typename Self::Point_d(array.begin(), array.end()); } }; @@ -150,8 +115,8 @@ struct Orthtree_traits_point_d { #else struct Construct_bbox_d { - Bbox_d operator()(const Array& min, const Array& max) const { - return Bbox_d(Point_d(min.begin(), min.end()), Point_d(max.begin(), max.end())); + typename Self::Bbox_d operator()(const typename Self::Array& min, const typename Self::Array& max) const { + return typename Self::Bbox_d(typename Self::Point_d(min.begin(), min.end()), typename Self::Point_d(max.begin(), max.end())); } }; @@ -177,17 +142,17 @@ struct Orthtree_traits_point_d { */ Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - std::pair root_node_bbox() const { + std::pair root_node_bbox() const { - Array bbox_min; - Array bbox_max; + typename Self::Array bbox_min; + typename Self::Array bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found { const auto& point = get(m_point_map, *(m_point_set.begin())); std::size_t i = 0; - for (const FT& x: cartesian_range(point)) { + for (const typename Self::FT& x: cartesian_range(point)) { bbox_min[i] = x; bbox_max[i] = x; ++i; @@ -197,7 +162,7 @@ struct Orthtree_traits_point_d { for (const auto& p: m_point_set) { const auto& point = get(m_point_map, p); std::size_t i = 0; - for (const FT& x: cartesian_range(point)) { + for (const typename Self::FT& x: cartesian_range(point)) { bbox_min[i] = (std::min)(x, bbox_min[i]); bbox_max[i] = (std::max)(x, bbox_max[i]); ++i; @@ -210,12 +175,12 @@ struct Orthtree_traits_point_d { Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } template - void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { + void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); reassign_points(tree, m_point_map, n, center, tree.data(n)); } - Point_d get_element(const Node_data_element& index) const { + typename Self::Point_d get_element(const Node_data_element& index) const { return get(m_point_map, index); } From 7fd3badc4ca8110d0ae1cae8385ebeac9a784f02 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 27 Jul 2023 21:44:04 -0600 Subject: [PATCH 126/520] easy check for empty polygons --- .../test/Polygon_repair_2/draw_test_polygons.cpp | 14 +++++++------- .../Polygon_repair_2/repair_polygon_2_test.cpp | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp index e75a0ecdb646..1c8c9366cfc1 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp @@ -23,20 +23,20 @@ int main(int argc, char* argv[]) { if (file.path().filename().extension() != ".wkt") continue; std::cout << "Reading " << file.path().filename() << "..." << std::endl; - // if (file.path().filename() != "hole-outside.wkt") continue; + // if (file.path().filename() != "nesting.wkt") continue; std::string in; std::getline(std::ifstream(file.path()), in); std::istringstream iss(in); - std::size_t is_polygon = in.find("POLYGON"); - std::size_t is_multipolygon = in.find("MULTIPOLYGON"); Multipolygon_with_holes_2 rmp; - if (is_polygon == 0) { + + if (in.find("POLYGON") == 0) { Polygon_with_holes_2 p; - CGAL::IO::read_polygon_WKT(iss, p); - CGAL::draw(p); + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + } CGAL::draw(p); rmp = CGAL::Polygon_repair_2::repair(p); - } else if (is_multipolygon == 0) { + } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); CGAL::draw(mp); diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index 545c19943daa..a9e367b39b8c 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -29,8 +29,9 @@ int main(int argc, char* argv[]) { Multipolygon_with_holes_2 rmp; if (in.find("POLYGON") == 0) { Polygon_with_holes_2 p; - CGAL::IO::read_polygon_WKT(iss, p); - rmp = CGAL::Polygon_repair_2::repair(p); + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + } rmp = CGAL::Polygon_repair_2::repair(p); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); From 49177f78dfd21fde270b4d690d16c65866087b80 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 27 Jul 2023 21:45:33 -0600 Subject: [PATCH 127/520] new nesting code + reconstruction fix --- .../Polygon_repair_2/nasty_polygons.cpp | 9 +- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 110 +++++++++++------- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp index d9fddbba8350..9e64c21ecb56 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp @@ -16,10 +16,10 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - // std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); + // std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); - std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); + std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); std::ofstream ofs("/Users/ken/Downloads/2.geojson"); std::string in; @@ -30,10 +30,7 @@ int main(int argc, char* argv[]) { Polygon_repair_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); - - - - pr.compute_hole_nesting(); + pr.compute_nesting(); pr.reconstruct_multipolygon(); Multipolygon_with_holes_2 rmp = pr.multipolygon(); std::ostringstream oss; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 5ad3d31b15dc..6b1f35712690 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -39,7 +39,7 @@ Multipolygon_with_holes_2 repair(const Polygon_2 0) { pr.label_triangulation(); - pr.compute_hole_nesting(); + pr.compute_nesting(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -52,7 +52,7 @@ Multipolygon_with_holes_2 repair(const Polygon_with_ho pr.add_to_triangulation(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation(); - pr.compute_hole_nesting(); + pr.compute_nesting(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -65,7 +65,7 @@ Multipolygon_with_holes_2 repair(const Multipolygon_wi pr.add_to_triangulation(mp); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation(); - pr.compute_hole_nesting(); + pr.compute_nesting(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -211,13 +211,14 @@ class Polygon_repair_2 { void reconstruct_ring(std::list& ring, typename Triangulation::Face_handle face_adjacent_to_boundary, int opposite_vertex) { + // std::cout << "Reconstructing ring for face " << face_adjacent_to_boundary->label() << "..." << std::endl; // Create ring typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; int current_opposite_vertex = opposite_vertex; do { typename Triangulation::Vertex_handle pivot_vertex = current_face->vertex(current_face->cw(current_opposite_vertex)); - // std::cout << "Adding point " << pivot_vertex->point() << std::endl; + // std::cout << "\tAdding point " << pivot_vertex->point() << std::endl; ring.push_back(pivot_vertex->point()); typename Triangulation::Face_circulator fc = t.incident_faces(pivot_vertex, current_face); do { @@ -239,21 +240,42 @@ class Polygon_repair_2 { } } - void compute_hole_nesting() { - nesting.resize(number_of_holes); - for (auto const &face: t.finite_face_handles()) { - if (face->label() >= -1) continue; // skip non-hole triangles + void compute_nesting() { + for (auto const face: t.all_face_handles()) { + face->processed() = false; + } polygon_nesting.resize(number_of_polygons); + hole_nesting.resize(number_of_holes); + std::list to_check; + to_check.push_back(t.infinite_face()); + t.infinite_face()->processed() = true; + while (!to_check.empty()) { + typename Triangulation::Face_handle face = to_check.front(); + to_check.pop_front(); for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { - if (face->label() == face->neighbor(opposite_vertex)->label()) continue; - nesting[-face->label()-2].insert(face->neighbor(opposite_vertex)->label()); + if (face->neighbor(opposite_vertex)->processed()) continue; + if (face->label() == face->neighbor(opposite_vertex)->label()) { + to_check.push_front(face->neighbor(opposite_vertex)); + } else { + if (face->neighbor(opposite_vertex)->label() < -1) { + hole_nesting[-face->neighbor(opposite_vertex)->label()-2].insert(face->label()); + } else { + polygon_nesting[face->neighbor(opposite_vertex)->label()-1].insert(face->label()); + } to_check.push_back(face->neighbor(opposite_vertex)); + } face->neighbor(opposite_vertex)->processed() = true; } } - // int hole_label = -2; - // for (auto const &hole: nesting) { - // std::cout << "Hole " << hole_label-- << " contained in polygon(s): "; + // int polygon_label = 1; + // for (auto const &polygon: polygon_nesting) { + // std::cout << "Polygon " << polygon_label++ << " contained in hole(s):"; + // for (auto const& hole: polygon) { + // std::cout << " " << hole; + // } std::cout << std::endl; + // } int hole_label = -2; + // for (auto const &hole: hole_nesting) { + // std::cout << "Hole " << hole_label-- << " contained in polygon(s):"; // for (auto const &polygon: hole) { - // std::cout << polygon << " "; + // std::cout << " " << polygon; // } std::cout << std::endl; // } } @@ -270,38 +292,40 @@ class Polygon_repair_2 { face->processed() = false; } for (auto const &face: t.finite_face_handles()) { if (face->label() == -1) continue; // exterior triangle - if (face->label() < -1 && nesting[-face->label()-2].size() > 1) continue; // exterior triangle + if (face->label() < -1 && hole_nesting[-face->label()-2].size() > 1) continue; // exterior triangle if (face->processed()) continue; // already reconstructed for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { - if (face->label() != face->neighbor(opposite_vertex)->label()) { - - std::list ring; - reconstruct_ring(ring, face, opposite_vertex); - if (face->label() > 0) { - polygons[face->label()-1].insert(polygons[face->label()-1].vertices_end(), - ring.begin(), ring.end()); - } else { - int hole_nesting = *(nesting[-face->label()-2].begin()); - // std::cout << "Hole: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << hole_nesting << std::endl; - ring.push_back(ring.front()); - ring.pop_front(); - holes[hole_nesting-1].insert(Polygon_2(ring.rbegin(), ring.rend())); - } - - std::list to_check; - to_check.push_back(face); - while (!to_check.empty()) { - for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (to_check.front()->label() == to_check.front()->neighbor(neighbour)->label() && - !to_check.front()->neighbor(neighbour)->processed()) { - to_check.push_back(to_check.front()->neighbor(neighbour)); - } - } to_check.front()->processed() = true; - to_check.pop_front(); - } + if (face->label() == face->neighbor(opposite_vertex)->label()) continue; // not adjacent to boundary + if (face->label() > 0) { + if (polygon_nesting[face->label()-1].count(face->neighbor(opposite_vertex)->label()) != 1) continue; // not adjacent to outer boundary + } else if (hole_nesting[-face->label()-2].count(face->neighbor(opposite_vertex)->label()) != 1) continue; // not adjacent to correct polygon + + std::list ring; + reconstruct_ring(ring, face, opposite_vertex); + if (face->label() > 0) { + polygons[face->label()-1].insert(polygons[face->label()-1].vertices_end(), + ring.begin(), ring.end()); + } else { + int polygon = *(hole_nesting[-face->label()-2].begin());; + // std::cout << "Hole: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << polygon << std::endl; + ring.push_back(ring.front()); + ring.pop_front(); + holes[polygon-1].insert(Polygon_2(ring.rbegin(), ring.rend())); + } - break; + std::list to_check; + to_check.push_back(face); + while (!to_check.empty()) { + for (int neighbour = 0; neighbour < 3; ++neighbour) { + if (to_check.front()->label() == to_check.front()->neighbor(neighbour)->label() && + !to_check.front()->neighbor(neighbour)->processed()) { + to_check.push_back(to_check.front()->neighbor(neighbour)); + } + } to_check.front()->processed() = true; + to_check.pop_front(); } + + break; } } @@ -341,7 +365,7 @@ class Polygon_repair_2 { Triangulation t; Multipolygon_with_holes_2 mp; int number_of_polygons, number_of_holes; - std::vector> nesting; // note: holes are surrounded by exactly one polygon + std::vector> polygon_nesting, hole_nesting; // note: holes are surrounded by exactly one polygon }; } // namespace Polygon_repair_2 From 60a7ef65f9ffad78adc68fb1ebcea43087bda981 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 27 Jul 2023 21:45:45 -0600 Subject: [PATCH 128/520] ref output for nesting unit test --- Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt | 1 + 1 file changed, 1 insertion(+) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt new file mode 100644 index 000000000000..15587830fca1 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3))) \ No newline at end of file From 9d8309f80f509c4a2a51a04c5411c6332dac9b5c Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 27 Jul 2023 21:46:10 -0600 Subject: [PATCH 129/520] code to write labelled triangulation --- .../write_labeled_triangulation.cpp | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp diff --git a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp new file mode 100644 index 000000000000..8b4c48127795 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; + +int main(int argc, char* argv[]) { + + // std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); + // std::ofstream ofs("/Users/ken/Downloads/2.geojson"); + + // std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); + // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); + + std::ifstream ifs("../../test/Polygon_repair_2/data/in/nesting.wkt"); + std::ofstream ofs("/Users/ken/Downloads/triangulation.geojson"); + + std::string in; + std::getline(ifs, in); + std::istringstream iss(in); + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + Polygon_repair_2 pr; + pr.add_to_triangulation(mp); + pr.label_triangulation(); + + // ofs << std::fixed; + // ofs << std::setprecision(15); + + // ofs << "{" << std::endl; + // ofs << "\t\"type\": \"FeatureCollection\"," << std::endl; + // ofs << "\t\"features\": [" << std::endl; + + // for (Polygon_repair_2::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); + // face != pr.triangulation().finite_faces_end(); ++face) { + // ofs << "\t\t{" << std::endl; + // ofs << "\t\t\t\"type\": \"Feature\"," << std::endl; + // ofs << "\t\t\t\"properties\": {" << std::endl; + // ofs << "\t\t\t\t\"label\": " << face->label() << std::endl; + // ofs << "\t\t\t}," << std::endl; + // ofs << "\t\t\t\"geometry\": {" << std::endl; + // ofs << "\t\t\t\t\"type\": \"Polygon\"," << std::endl; + // ofs << "\t\t\t\t\"coordinates\": [" << std::endl; + // ofs << "\t\t\t\t\t[" << std::endl; + // ofs << "\t\t\t\t\t\t[" << face->vertex(0)->point().x() << ", " << face->vertex(0)->point().y() << "]," << std::endl; + // ofs << "\t\t\t\t\t\t[" << face->vertex(1)->point().x() << ", " << face->vertex(1)->point().y() << "]," << std::endl; + // ofs << "\t\t\t\t\t\t[" << face->vertex(2)->point().x() << ", " << face->vertex(2)->point().y() << "]" << std::endl; + // ofs << "\t\t\t\t\t]" << std::endl; + // ofs << "\t\t\t\t]" << std::endl; + // ofs << "\t\t\t}" << std::endl; + // ofs << "\t\t}"; + // Polygon_repair_2::Triangulation::Finite_faces_iterator next_face = face; + // ++next_face; + // if (next_face != pr.triangulation().finite_faces_end()) ofs << ","; + // ofs << std::endl; + // } + + // ofs << "\t]" << std::endl; + // ofs << "}" << std::endl; + + pr.compute_nesting(); + pr.reconstruct_multipolygon(); + Multipolygon_with_holes_2 rmp = pr.multipolygon(); + // std::ostringstream oss; + // CGAL::IO::write_multi_polygon_WKT(oss, rmp); + // std::string out = oss.str(); + + // ofs << std::fixed; + // ofs << std::setprecision(15); + + + // ofs << "\t\"coordinates\": [" << std::endl; + // for (int i = 0; i < rmp.polygons().size(); ++i) { + // ofs << "\t\t[" << std::endl; + + // ofs << "\t\t\t[" << std::endl; + // for (int j = 0; j < rmp.polygons()[i].outer_boundary().size(); ++j) { + // ofs << "\t\t\t\t[" << rmp.polygons()[i].outer_boundary()[j].x() << + // ", " << rmp.polygons()[i].outer_boundary()[j].y() << "]"; + // if (j < rmp.polygons()[i].outer_boundary().size()-1) ofs << ","; + // ofs << std::endl; + // } ofs << "\t\t\t]"; + // if (rmp.polygons()[i].number_of_holes() > 0) ofs << ","; + // ofs << std::endl; + + // for (int j = 0; j < rmp.polygons()[i].holes().size(); ++j) { + // ofs << "\t\t\t[" << std::endl; + // for (int k = 0; k < rmp.polygons()[i].holes()[j].size(); ++k) { + // ofs << "\t\t\t\t[" << rmp.polygons()[i].holes()[j][k].x() << + // ", " << rmp.polygons()[i].holes()[j][k].y() << "]"; + // if (k < rmp.polygons()[i].holes()[j].size()-1) ofs << ","; + // ofs << std::endl; + // } + // ofs << "\t\t\t]"; + // if (j < rmp.polygons()[i].holes().size()-1) ofs << ","; + // ofs << std::endl; + // } + + // ofs << "\t\t]"; + // if (i < rmp.polygons().size()-1) ofs << ","; + // ofs << std::endl; + // } ofs << "\t]" << std::endl; + + + return 0; +} From cca727f5c34647864f0947e30ce98e63f1ab2d95 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 28 Jul 2023 10:58:42 -0600 Subject: [PATCH 130/520] c++17 --- Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt | 2 ++ Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp | 2 +- .../test/Polygon_repair_2/repair_polygon_2_test.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt index 365fba1a84c2..3f145cb62154 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt @@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Tests) +set(CMAKE_CXX_STANDARD 17) + find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) # create a target per cppfile diff --git a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp index 1c8c9366cfc1..c89f479c4958 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp @@ -19,7 +19,7 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { + for (const auto& file: std::filesystem::directory_iterator("data/in")) { if (file.path().filename().extension() != ".wkt") continue; std::cout << "Reading " << file.path().filename() << "..." << std::endl; diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index a9e367b39b8c..50bbe4a0813b 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -16,7 +16,7 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - for (const auto& file: std::__fs::filesystem::directory_iterator("data/in")) { + for (const auto& file: std::filesystem::directory_iterator("data/in")) { if (file.path().filename().extension() != ".wkt") continue; std::cout << "Testing " << file.path().filename() << "... "; From 4fc0727298aad7d05ed4ca0b1cf9c9c660bc873e Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 28 Jul 2023 21:11:25 -0600 Subject: [PATCH 131/520] consistent polygon vs hole orientation -> no need for nesting --- .../Polygon_repair_2/nasty_polygons.cpp | 1 - .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 85 +++---------------- 2 files changed, 12 insertions(+), 74 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp index 9e64c21ecb56..3170158ed334 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp @@ -30,7 +30,6 @@ int main(int argc, char* argv[]) { Polygon_repair_2 pr; pr.add_to_triangulation(p); pr.label_triangulation(); - pr.compute_nesting(); pr.reconstruct_multipolygon(); Multipolygon_with_holes_2 rmp = pr.multipolygon(); std::ostringstream oss; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 6b1f35712690..fea6c0e2d6aa 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -39,7 +39,6 @@ Multipolygon_with_holes_2 repair(const Polygon_2 0) { pr.label_triangulation(); - pr.compute_nesting(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -52,7 +51,6 @@ Multipolygon_with_holes_2 repair(const Polygon_with_ho pr.add_to_triangulation(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation(); - pr.compute_nesting(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -65,7 +63,6 @@ Multipolygon_with_holes_2 repair(const Multipolygon_wi pr.add_to_triangulation(mp); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation(); - pr.compute_nesting(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -217,6 +214,8 @@ class Polygon_repair_2 { typename Triangulation::Face_handle current_face = face_adjacent_to_boundary; int current_opposite_vertex = opposite_vertex; do { + CGAL_assertion(current_face->label() == face_adjacent_to_boundary->label()); + current_face->processed() = true; typename Triangulation::Vertex_handle pivot_vertex = current_face->vertex(current_face->cw(current_opposite_vertex)); // std::cout << "\tAdding point " << pivot_vertex->point() << std::endl; ring.push_back(pivot_vertex->point()); @@ -240,46 +239,6 @@ class Polygon_repair_2 { } } - void compute_nesting() { - for (auto const face: t.all_face_handles()) { - face->processed() = false; - } polygon_nesting.resize(number_of_polygons); - hole_nesting.resize(number_of_holes); - std::list to_check; - to_check.push_back(t.infinite_face()); - t.infinite_face()->processed() = true; - while (!to_check.empty()) { - typename Triangulation::Face_handle face = to_check.front(); - to_check.pop_front(); - for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { - if (face->neighbor(opposite_vertex)->processed()) continue; - if (face->label() == face->neighbor(opposite_vertex)->label()) { - to_check.push_front(face->neighbor(opposite_vertex)); - } else { - if (face->neighbor(opposite_vertex)->label() < -1) { - hole_nesting[-face->neighbor(opposite_vertex)->label()-2].insert(face->label()); - } else { - polygon_nesting[face->neighbor(opposite_vertex)->label()-1].insert(face->label()); - } to_check.push_back(face->neighbor(opposite_vertex)); - } face->neighbor(opposite_vertex)->processed() = true; - } - } - - // int polygon_label = 1; - // for (auto const &polygon: polygon_nesting) { - // std::cout << "Polygon " << polygon_label++ << " contained in hole(s):"; - // for (auto const& hole: polygon) { - // std::cout << " " << hole; - // } std::cout << std::endl; - // } int hole_label = -2; - // for (auto const &hole: hole_nesting) { - // std::cout << "Hole " << hole_label-- << " contained in polygon(s):"; - // for (auto const &polygon: hole) { - // std::cout << " " << polygon; - // } std::cout << std::endl; - // } - } - // Reconstruct multipolygon based on the triangles labeled as inside the polygon void reconstruct_multipolygon() { mp.clear(); @@ -290,42 +249,22 @@ class Polygon_repair_2 { for (auto const face: t.all_face_handles()) { face->processed() = false; - } for (auto const &face: t.finite_face_handles()) { - if (face->label() == -1) continue; // exterior triangle - if (face->label() < -1 && hole_nesting[-face->label()-2].size() > 1) continue; // exterior triangle + } + for (auto const &face: t.finite_face_handles()) { + if (face->label() < 1) continue; // exterior triangle if (face->processed()) continue; // already reconstructed for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { if (face->label() == face->neighbor(opposite_vertex)->label()) continue; // not adjacent to boundary - if (face->label() > 0) { - if (polygon_nesting[face->label()-1].count(face->neighbor(opposite_vertex)->label()) != 1) continue; // not adjacent to outer boundary - } else if (hole_nesting[-face->label()-2].count(face->neighbor(opposite_vertex)->label()) != 1) continue; // not adjacent to correct polygon std::list ring; reconstruct_ring(ring, face, opposite_vertex); - if (face->label() > 0) { - polygons[face->label()-1].insert(polygons[face->label()-1].vertices_end(), - ring.begin(), ring.end()); + Polygon_2 polygon(ring.begin(), ring.end()); + // std::cout << "Reconstructed ring for polygon " << face->label() << " with ccw? " << (polygon.orientation() == CGAL::COUNTERCLOCKWISE) << std::endl; + if (polygon.orientation() == CGAL::COUNTERCLOCKWISE) { + polygons[face->label()-1] = polygon; } else { - int polygon = *(hole_nesting[-face->label()-2].begin());; - // std::cout << "Hole: " << face->label() << " -> item " << -face->label()-2 << " -> in polygon " << polygon << std::endl; - ring.push_back(ring.front()); - ring.pop_front(); - holes[polygon-1].insert(Polygon_2(ring.rbegin(), ring.rend())); - } - - std::list to_check; - to_check.push_back(face); - while (!to_check.empty()) { - for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (to_check.front()->label() == to_check.front()->neighbor(neighbour)->label() && - !to_check.front()->neighbor(neighbour)->processed()) { - to_check.push_back(to_check.front()->neighbor(neighbour)); - } - } to_check.front()->processed() = true; - to_check.pop_front(); - } - - break; + holes[face->label()-1].insert(polygon); + } break; } } @@ -365,7 +304,7 @@ class Polygon_repair_2 { Triangulation t; Multipolygon_with_holes_2 mp; int number_of_polygons, number_of_holes; - std::vector> polygon_nesting, hole_nesting; // note: holes are surrounded by exactly one polygon + // std::vector> polygon_nesting, hole_nesting; }; } // namespace Polygon_repair_2 From eeea56a00aa83867af54f5f0f65a3d797caef9a5 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 28 Jul 2023 21:11:40 -0600 Subject: [PATCH 132/520] cleanup test files --- .../write_labeled_triangulation.cpp | 119 +++++++----------- .../Polygon_repair_2/draw_test_polygons.cpp | 2 +- 2 files changed, 44 insertions(+), 77 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp index 8b4c48127795..29ee0acfa7d3 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp @@ -22,95 +22,62 @@ int main(int argc, char* argv[]) { // std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); - std::ifstream ifs("../../test/Polygon_repair_2/data/in/nesting.wkt"); + std::ifstream ifs("../../test/Polygon_repair_2/data/in/nesting-spike.wkt"); std::ofstream ofs("/Users/ken/Downloads/triangulation.geojson"); std::string in; std::getline(ifs, in); std::istringstream iss(in); - Multipolygon_with_holes_2 mp; - CGAL::IO::read_multi_polygon_WKT(iss, mp); Polygon_repair_2 pr; - pr.add_to_triangulation(mp); - pr.label_triangulation(); + + if (in.find("POLYGON") == 0) { + Polygon_with_holes_2 p; + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + } pr.add_to_triangulation(p); + } else if (in.find("MULTIPOLYGON") == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + pr.add_to_triangulation(mp); + } pr.label_triangulation(); // ofs << std::fixed; // ofs << std::setprecision(15); - // ofs << "{" << std::endl; - // ofs << "\t\"type\": \"FeatureCollection\"," << std::endl; - // ofs << "\t\"features\": [" << std::endl; - - // for (Polygon_repair_2::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); - // face != pr.triangulation().finite_faces_end(); ++face) { - // ofs << "\t\t{" << std::endl; - // ofs << "\t\t\t\"type\": \"Feature\"," << std::endl; - // ofs << "\t\t\t\"properties\": {" << std::endl; - // ofs << "\t\t\t\t\"label\": " << face->label() << std::endl; - // ofs << "\t\t\t}," << std::endl; - // ofs << "\t\t\t\"geometry\": {" << std::endl; - // ofs << "\t\t\t\t\"type\": \"Polygon\"," << std::endl; - // ofs << "\t\t\t\t\"coordinates\": [" << std::endl; - // ofs << "\t\t\t\t\t[" << std::endl; - // ofs << "\t\t\t\t\t\t[" << face->vertex(0)->point().x() << ", " << face->vertex(0)->point().y() << "]," << std::endl; - // ofs << "\t\t\t\t\t\t[" << face->vertex(1)->point().x() << ", " << face->vertex(1)->point().y() << "]," << std::endl; - // ofs << "\t\t\t\t\t\t[" << face->vertex(2)->point().x() << ", " << face->vertex(2)->point().y() << "]" << std::endl; - // ofs << "\t\t\t\t\t]" << std::endl; - // ofs << "\t\t\t\t]" << std::endl; - // ofs << "\t\t\t}" << std::endl; - // ofs << "\t\t}"; - // Polygon_repair_2::Triangulation::Finite_faces_iterator next_face = face; - // ++next_face; - // if (next_face != pr.triangulation().finite_faces_end()) ofs << ","; - // ofs << std::endl; - // } - - // ofs << "\t]" << std::endl; - // ofs << "}" << std::endl; + ofs << "{" << std::endl; + ofs << "\t\"type\": \"FeatureCollection\"," << std::endl; + ofs << "\t\"features\": [" << std::endl; + + for (Polygon_repair_2::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); + face != pr.triangulation().finite_faces_end(); ++face) { + ofs << "\t\t{" << std::endl; + ofs << "\t\t\t\"type\": \"Feature\"," << std::endl; + ofs << "\t\t\t\"properties\": {" << std::endl; + ofs << "\t\t\t\t\"label\": " << face->label() << std::endl; + ofs << "\t\t\t}," << std::endl; + ofs << "\t\t\t\"geometry\": {" << std::endl; + ofs << "\t\t\t\t\"type\": \"Polygon\"," << std::endl; + ofs << "\t\t\t\t\"coordinates\": [" << std::endl; + ofs << "\t\t\t\t\t[" << std::endl; + ofs << "\t\t\t\t\t\t[" << face->vertex(0)->point().x() << ", " << face->vertex(0)->point().y() << "]," << std::endl; + ofs << "\t\t\t\t\t\t[" << face->vertex(1)->point().x() << ", " << face->vertex(1)->point().y() << "]," << std::endl; + ofs << "\t\t\t\t\t\t[" << face->vertex(2)->point().x() << ", " << face->vertex(2)->point().y() << "]" << std::endl; + ofs << "\t\t\t\t\t]" << std::endl; + ofs << "\t\t\t\t]" << std::endl; + ofs << "\t\t\t}" << std::endl; + ofs << "\t\t}"; + Polygon_repair_2::Triangulation::Finite_faces_iterator next_face = face; + ++next_face; + if (next_face != pr.triangulation().finite_faces_end()) ofs << ","; + ofs << std::endl; + } + + ofs << "\t]" << std::endl; + ofs << "}" << std::endl; - pr.compute_nesting(); pr.reconstruct_multipolygon(); Multipolygon_with_holes_2 rmp = pr.multipolygon(); - // std::ostringstream oss; - // CGAL::IO::write_multi_polygon_WKT(oss, rmp); - // std::string out = oss.str(); - - // ofs << std::fixed; - // ofs << std::setprecision(15); - - - // ofs << "\t\"coordinates\": [" << std::endl; - // for (int i = 0; i < rmp.polygons().size(); ++i) { - // ofs << "\t\t[" << std::endl; - - // ofs << "\t\t\t[" << std::endl; - // for (int j = 0; j < rmp.polygons()[i].outer_boundary().size(); ++j) { - // ofs << "\t\t\t\t[" << rmp.polygons()[i].outer_boundary()[j].x() << - // ", " << rmp.polygons()[i].outer_boundary()[j].y() << "]"; - // if (j < rmp.polygons()[i].outer_boundary().size()-1) ofs << ","; - // ofs << std::endl; - // } ofs << "\t\t\t]"; - // if (rmp.polygons()[i].number_of_holes() > 0) ofs << ","; - // ofs << std::endl; - - // for (int j = 0; j < rmp.polygons()[i].holes().size(); ++j) { - // ofs << "\t\t\t[" << std::endl; - // for (int k = 0; k < rmp.polygons()[i].holes()[j].size(); ++k) { - // ofs << "\t\t\t\t[" << rmp.polygons()[i].holes()[j][k].x() << - // ", " << rmp.polygons()[i].holes()[j][k].y() << "]"; - // if (k < rmp.polygons()[i].holes()[j].size()-1) ofs << ","; - // ofs << std::endl; - // } - // ofs << "\t\t\t]"; - // if (j < rmp.polygons()[i].holes().size()-1) ofs << ","; - // ofs << std::endl; - // } - - // ofs << "\t\t]"; - // if (i < rmp.polygons().size()-1) ofs << ","; - // ofs << std::endl; - // } ofs << "\t]" << std::endl; - + CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); return 0; } diff --git a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp index c89f479c4958..cd3330a3e207 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { if (file.path().filename().extension() != ".wkt") continue; std::cout << "Reading " << file.path().filename() << "..." << std::endl; - // if (file.path().filename() != "nesting.wkt") continue; + if (file.path().filename() != "nesting-spike.wkt") continue; std::string in; std::getline(std::ifstream(file.path()), in); From 372244c5e2bc06f0ecae55eac6d580e829879ad4 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 28 Jul 2023 21:12:03 -0600 Subject: [PATCH 133/520] more unit tests --- .../test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt | 1 + .../test/Polygon_repair_2/data/in/crossing-polygons.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt | 1 + .../test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt | 1 + .../test/Polygon_repair_2/data/ref/crossing-polygons.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt | 1 + 7 files changed, 7 insertions(+) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt new file mode 100644 index 000000000000..6b945e81ff7d --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,3 1,3 2,0 2,0 1)),((1 0,2 0,2 3,1 3,1 0)),((1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons.wkt new file mode 100644 index 000000000000..662e1017f9c6 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,3 1,3 2,0 2,0 1)),((1 0,2 0,2 3,1 3,1 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt new file mode 100644 index 000000000000..e0df5a0dc7d8 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.1 0.1)),((0.2 0.2,2 1,0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt new file mode 100644 index 000000000000..1668214ed240 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt @@ -0,0 +1 @@ +POLYGON((0 0,2 1,0 0,1 0,1 1,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt new file mode 100644 index 000000000000..64190bb04e92 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,1 1,1 2,0 2,0 1)),((1 0,2 0,2 1,1 1,1 0)),((1 2,2 2,2 3,1 3,1 2)),((1.25 1.25,1.75 1.25,1.75 1.75,1.25 1.75,1.25 1.25)),((2 1,3 1,3 2,2 2,2 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons.wkt new file mode 100644 index 000000000000..815bc8c56258 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 1,1 1,1 2,0 2,0 1)),((1 0,2 0,2 1,1 1,1 0)),((1 2,2 2,2 3,1 3,1 2)),((2 1,3 1,3 2,2 2,2 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file From 1f98ff2244ffc930f87cd586bfe07f245661f4d8 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 28 Jul 2023 21:42:09 -0600 Subject: [PATCH 134/520] nesting spike reference file --- .../test/Polygon_repair_2/data/ref/nesting-spike.wkt | 1 + 1 file changed, 1 insertion(+) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting-spike.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting-spike.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting-spike.wkt new file mode 100644 index 000000000000..15587830fca1 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting-spike.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3))) \ No newline at end of file From cbed7ce3d4aaaaa45862d2e73170515e80a30e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 1 Aug 2023 13:17:37 +0200 Subject: [PATCH 135/520] remove trailing whitespaces --- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index fea6c0e2d6aa..8fb655405b89 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -249,7 +249,7 @@ class Polygon_repair_2 { for (auto const face: t.all_face_handles()) { face->processed() = false; - } + } for (auto const &face: t.finite_face_handles()) { if (face->label() < 1) continue; // exterior triangle if (face->processed()) continue; // already reconstructed From d33a2b276c5851c0e387b70438ed6c6501378bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 1 Aug 2023 13:43:02 +0200 Subject: [PATCH 136/520] fix change of meaning errors --- .../include/CGAL/Polygon_repair_2/Polygon_repair_2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 8fb655405b89..19e240322178 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -85,7 +85,7 @@ class Polygon_repair_2 { using Triangulation = Triangulation_with_odd_even_constraints_2; struct Polygon_less { - using Polygon_2 = Polygon_2; + using Polygon_2 = CGAL::Polygon_2; bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); @@ -100,7 +100,7 @@ class Polygon_repair_2 { }; struct Polygon_with_holes_less { - using Polygon_with_holes_2 = Polygon_with_holes_2; + using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; Polygon_less pl; bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { if (pl(pa.outer_boundary(), pb.outer_boundary())) return true; From 45dc0bebfc2b1737a5ffdedc9ba3cec17dad3c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 1 Aug 2023 14:50:50 +0200 Subject: [PATCH 137/520] add missing dependencies --- .../Polygon_repair_2/dependencies | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/dependencies diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/dependencies b/Polygon_repair_2/package_info/Polygon_repair_2/dependencies new file mode 100644 index 000000000000..f1acb2ceddc5 --- /dev/null +++ b/Polygon_repair_2/package_info/Polygon_repair_2/dependencies @@ -0,0 +1,27 @@ +Algebraic_foundations +Arithmetic_kernel +Cartesian_kernel +Circulator +Distance_2 +Distance_3 +Filtered_kernel +GraphicsView +Hash_map +Homogeneous_kernel +Installation +Intersections_2 +Intersections_3 +Interval_support +Kernel_23 +Kernel_d +Modular_arithmetic +Number_types +Polygon +Polygon_repair_2 +Profiling_tools +Property_map +STL_Extension +Spatial_sorting +Stream_support +TDS_2 +Triangulation_2 From bc858b3ed59bf121d5208eaf1fbaa40a02bf6392 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 1 Aug 2023 18:36:17 -0600 Subject: [PATCH 138/520] remove vertices in collinear segments --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 39 ++++++++++++++++++- ...iangulation_face_base_with_repair_info_2.h | 2 +- ...riangulation_with_odd_even_constraints_2.h | 3 +- .../test/Polygon_repair_2/data/in/spiral.wkt | 1 + .../data/in/square-hole-rhombus.wkt | 1 + .../test/Polygon_repair_2/data/in/star.wkt | 1 + .../data/ref/overlapping-edge-partial.wkt | 2 +- .../data/ref/overlapping-edge.wkt | 2 +- .../test/Polygon_repair_2/data/ref/spiral.wkt | 1 + .../data/ref/square-hole-rhombus.wkt | 1 + .../test/Polygon_repair_2/data/ref/star.wkt | 1 + 11 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spiral.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/square-hole-rhombus.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/star.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spiral.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/square-hole-rhombus.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/star.wkt diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 19e240322178..444cc3801637 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -176,6 +176,38 @@ class Polygon_repair_2 { // Label triangles in triangulation void label_triangulation() { + + // Simplify collinear edges (gets rid of order dependency) + for (auto vertex: t.all_vertex_handles()) { + typename Triangulation::Edge_circulator first_edge = t.incident_edges(vertex); + typename Triangulation::Edge_circulator current_edge = first_edge; + std::vector incident_constrained_edges; + do { + if (t.is_constrained(*current_edge)) { + incident_constrained_edges.push_back(*current_edge); + } ++current_edge; + } while (current_edge != first_edge); + if (incident_constrained_edges.size() == 2) { + typename Kernel::Point_2 v1 = incident_constrained_edges.front().first->vertex(incident_constrained_edges.front().first->ccw(incident_constrained_edges.front().second))->point(); + typename Kernel::Point_2 v2 = incident_constrained_edges.back().first->vertex(incident_constrained_edges.back().first->ccw(incident_constrained_edges.back().second))->point(); + typename Kernel::Vector_2 tov1 = vertex->point()-v1; + typename Kernel::Vector_2 tov2 = vertex->point()-v2; + typename Kernel::FT angle = acos((tov1*tov2) / (sqrt(tov1.squared_length())*sqrt(tov2.squared_length()))); + double epsilon = 0.00001; + double pi = 3.14159265358979323846; + if (angle < pi+epsilon && angle > pi-epsilon) { + // std::cout << "Collinear points" << std::endl; + // std::cout << "v1: " << v1 << std::endl; + // std::cout << "in: " << vertex->point() << std::endl; + // std::cout << "v2: " << v2 << std::endl; + t.remove_incident_constraints(vertex); + t.remove(vertex); + t.insert_constraint(v1, v2); + } + } + } + + // Init labels for (auto const face: t.all_face_handles()) { face->label() = 0; face->processed() = false; @@ -242,8 +274,8 @@ class Polygon_repair_2 { // Reconstruct multipolygon based on the triangles labeled as inside the polygon void reconstruct_multipolygon() { mp.clear(); - std::vector> polygons; - std::vector, Polygon_less>> holes; // holes are ordered + std::vector> polygons; // outer boundaries + std::vector, Polygon_less>> holes; // holes are ordered (per polygon) polygons.resize(number_of_polygons); holes.resize(number_of_polygons); @@ -256,8 +288,11 @@ class Polygon_repair_2 { for (int opposite_vertex = 0; opposite_vertex < 3; ++opposite_vertex) { if (face->label() == face->neighbor(opposite_vertex)->label()) continue; // not adjacent to boundary + // Reconstruct ring std::list ring; reconstruct_ring(ring, face, opposite_vertex); + + // Put ring in polygons Polygon_2 polygon(ring.begin(), ring.end()); // std::cout << "Reconstructed ring for polygon " << face->label() << " with ccw? " << (polygon.orientation() == CGAL::COUNTERCLOCKWISE) << std::endl; if (polygon.orientation() == CGAL::COUNTERCLOCKWISE) { diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h index 770b332d86b9..68143c4a13bc 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h @@ -38,7 +38,7 @@ class Triangulation_face_base_with_repair_info_2 : public FaceBase { : FaceBase(v0, v1, v2) {} Triangulation_face_base_with_repair_info_2(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2, - Face_handle n0, Face_handle n1, Face_handle n2 ) + Face_handle n0, Face_handle n1, Face_handle n2) : FaceBase(v0, v1, v2, n0, n1, n2) {} const bool& processed() const { return _processed; } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 957faf869c88..098021d91ad5 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -76,7 +76,8 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { // Degenerate edge if (va == vb) return; - // [va, vb] is an existing edge OR it is composed of shorter edges + // [va, vb] is either an existing edge OR + // there's an existing shorter edge from va in the direction of vb Vertex_handle vc; // [va, vc] is the first edge along [va, vb] Face_handle incident_face; // incident to [va, vc] int opposite_vertex; // opposite to [va, vc] diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spiral.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spiral.wkt new file mode 100644 index 000000000000..d79a0892188c --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spiral.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.4 0.4,0.6 0.4,0.6 0.6,0.4 0.6,0.5 0.5,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/square-hole-rhombus.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/square-hole-rhombus.wkt new file mode 100644 index 000000000000..1cfa76db480b --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/square-hole-rhombus.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0,1 0.5,0.5 1,0 0.5,0.5 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/star.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/star.wkt new file mode 100644 index 000000000000..eb43ea7debbf --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/star.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1.5 3,3 0,0 1.5,3 3,1.5 0,0 3,3 1.5,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt index f7372fef3b31..5588dadf3e63 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt @@ -1 +1 @@ -MULTIPOLYGON(((0 0,1 0,2 0,2 0.5,1 0.5,1 1,0 1,0 0))) \ No newline at end of file +MULTIPOLYGON(((0 0,2 0,2 0.5,1 0.5,1 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt index 33b6059e3993..ed4275a4e2a8 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt @@ -1 +1 @@ -MULTIPOLYGON(((0 0,1 0,2 0,2 1,1 1,0 1,0 0))) \ No newline at end of file +MULTIPOLYGON(((0 0,2 0,2 1,0 1,0 0))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spiral.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spiral.wkt new file mode 100644 index 000000000000..9ba30e469e2d --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spiral.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0.1 0.1,0 0),(0.1 0.1,0.2 0.2,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.3 0.3,0.2 0.2),(0.3 0.3,0.4 0.4,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3)),((0.4 0.4,0.6 0.4,0.6 0.6,0.4 0.6,0.5 0.5,0.4 0.4))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/square-hole-rhombus.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/square-hole-rhombus.wkt new file mode 100644 index 000000000000..d55952100cef --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/square-hole-rhombus.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,0.5 0,0 0.5,0 0)),((0 0.5,0.5 1,0 1,0 0.5)),((0.5 0,1 0,1 0.5,0.5 0)),((0.5 1,1 0.5,1 1,0.5 1))) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/star.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/star.wkt new file mode 100644 index 000000000000..b782e4bbd40e --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/star.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1.2 0.6,1 1,0.6 1.2,0 0)),((0 1.5,0.6 1.2,0.75 1.5,0.6 1.8,0 1.5)),((0 3,0.6 1.8,1 2,1.2 2.4,0 3)),((0.75 1.5,1 1,1.5 0.75,2 1,2.25 1.5,2 2,1.5 2.25,1 2,0.75 1.5)),((1.2 0.6,1.5 0,1.8 0.6,1.5 0.75,1.2 0.6)),((1.2 2.4,1.5 2.25,1.8 2.4,1.5 3,1.2 2.4)),((1.8 0.6,3 0,2.4 1.2,2 1,1.8 0.6)),((1.8 2.4,2 2,2.4 1.8,3 3,1.8 2.4)),((2.25 1.5,2.4 1.2,3 1.5,2.4 1.8,2.25 1.5))) \ No newline at end of file From a290df2e840741fd56ee221f39e58d92a2525207 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 1 Aug 2023 19:16:55 -0600 Subject: [PATCH 139/520] spikes test --- Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt | 1 + Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt | 1 + 2 files changed, 2 insertions(+) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt new file mode 100644 index 000000000000..c831dd8e2506 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0 0),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt new file mode 100644 index 000000000000..511245f448ea --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0))) \ No newline at end of file From c559d6d54fdacdd8d7336e34f7793499944758b2 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 2 Aug 2023 19:44:08 -0600 Subject: [PATCH 140/520] preprocessing to get unique edges --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 67 ++++++++++++++++--- .../Polygon_repair_2/data/in/spikes-fp.wkt | 1 + .../Polygon_repair_2/data/ref/spikes-fp.wkt | 1 + 3 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/in/spikes-fp.wkt create mode 100644 Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes-fp.wkt diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 444cc3801637..916c7c84764f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -124,23 +124,75 @@ class Polygon_repair_2 { // Add edges of the polygon to the triangulation void add_to_triangulation(const Polygon_2& polygon) { + std::unordered_set, + boost::hash>> edges_to_insert; + for (auto const& edge: polygon.edges()) { - t.odd_even_insert_constraint(edge.source(), edge.target()); + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } + + for (auto const& edge: edges_to_insert) { + t.odd_even_insert_constraint(edge.first, edge.second); } } // Add edges of the polygon to the triangulation void add_to_triangulation(const Polygon_with_holes_2& polygon) { - add_to_triangulation(polygon.outer_boundary()); + std::unordered_set, + boost::hash>> edges_to_insert; + + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } for (auto const& hole: polygon.holes()) { - add_to_triangulation(hole); + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } + } + + for (auto const& edge: edges_to_insert) { + t.odd_even_insert_constraint(edge.first, edge.second); } } // Add edges of the polygon to the triangulation void add_to_triangulation(const Multipolygon_with_holes_2& multipolygon) { + std::unordered_set, + boost::hash>> edges_to_insert; + for (auto const& polygon: multipolygon.polygons()) { - add_to_triangulation(polygon); + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) continue; + std::pair pair = (edge.source() < edge.target())? + std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } + } + } + + for (auto const& edge: edges_to_insert) { + t.odd_even_insert_constraint(edge.first, edge.second); } } @@ -190,12 +242,7 @@ class Polygon_repair_2 { if (incident_constrained_edges.size() == 2) { typename Kernel::Point_2 v1 = incident_constrained_edges.front().first->vertex(incident_constrained_edges.front().first->ccw(incident_constrained_edges.front().second))->point(); typename Kernel::Point_2 v2 = incident_constrained_edges.back().first->vertex(incident_constrained_edges.back().first->ccw(incident_constrained_edges.back().second))->point(); - typename Kernel::Vector_2 tov1 = vertex->point()-v1; - typename Kernel::Vector_2 tov2 = vertex->point()-v2; - typename Kernel::FT angle = acos((tov1*tov2) / (sqrt(tov1.squared_length())*sqrt(tov2.squared_length()))); - double epsilon = 0.00001; - double pi = 3.14159265358979323846; - if (angle < pi+epsilon && angle > pi-epsilon) { + if (CGAL::collinear(v1, vertex->point(), v2)) { // std::cout << "Collinear points" << std::endl; // std::cout << "v1: " << v1 << std::endl; // std::cout << "in: " << vertex->point() << std::endl; diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes-fp.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes-fp.wkt new file mode 100644 index 000000000000..2bb7ded142f5 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes-fp.wkt @@ -0,0 +1 @@ +POLYGON((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5)) \ No newline at end of file diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes-fp.wkt b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes-fp.wkt new file mode 100644 index 000000000000..861654da6079 --- /dev/null +++ b/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes-fp.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02))) \ No newline at end of file From ffeeb58f938d4b1a102b99ed783590bcba803b8c Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 2 Aug 2023 20:05:53 -0600 Subject: [PATCH 141/520] trailing whitespaces --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 916c7c84764f..7da830e93467 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -129,11 +129,11 @@ class Polygon_repair_2 { for (auto const& edge: polygon.edges()) { if (edge.source() == edge.target()) continue; - std::pair pair = (edge.source() < edge.target())? + std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); - } + } for (auto const& edge: edges_to_insert) { t.odd_even_insert_constraint(edge.first, edge.second); @@ -147,15 +147,15 @@ class Polygon_repair_2 { for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) continue; - std::pair pair = (edge.source() < edge.target())? + std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); - } + } for (auto const& hole: polygon.holes()) { for (auto const& edge: hole.edges()) { if (edge.source() == edge.target()) continue; - std::pair pair = (edge.source() < edge.target())? + std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); @@ -175,15 +175,15 @@ class Polygon_repair_2 { for (auto const& polygon: multipolygon.polygons()) { for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) continue; - std::pair pair = (edge.source() < edge.target())? + std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); - } + } for (auto const& hole: polygon.holes()) { for (auto const& edge: hole.edges()) { if (edge.source() == edge.target()) continue; - std::pair pair = (edge.source() < edge.target())? + std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); From 506d51f1d32a668481f7d70b97fc4969ea865a10 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 3 Aug 2023 21:35:54 -0600 Subject: [PATCH 142/520] code to repair and generate svgs from the clipart files --- .../examples/Polygon_repair_2/CMakeLists.txt | 2 + .../examples/Polygon_repair_2/clipart.cpp | 117 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt index 73263ecace7c..b7fedd3936a6 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt @@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Examples) +set(CMAKE_CXX_STANDARD 17) + find_package(CGAL REQUIRED) # create a target per cppfile diff --git a/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp b/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp new file mode 100644 index 000000000000..4c0a0ccd8548 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; + +int main(int argc, char* argv[]) { + + std::string folder_in = "/Volumes/Toshiba/out_fix"; + std::string folder_out = "/Volumes/Toshiba/repaired"; + double desired_width = 500.0; + int current = 0, how_many = 10; + + for (const auto& file: std::filesystem::directory_iterator(folder_in)) { + if (file.path().filename().extension() != ".obj") continue; + std::cout << "Reading " << file.path().filename() << "..." << std::endl; + + Polygon_repair_2 pr; + std::vector vertices; + std::vector> edges; + + std::ifstream ifs(file.path()); + std::string line; + + while (std::getline(ifs, line)) { + std::istringstream iss(line); + + char c; + iss >> c; + + if (c == 'v') { + double x, y; + iss >> x >> y; + vertices.emplace_back(x, y); + } else if (c == 'l') { + unsigned int a, b; + iss >> a >> b; + edges.push_back(std::make_pair(vertices[a-1], vertices[b-1])); + } + } + + ifs.close(); + + std::unordered_set, + boost::hash>> edges_to_insert; + + for (auto const& edge: edges) { + if (edge.first == edge.second) continue; + std::pair pair = (edge.first < edge.second)? + std::make_pair(edge.first, edge.second) : std::make_pair(edge.second, edge.first); + auto inserted = edges_to_insert.insert(pair); + if (!inserted.second) edges_to_insert.erase(inserted.first); + } + + for (auto const& edge: edges_to_insert) { + pr.triangulation().odd_even_insert_constraint(edge.first, edge.second); + } + + // std::cout << pr.triangulation().number_of_faces() << " faces in the triangulation" << std::endl; + + if (pr.triangulation().number_of_faces() > 0) { + pr.label_triangulation(); + pr.reconstruct_multipolygon(); + } Multipolygon_with_holes_2 mp = pr.multipolygon(); + + // std::cout << mp << std::endl; + // std::cout << mp.number_of_polygons() << " polygons" << std::endl; + + if (mp.number_of_polygons() > 0) { + CGAL::Bbox_2 bbox = mp.polygons().front().bbox(); + for (auto const& polygon: mp.polygons()) { + bbox += polygon.outer_boundary().bbox(); + } // std::cout << bbox.xmin() << " " << bbox.xmax() << " " << bbox.ymin() << " " << bbox.ymax() << std::endl; + Kernel::Vector_2 translate(-bbox.xmin(), -bbox.ymin()); + double scale = desired_width/(bbox.xmax()-bbox.xmin()); + + + std::ofstream ofs(folder_out + "/" + std::string(file.path().stem()) + ".svg"); + ofs << "" << std::endl; + + for (auto const& polygon: mp.polygons()) { + // std::cout << polygon << std::endl; + ofs << "\t" << std::endl; + } + + for (auto const& polygon: mp.polygons()) { + for (auto const& hole: polygon.holes()) { + // std::cout << hole << std::endl; + ofs << "\t" << std::endl; + } + } + + ofs << ""; + ofs.close(); + } ++current; + +// if (current >= how_many) break; + } + + return 0; +} From f1b2adb2d4da990fc5c3e23a2425f02fac7d1539 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 4 Aug 2023 17:06:31 -0600 Subject: [PATCH 143/520] start vertex search from face incident to last insertion --- .../examples/Polygon_repair_2/clipart.cpp | 62 ++++++++++++++----- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 8 +-- ...riangulation_with_odd_even_constraints_2.h | 10 ++- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp b/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp index 4c0a0ccd8548..5549158ae13e 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp @@ -14,17 +14,32 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; +void print_timer(clock_t start_time) { + clock_t stop_time = clock(); + double seconds = (stop_time-start_time)/(double)CLOCKS_PER_SEC; + std::cout << seconds << " seconds"; +} + int main(int argc, char* argv[]) { - std::string folder_in = "/Volumes/Toshiba/out_fix"; - std::string folder_out = "/Volumes/Toshiba/repaired"; +// std::string folder_in = "/Volumes/Toshiba/out_fix"; +// std::string folder_out = "/Volumes/Toshiba/repaired"; + std::string folder_in = "/Users/ken/Downloads/test"; + std::string folder_out = "/Users/ken/Downloads/test"; double desired_width = 500.0; - int current = 0, how_many = 10; + int current = 0, how_many = 100000; + clock_t start_time; for (const auto& file: std::filesystem::directory_iterator(folder_in)) { + if (file.path().filename().extension() != ".obj") continue; - std::cout << "Reading " << file.path().filename() << "..." << std::endl; - + std::cout << "Reading " << file.path().filename() << "..."; +// if (std::filesystem::exists(folder_out + "/" + std::string(file.path().stem()) + ".svg")) { +// std::cout << " skipped: already processed" << std::endl; +// continue; +// } + + start_time = clock(); Polygon_repair_2 pr; std::vector vertices; std::vector> edges; @@ -34,10 +49,8 @@ int main(int argc, char* argv[]) { while (std::getline(ifs, line)) { std::istringstream iss(line); - char c; iss >> c; - if (c == 'v') { double x, y; iss >> x >> y; @@ -47,10 +60,12 @@ int main(int argc, char* argv[]) { iss >> a >> b; edges.push_back(std::make_pair(vertices[a-1], vertices[b-1])); } - } - - ifs.close(); + } ifs.close(); + std::cout << "Read and parsed file in "; + print_timer(start_time); + std::cout << std::endl; + start_time = clock(); std::unordered_set, boost::hash>> edges_to_insert; @@ -60,17 +75,35 @@ int main(int argc, char* argv[]) { std::make_pair(edge.first, edge.second) : std::make_pair(edge.second, edge.first); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); - } + } std::cout << "Generated unique edges in "; + print_timer(start_time); + std::cout << std::endl; + start_time = clock(); + Polygon_repair_2::Triangulation::Face_handle search_start; for (auto const& edge: edges_to_insert) { - pr.triangulation().odd_even_insert_constraint(edge.first, edge.second); - } + Polygon_repair_2::Triangulation::Vertex_handle va = pr.triangulation().insert(edge.first, search_start); + Polygon_repair_2::Triangulation::Vertex_handle vb = pr.triangulation().insert(edge.second, va->face()); // vb is likely close to va + pr.triangulation().odd_even_insert_constraint(va, vb); + search_start = vb->face(); + } std::cout << "Inserted constraints in "; + print_timer(start_time); + std::cout << std::endl; // std::cout << pr.triangulation().number_of_faces() << " faces in the triangulation" << std::endl; if (pr.triangulation().number_of_faces() > 0) { + start_time = clock(); pr.label_triangulation(); + std::cout << "Labelled in "; + print_timer(start_time); + std::cout << std::endl; + + start_time = clock(); pr.reconstruct_multipolygon(); + std::cout << "Reconstructed multipolygon in "; + print_timer(start_time); + std::cout << std::endl; } Multipolygon_with_holes_2 mp = pr.multipolygon(); // std::cout << mp << std::endl; @@ -108,9 +141,10 @@ int main(int argc, char* argv[]) { ofs << ""; ofs.close(); + std::cout << " ok" << std::endl; } ++current; -// if (current >= how_many) break; + if (current >= how_many) break; } return 0; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 7da830e93467..23e79c8706b2 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -136,7 +136,7 @@ class Polygon_repair_2 { } for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second); + t.odd_even_insert_constraint(edge.first, edge.second, search_start); } } @@ -163,7 +163,7 @@ class Polygon_repair_2 { } for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second); + t.odd_even_insert_constraint(edge.first, edge.second, search_start); } } @@ -192,7 +192,7 @@ class Polygon_repair_2 { } for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second); + t.odd_even_insert_constraint(edge.first, edge.second, search_start); } } @@ -386,7 +386,7 @@ class Polygon_repair_2 { Triangulation t; Multipolygon_with_holes_2 mp; int number_of_polygons, number_of_holes; - // std::vector> polygon_nesting, hole_nesting; + typename Triangulation::Face_handle search_start; }; } // namespace Polygon_repair_2 diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 098021d91ad5..4721aad31299 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -111,9 +111,17 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { // Add constraint from pa to pb using the odd-even rule void odd_even_insert_constraint(Point pa, Point pb) { Vertex_handle va = insert(pa); - Vertex_handle vb = insert(pb); + Vertex_handle vb = insert(pb, va->face()); // vb is likely close to va odd_even_insert_constraint(va, vb); } + + // Add constraint from pa to pb using the odd-even rule (updating search start face) + void odd_even_insert_constraint(Point pa, Point pb, Face_handle &f) { + Vertex_handle va = insert(pa, f); + Vertex_handle vb = insert(pb, va->face()); // vb is likely close to va + odd_even_insert_constraint(va, vb); + f = vb->face(); + } // Starts at an arbitrary interior face Interior_faces_iterator interior_faces_begin() { From a688d1affd3db6c242d7987f10648e4e169b05cb Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 4 Aug 2023 17:06:43 -0600 Subject: [PATCH 144/520] init bbox --- .../CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h index d8c9d47736ad..703e89b73e0a 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h @@ -95,6 +95,7 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { */ CGAL::Bbox_2 bounding_box() { Bbox_2 bbox; + if (m_mpwh.number_of_polygons() > 0) bbox = m_mpwh.polygons().front().outer_boundary().bbox(); for (auto const& pwh: m_mpwh.polygons()) { bbox += pwh.outer_boundary().bbox(); } From 26d25172e5b4919c14185af8e899b56bb2d377f3 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 8 Aug 2023 20:13:07 -0600 Subject: [PATCH 145/520] started with user manual --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 723a7d05053d..61b9085c7784 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -7,11 +7,33 @@ namespace CGAL { \cgalAutoToc \author Ken Arroyo Ohori -This chapter describes the ... +\section SectionPolygonRepair2_Introduction Introduction + +This package implements a polygon repair method based on a constrained triangulation \cgalCite{ledoux14prepair}. Starting from a possibly invalid input in the form of a polygon, polygon with holes or multipolygon with holes, the method performs a constrained triangulation of the input edges, applies a heuristic to label each triangle according to what it represents (exterior, polygon interior or hole), and reconstructs the polygon(s) represented by the triangulation. The method returns a valid output stored in a multipolygon with holes. Currently, the odd-even labeling heuristic is implemented in the package. \section SectionPolygonRepair2_Definitions Definitions -Section on definitions here ... +- A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ that is bounded by a cycle of linear edges, which is known as its outer boundary. This outer boundary should be simple, meaning that the interiors of its edges are pairwise disjoint and all of its vertices have a degree of two. It is thus topologically equivalent to a disk and is represented internally as the sequence of points of the vertices of its outer boundary. + +- A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is bounded by one outer boundary and zero or more inner boundaries, where each inner boundary represents a hole in the polygon. Considered by itself, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common edges), forming vertices with degrees larger than two at the tangential points. The interior of a polygon with holes should form a connected point set. Note that a valid polygon can also be represented as a valid polygon with zero holes. + +- A valid multipolygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is represented by a set of zero or more valid polygons with holes. The interiors of the polygons with holes should be pairwise disjoint, but their are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon with holes. + +\subsection SubsectionPolygonRepair2_Output Output + +The conditions listed above are sufficient to define valid polygons, polygons with holes and multipolygons with holes for most applications. However, in order to ensure unique deterministic output, the valid multipolygons with holes returned by the package conform to more strict criteria: + +- Adjacent collinear edges touching at vertices of degree two are merged +- The sequence of vertices representing a boundary starts from its lexicographically smallest vertex +- Outer boundaries are oriented counterclockwise and inner boundaries are oriented clockwise +- The inner boundaries of a polygon with holes are stored in lexicographic order +- The polygons with holes of a multipolygon with holes are also stored in lexicographic order + +\section SectionPolygonRepair2_Algorithm Algorithm + +The method interprets the input + +If the input is already valid, the method will return a valid output representing the same area \section SectionPolygonRepair2_Examples Examples From cab7deb47f410569ded234d4730531e6544ceee2 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 8 Aug 2023 20:21:41 -0600 Subject: [PATCH 146/520] fix citation --- Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 61b9085c7784..ece938dba1ad 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -9,7 +9,7 @@ namespace CGAL { \section SectionPolygonRepair2_Introduction Introduction -This package implements a polygon repair method based on a constrained triangulation \cgalCite{ledoux14prepair}. Starting from a possibly invalid input in the form of a polygon, polygon with holes or multipolygon with holes, the method performs a constrained triangulation of the input edges, applies a heuristic to label each triangle according to what it represents (exterior, polygon interior or hole), and reconstructs the polygon(s) represented by the triangulation. The method returns a valid output stored in a multipolygon with holes. Currently, the odd-even labeling heuristic is implemented in the package. +This package implements a polygon repair method based on a constrained triangulation \cgalCite{14cg}. Starting from a possibly invalid input in the form of a polygon, polygon with holes or multipolygon with holes, the method performs a constrained triangulation of the input edges, applies a heuristic to label each triangle according to what it represents (exterior, polygon interior or hole), and reconstructs the polygon(s) represented by the triangulation. The method returns a valid output stored in a multipolygon with holes. Currently, the odd-even labeling heuristic is implemented in the package. \section SectionPolygonRepair2_Definitions Definitions From ff1b359b1b59bdec813f5472a915986379ee6824 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 10 Aug 2023 22:19:42 -0600 Subject: [PATCH 147/520] more manual --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 130 ++++++++++++++---- 1 file changed, 107 insertions(+), 23 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index ece938dba1ad..0fc61840a73b 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -9,43 +9,127 @@ namespace CGAL { \section SectionPolygonRepair2_Introduction Introduction -This package implements a polygon repair method based on a constrained triangulation \cgalCite{14cg}. Starting from a possibly invalid input in the form of a polygon, polygon with holes or multipolygon with holes, the method performs a constrained triangulation of the input edges, applies a heuristic to label each triangle according to what it represents (exterior, polygon interior or hole), and reconstructs the polygon(s) represented by the triangulation. The method returns a valid output stored in a multipolygon with holes. Currently, the odd-even labeling heuristic is implemented in the package. +This package implements a polygon repair method based on a constrained +triangulation \cgalCite{14cg}. Starting from a possibly invalid input +in the form of a polygon, polygon with holes or multipolygon with holes, +the method performs a constrained triangulation of the input edges, labels +each triangle according to what it represents (exterior, polygon interior +or hole), and reconstructs the polygon(s) represented by the triangulation. +The method returns a valid output stored in a multipolygon with holes. -\section SectionPolygonRepair2_Definitions Definitions - -- A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ that is bounded by a cycle of linear edges, which is known as its outer boundary. This outer boundary should be simple, meaning that the interiors of its edges are pairwise disjoint and all of its vertices have a degree of two. It is thus topologically equivalent to a disk and is represented internally as the sequence of points of the vertices of its outer boundary. - -- A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is bounded by one outer boundary and zero or more inner boundaries, where each inner boundary represents a hole in the polygon. Considered by itself, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common edges), forming vertices with degrees larger than two at the tangential points. The interior of a polygon with holes should form a connected point set. Note that a valid polygon can also be represented as a valid polygon with zero holes. - -- A valid multipolygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is represented by a set of zero or more valid polygons with holes. The interiors of the polygons with holes should be pairwise disjoint, but their are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon with holes. +Different triangulation and labelling heuristics are possible, but +currently only the odd-even heuristic is implemented in the package. -\subsection SubsectionPolygonRepair2_Output Output +\section SectionPolygonRepair2_Definitions Definitions -The conditions listed above are sufficient to define valid polygons, polygons with holes and multipolygons with holes for most applications. However, in order to ensure unique deterministic output, the valid multipolygons with holes returned by the package conform to more strict criteria: +- A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ +that is bounded by a cycle of linear edges, which is known as its +outer boundary. This outer boundary should be simple, +meaning that the interiors of its edges are pairwise disjoint and all of +its vertices have a degree of two. It is thus topologically equivalent to a +disk and is represented internally as the sequence of points at the common +end points of the edges around its outer boundary. + +- A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ +that is bounded by one outer boundary and zero or more inner boundaries, +where each inner boundary represents a hole in the polygon. Considered by +itself, each boundary should be simple. The different boundaries of a polygon +are allowed to intersect tangentially at their common vertices (with no common +edges), forming vertices with degrees larger than two at the tangential points. +The interior of a polygon with holes should form a connected point set. +Note that a valid polygon can also be represented as a valid polygon with +holes (where the number of holes is zero). + +- A valid multipolygon with holes is a point set in \f$ \mathbb{R}^2\f$ +that is represented by a set of zero or more valid polygons with holes. +The interiors of the polygons with holes should be pairwise disjoint, but they +are allowed to intersect tangentially at their common vertices. Note that +a valid polygon with holes can also be represented as a valid multipolygon +with holes. + +FIGURE + +\subsection SubsectionPolygonRepair2_Output Stricter conditions for output + +The conditions listed above are sufficient to define valid polygons, polygons +with holes and multipolygons with holes for most applications. However, in +order to ensure unique deterministic output from the repair algorithm, +the valid multipolygons with holes returned by the package conform to more +strict criteria: - Adjacent collinear edges touching at vertices of degree two are merged -- The sequence of vertices representing a boundary starts from its lexicographically smallest vertex -- Outer boundaries are oriented counterclockwise and inner boundaries are oriented clockwise -- The inner boundaries of a polygon with holes are stored in lexicographic order -- The polygons with holes of a multipolygon with holes are also stored in lexicographic order +- The sequence of vertices representing a boundary starts from its +lexicographically smallest vertex +- Outer boundaries are oriented counterclockwise and inner boundaries are +oriented clockwise +- The inner boundaries of a polygon with holes are stored in lexicographic +order +- The polygons with holes of a multipolygon with holes are also stored in +lexicographic order \section SectionPolygonRepair2_Algorithm Algorithm -The method interprets the input - -If the input is already valid, the method will return a valid output representing the same area +Broadly, the algorithm consists of three steps: + +-# Constrained triangulation: the edges in the polygon, polygon with +holes or multipolygon with holes are added as constraints in the triangulation. +-# Labeling of the faces: sets of faces forming contiguous regions that +are accessible from each other by following adjacency relationships without +passing through a constrained edge are labeled with ids according to +what they represent (exterior, polygon interior or hole). The faces iteratively +adjacent to the infinite face without passing through a constrained edge are +labeled as the exterior. +-# Reconstruction of the multipolygon: each boundary of each polygon +with holes is reconstructed by finding a polygon interior triangle that is +adjacent to the exterior or a hole, then following the boundary in an +orientation that is counterclockwise for outer boundaries and clockwise for +inner boundaries. This orientation and the polygon interior id is used to +determine what polygon with holes it belongs to and whether it is an outer or +an inner boundary. + +FIGURE + +If the input is already valid, the method will return a valid output representing +the same area. However, the output might be different in order to conform to the +stricter conditions to generate deterministic output (see +\ref SubsectionPolygonRepair2_Output). + +Also, it is worth noting that even the repair of a single polygon without holes +but with self-intersections can result in a multipolygon with holes. This is why +the repair function will always return a multipolygon with holes. The user can +then check whether it consists of a single polygon with holes, and whether this +polygon with holes has zero holes and treat it appropriately. + +\subsection SubsectionPolygonRepair2_OddEven Odd-even repair + +The odd-even repair heuristic results in areas that are alternatingly assigned +as polygon interiors and exterior/holes each time that an input edge is passed. +It doesn't distinguish between edges that are part of outer boundaries or inner +boundaries. + +FIGURE + +In order to generate consistent output, the odd-even heuristic requires it to be +applied to both edges and faces. For the edges, this heuristic means that only +the line segments that are present an odd number of times in the input will be +constrained edges in the triangulation. For the face labels, this heuristic means +that after the exterior faces have been labeled, the other faces will be +alternatingly labeled with polygon interior and hole ids every time that a +constrained edge is passed. + +FIGURE \section SectionPolygonRepair2_Examples Examples -\subsection SubsectionPolygonRepair2_FirstExample First Example +\subsection SubsectionPolygonRepair2_Multipolygon Defining and traversing a multipolygon + +\subsection SubsectionPolygonRepair2_WKTDraw Reading well-known text and drawing a multipolygon -The following example shows ... +\subsection SubsectionPolygonRepair2_Repair Repairing -\cgalExample{Polygon_repair_2/repair_polygon_2.cpp} +\subsection SubsectionPolygonRepair2_Repair Repairing (hidden API)? -\cgalFigureBegin{figPck,bench.png} -Left: ... -\cgalFigureEnd +\section SectionPolygonRepair2_Performance Performance */ } /* namespace CGAL */ From 88a4322de13033424c622238d6f6ed8ac3811d6d Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 11 Aug 2023 17:10:19 -0600 Subject: [PATCH 148/520] insert all vertices first, then all constrained edges --- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 107 +++++++++++++++--- ...riangulation_with_odd_even_constraints_2.h | 8 -- 2 files changed, 91 insertions(+), 24 deletions(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 23e79c8706b2..0da8a4e1259c 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -125,74 +125,149 @@ class Polygon_repair_2 { // Add edges of the polygon to the triangulation void add_to_triangulation(const Polygon_2& polygon) { std::unordered_set, - boost::hash>> edges_to_insert; + boost::hash>> unique_edges; + // Get unique edges for (auto const& edge: polygon.edges()) { if (edge.source() == edge.target()) continue; std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); - auto inserted = edges_to_insert.insert(pair); - if (!inserted.second) edges_to_insert.erase(inserted.first); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); } + // Insert vertices + std::unordered_map vertices; + std::vector> edges_to_insert; + edges_to_insert.reserve(unique_edges.size()); + for (auto const& edge: unique_edges) { + typename Triangulation::Vertex_handle first_vertex, second_vertex; + typename std::unordered_map::const_iterator found = vertices.find(edge.first); + if (found == vertices.end()) { + first_vertex = t.insert(edge.first, search_start); + vertices[edge.first] = first_vertex; + } else { + first_vertex = found->second; + } search_start = first_vertex->face(); + found = vertices.find(edge.second); + if (found == vertices.end()) { + second_vertex = t.insert(edge.second, search_start); + vertices[edge.second] = second_vertex; + } else { + second_vertex = found->second; + } search_start = second_vertex->face(); + edges_to_insert.emplace_back(first_vertex, second_vertex); + } + + // Insert edges for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second, search_start); + t.odd_even_insert_constraint(edge.first, edge.second); } } // Add edges of the polygon to the triangulation void add_to_triangulation(const Polygon_with_holes_2& polygon) { std::unordered_set, - boost::hash>> edges_to_insert; + boost::hash>> unique_edges; + // Get unique edges for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) continue; std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); - auto inserted = edges_to_insert.insert(pair); - if (!inserted.second) edges_to_insert.erase(inserted.first); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); } for (auto const& hole: polygon.holes()) { for (auto const& edge: hole.edges()) { if (edge.source() == edge.target()) continue; std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); - auto inserted = edges_to_insert.insert(pair); - if (!inserted.second) edges_to_insert.erase(inserted.first); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); } } + // Insert vertices + std::unordered_map vertices; + std::vector> edges_to_insert; + edges_to_insert.reserve(unique_edges.size()); + for (auto const& edge: unique_edges) { + typename Triangulation::Vertex_handle first_vertex, second_vertex; + typename std::unordered_map::const_iterator found = vertices.find(edge.first); + if (found == vertices.end()) { + first_vertex = t.insert(edge.first, search_start); + vertices[edge.first] = first_vertex; + } else { + first_vertex = found->second; + } search_start = first_vertex->face(); + found = vertices.find(edge.second); + if (found == vertices.end()) { + second_vertex = t.insert(edge.second, search_start); + vertices[edge.second] = second_vertex; + } else { + second_vertex = found->second; + } search_start = second_vertex->face(); + edges_to_insert.emplace_back(first_vertex, second_vertex); + } + + // Insert edges for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second, search_start); + t.odd_even_insert_constraint(edge.first, edge.second); } } // Add edges of the polygon to the triangulation void add_to_triangulation(const Multipolygon_with_holes_2& multipolygon) { std::unordered_set, - boost::hash>> edges_to_insert; + boost::hash>> unique_edges; + // Get unique edges for (auto const& polygon: multipolygon.polygons()) { for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) continue; std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); - auto inserted = edges_to_insert.insert(pair); - if (!inserted.second) edges_to_insert.erase(inserted.first); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); } for (auto const& hole: polygon.holes()) { for (auto const& edge: hole.edges()) { if (edge.source() == edge.target()) continue; std::pair pair = (edge.source() < edge.target())? std::make_pair(edge.source(), edge.target()) : std::make_pair(edge.target(), edge.source()); - auto inserted = edges_to_insert.insert(pair); - if (!inserted.second) edges_to_insert.erase(inserted.first); + auto inserted = unique_edges.insert(pair); + if (!inserted.second) unique_edges.erase(inserted.first); } } } + // Insert vertices + std::unordered_map vertices; + std::vector> edges_to_insert; + edges_to_insert.reserve(unique_edges.size()); + for (auto const& edge: unique_edges) { + typename Triangulation::Vertex_handle first_vertex, second_vertex; + typename std::unordered_map::const_iterator found = vertices.find(edge.first); + if (found == vertices.end()) { + first_vertex = t.insert(edge.first, search_start); + vertices[edge.first] = first_vertex; + } else { + first_vertex = found->second; + } search_start = first_vertex->face(); + found = vertices.find(edge.second); + if (found == vertices.end()) { + second_vertex = t.insert(edge.second, search_start); + vertices[edge.second] = second_vertex; + } else { + second_vertex = found->second; + } search_start = second_vertex->face(); + edges_to_insert.emplace_back(first_vertex, second_vertex); + } + + // Insert edges for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second, search_start); + t.odd_even_insert_constraint(edge.first, edge.second); } } diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h index 4721aad31299..f496d1154092 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h @@ -114,14 +114,6 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { Vertex_handle vb = insert(pb, va->face()); // vb is likely close to va odd_even_insert_constraint(va, vb); } - - // Add constraint from pa to pb using the odd-even rule (updating search start face) - void odd_even_insert_constraint(Point pa, Point pb, Face_handle &f) { - Vertex_handle va = insert(pa, f); - Vertex_handle vb = insert(pb, va->face()); // vb is likely close to va - odd_even_insert_constraint(va, vb); - f = vb->face(); - } // Starts at an arbitrary interior face Interior_faces_iterator interior_faces_begin() { From 59f2cb0901ab0d062d258e12a4077611c4ff8f2a Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 11 Aug 2023 17:25:21 -0600 Subject: [PATCH 149/520] renamed functions with repair method --- .../examples/Polygon_repair_2/clipart.cpp | 10 +++---- .../Polygon_repair_2/nasty_polygons.cpp | 4 +-- .../write_labeled_triangulation.cpp | 6 ++--- .../CGAL/Polygon_repair_2/Polygon_repair_2.h | 26 +++++++++---------- .../Polygon_repair_2/draw_test_polygons.cpp | 4 +-- .../repair_polygon_2_test.cpp | 4 +-- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp b/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp index 5549158ae13e..421e68e7334e 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp @@ -31,14 +31,14 @@ int main(int argc, char* argv[]) { clock_t start_time; for (const auto& file: std::filesystem::directory_iterator(folder_in)) { - + if (file.path().filename().extension() != ".obj") continue; std::cout << "Reading " << file.path().filename() << "..."; // if (std::filesystem::exists(folder_out + "/" + std::string(file.path().stem()) + ".svg")) { // std::cout << " skipped: already processed" << std::endl; // continue; // } - + start_time = clock(); Polygon_repair_2 pr; std::vector vertices; @@ -94,11 +94,11 @@ int main(int argc, char* argv[]) { if (pr.triangulation().number_of_faces() > 0) { start_time = clock(); - pr.label_triangulation(); + pr.label_triangulation_odd_even(); std::cout << "Labelled in "; print_timer(start_time); std::cout << std::endl; - + start_time = clock(); pr.reconstruct_multipolygon(); std::cout << "Reconstructed multipolygon in "; @@ -143,7 +143,7 @@ int main(int argc, char* argv[]) { ofs.close(); std::cout << " ok" << std::endl; } ++current; - + if (current >= how_many) break; } diff --git a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp index 3170158ed334..97ed92f25150 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp @@ -28,8 +28,8 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p; CGAL::IO::read_polygon_WKT(iss, p); Polygon_repair_2 pr; - pr.add_to_triangulation(p); - pr.label_triangulation(); + pr.add_to_triangulation_odd_even(p); + pr.label_triangulation_odd_even(); pr.reconstruct_multipolygon(); Multipolygon_with_holes_2 rmp = pr.multipolygon(); std::ostringstream oss; diff --git a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp index 29ee0acfa7d3..d7e8333d075f 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp @@ -34,12 +34,12 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p; if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); - } pr.add_to_triangulation(p); + } pr.add_to_triangulation_odd_even(p); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); - pr.add_to_triangulation(mp); - } pr.label_triangulation(); + pr.add_to_triangulation_odd_even(mp); + } pr.label_triangulation_odd_even(); // ofs << std::fixed; // ofs << std::setprecision(15); diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h index 0da8a4e1259c..85ef9d1673de 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h @@ -34,11 +34,11 @@ class Polygon_repair_2; /// \ingroup PkgPolygonRepair2Functions /// Repair a polygon without holes template -Multipolygon_with_holes_2 repair(const Polygon_2& p) { +Multipolygon_with_holes_2 repair_odd_even(const Polygon_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; - pr.add_to_triangulation(p); + pr.add_to_triangulation_odd_even(p); if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation(); + pr.label_triangulation_odd_even(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -46,11 +46,11 @@ Multipolygon_with_holes_2 repair(const Polygon_2 -Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p) { +Multipolygon_with_holes_2 repair_odd_even(const Polygon_with_holes_2& p) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; - pr.add_to_triangulation(p); + pr.add_to_triangulation_odd_even(p); if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation(); + pr.label_triangulation_odd_even(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -58,11 +58,11 @@ Multipolygon_with_holes_2 repair(const Polygon_with_ho /// \ingroup PkgPolygonRepair2Functions /// Repair a multipolygon with holes template -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp) { +Multipolygon_with_holes_2 repair_odd_even(const Multipolygon_with_holes_2& mp) { CGAL::Polygon_repair_2::Polygon_repair_2 pr; - pr.add_to_triangulation(mp); + pr.add_to_triangulation_odd_even(mp); if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation(); + pr.label_triangulation_odd_even(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -123,7 +123,7 @@ class Polygon_repair_2 { /// @{ // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_2& polygon) { + void add_to_triangulation_odd_even(const Polygon_2& polygon) { std::unordered_set, boost::hash>> unique_edges; @@ -166,7 +166,7 @@ class Polygon_repair_2 { } // Add edges of the polygon to the triangulation - void add_to_triangulation(const Polygon_with_holes_2& polygon) { + void add_to_triangulation_odd_even(const Polygon_with_holes_2& polygon) { std::unordered_set, boost::hash>> unique_edges; @@ -218,7 +218,7 @@ class Polygon_repair_2 { } // Add edges of the polygon to the triangulation - void add_to_triangulation(const Multipolygon_with_holes_2& multipolygon) { + void add_to_triangulation_odd_even(const Multipolygon_with_holes_2& multipolygon) { std::unordered_set, boost::hash>> unique_edges; @@ -302,7 +302,7 @@ class Polygon_repair_2 { } // Label triangles in triangulation - void label_triangulation() { + void label_triangulation_odd_even() { // Simplify collinear edges (gets rid of order dependency) for (auto vertex: t.all_vertex_handles()) { diff --git a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp index cd3330a3e207..8b6d05807446 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp @@ -35,12 +35,12 @@ int main(int argc, char* argv[]) { if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); } CGAL::draw(p); - rmp = CGAL::Polygon_repair_2::repair(p); + rmp = CGAL::Polygon_repair_2::repair_odd_even(p); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); CGAL::draw(mp); - rmp = CGAL::Polygon_repair_2::repair(mp); + rmp = CGAL::Polygon_repair_2::repair_odd_even(mp); } std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp index 50bbe4a0813b..bc3e937d9dec 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp @@ -31,11 +31,11 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p; if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); - } rmp = CGAL::Polygon_repair_2::repair(p); + } rmp = CGAL::Polygon_repair_2::repair_odd_even(p); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); - rmp = CGAL::Polygon_repair_2::repair(mp); + rmp = CGAL::Polygon_repair_2::repair_odd_even(mp); } std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); From 00832504801616c104e24e224ec793ae0ee2e487 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 11 Aug 2023 20:10:59 -0600 Subject: [PATCH 150/520] simple multipolygon example --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 7 ++++- .../Polygon_repair_2/multipolygon.cpp | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 0fc61840a73b..9300210f5255 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -121,7 +121,12 @@ FIGURE \section SectionPolygonRepair2_Examples Examples -\subsection SubsectionPolygonRepair2_Multipolygon Defining and traversing a multipolygon +\subsection SubsectionPolygonRepair2_Multipolygon The multipolygon with holes class + +The following example shows the creation of a multipolygon with holes and the traversal +of the polygons in it. + +\cgalExample{Polygon_repair_2/multipolygon.cpp} \subsection SubsectionPolygonRepair2_WKTDraw Reading well-known text and drawing a multipolygon diff --git a/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp b/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp new file mode 100644 index 000000000000..aa77c84edf64 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp @@ -0,0 +1,29 @@ +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int main(int argc, char* argv[]) { + + Point_2 p1_outer[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; + Point_2 p1_inner[] = {Point_2(0.2,0.2), Point_2(0.8,0.2), Point_2(0.8,0.8), Point_2(0.2,0.8)}; + + Polygon_with_holes_2 p1(Polygon_2(p1_outer, p1_outer+4)); + Polygon_2 h(p1_inner, p1_inner+4); + p1.add_hole(h); + + Point_2 p2_outer[] = {Point_2(0.4,0.4), Point_2(0.6,0.4), Point_2(0.6,0.6), Point_2(0.4,0.6)}; + Polygon_with_holes_2 p2(Polygon_2(p2_outer, p2_outer+4)); + + Multipolygon_with_holes_2 mp; + mp.add_polygon(p1); + mp.add_polygon(p2); + + for (auto const& p: mp.polygons()) { + std::cout << p << std::endl; + } +} \ No newline at end of file From 5a9e002874bf379f756d8fefbe37586220cbc476 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sun, 20 Aug 2023 15:35:09 +0200 Subject: [PATCH 151/520] Update Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt Co-authored-by: Sebastien Loriot --- Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 9300210f5255..70aed0a15555 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -32,8 +32,7 @@ end points of the edges around its outer boundary. - A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is bounded by one outer boundary and zero or more inner boundaries, -where each inner boundary represents a hole in the polygon. Considered by -itself, each boundary should be simple. The different boundaries of a polygon +where each inner boundary represents a hole in the polygon. Considered independently, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common edges), forming vertices with degrees larger than two at the tangential points. The interior of a polygon with holes should form a connected point set. From 634fb06f1dfad9ff44f53900b24b8b03b0c2648b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sun, 20 Aug 2023 15:35:22 +0200 Subject: [PATCH 152/520] Update Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt Co-authored-by: Sebastien Loriot --- Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 70aed0a15555..33e5f8ea5702 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -48,7 +48,7 @@ with holes. FIGURE -\subsection SubsectionPolygonRepair2_Output Stricter conditions for output +\subsection SubsectionPolygonRepair2_Output Stricter Conditions for Output The conditions listed above are sufficient to define valid polygons, polygons with holes and multipolygons with holes for most applications. However, in From 4ef71dcfd08d986521e18e0e95da95ee938e47f8 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sun, 20 Aug 2023 15:35:37 +0200 Subject: [PATCH 153/520] Update Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h Co-authored-by: Sebastien Loriot --- .../include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h index a584d088cab8..8d13989b4e82 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h @@ -89,7 +89,7 @@ class Multipolygon_with_holes_2 { }; /*! -This operator exports a multipolygon with holes to the output stream `os`. +exports a multipolygon with holes to the output stream `os`. An \ascii and a binary format exist. The format can be selected with the \cgal modifiers for streams, `set_ascii_mode()` and `set_binary_mode()`, From 5ee8cda82012118c28657b572cd1780711450224 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sun, 20 Aug 2023 16:02:47 +0200 Subject: [PATCH 154/520] draw multipolygon doc --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 9300210f5255..1666dbc1e8e7 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -128,7 +128,17 @@ of the polygons in it. \cgalExample{Polygon_repair_2/multipolygon.cpp} -\subsection SubsectionPolygonRepair2_WKTDraw Reading well-known text and drawing a multipolygon +\subsection SubsectionPolygonRepair2_WKTDraw Draw a multipolygon + +A multipolygon can be visualized by calling the \link PkgPolygonRepair2Ref CGAL::draw

() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window (versions for polygons and polygons with holes also exist, cf. \link PkgDrawPolygon2 CGAL::draw() \endlink and \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink). + +\cgalExample{Polygon_repair_2/draw_multipolygon.cpp} + +This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined. +Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition `CGAL_USE_BASIC_VIEWER`. + +FIGURE +Result of the run of the draw_polygon program. A window shows the multipolygon and allows to navigate through the scene. \subsection SubsectionPolygonRepair2_Repair Repairing From 72c38e46fc8469c489a9b32311aefb6ca8b23c12 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 21 Aug 2023 11:25:43 +0200 Subject: [PATCH 155/520] rest of draw_multipolygon example --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 28 +++++++++++++------ .../examples/Polygon_repair_2/CMakeLists.txt | 8 ++++-- .../Polygon_repair_2/draw_multipolygon.cpp | 18 ++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 158d5e1c2f75..0ccbde4e058b 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -32,7 +32,8 @@ end points of the edges around its outer boundary. - A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is bounded by one outer boundary and zero or more inner boundaries, -where each inner boundary represents a hole in the polygon. Considered independently, each boundary should be simple. The different boundaries of a polygon +where each inner boundary represents a hole in the polygon. Considered +independently, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common edges), forming vertices with degrees larger than two at the tangential points. The interior of a polygon with holes should form a connected point set. @@ -44,7 +45,7 @@ that is represented by a set of zero or more valid polygons with holes. The interiors of the polygons with holes should be pairwise disjoint, but they are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon -with holes. +with holes. For simplicity, we call multipolygons with holes as multipolygons. FIGURE @@ -129,19 +130,30 @@ of the polygons in it. \subsection SubsectionPolygonRepair2_WKTDraw Draw a multipolygon -A multipolygon can be visualized by calling the \link PkgPolygonRepair2Ref CGAL::draw

() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window (versions for polygons and polygons with holes also exist, cf. \link PkgDrawPolygon2 CGAL::draw() \endlink and \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink). +A multipolygon can be visualized by calling the \link PkgPolygonRepair2Ref +CGAL::draw() \endlink function as shown in the following example. +This function opens a new window showing the given polygon. A call to this function +is blocking, that is the program continues as soon as the user closes the window. +Versions for polygons and polygons with holes also exist, cf. \link PkgDrawPolygon2 +CGAL::draw

() \endlink and \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink. + +The multipolygon shown in this example is created using the well-known text (WKT) reader, +which can read and write multipolygons. \cgalExample{Polygon_repair_2/draw_multipolygon.cpp} -This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined. -Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition `CGAL_USE_BASIC_VIEWER`. +This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` +is defined. Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` +and add the definition `CGAL_USE_BASIC_VIEWER`. -FIGURE -Result of the run of the draw_polygon program. A window shows the multipolygon and allows to navigate through the scene. +\cgalFigureBegin{draw_multipolygon, draw_multipolygon.png} +Result of the run of the draw_multipolygon program. A window shows the multipolygon +and allows to navigate through the scene. +\cgalFigureEnd \subsection SubsectionPolygonRepair2_Repair Repairing -\subsection SubsectionPolygonRepair2_Repair Repairing (hidden API)? +\subsection SubsectionPolygonRepair2_RepairHiddenApi Step by step repairing \section SectionPolygonRepair2_Performance Performance diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt index b7fedd3936a6..3c6c418ecdee 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt @@ -6,7 +6,7 @@ project(Polygon_repair_2_Examples) set(CMAKE_CXX_STANDARD 17) -find_package(CGAL REQUIRED) +find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) # create a target per cppfile file( @@ -15,4 +15,8 @@ file( ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") -endforeach() \ No newline at end of file +endforeach() + +if(CGAL_Qt5_FOUND) + target_link_libraries(draw_multipolygon PUBLIC CGAL::CGAL_Basic_viewer) +endif() \ No newline at end of file diff --git a/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp b/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp new file mode 100644 index 000000000000..2e0e77128139 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp @@ -0,0 +1,18 @@ +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int main(int argc, char* argv[]) { + std::ifstream in("data/nesting.wkt"); + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(in, mp); + CGAL::draw(mp); + + return 0; +} From 6db7abbb6c8d0403444a53e080950e7055ae2707 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 21 Aug 2023 11:36:56 +0200 Subject: [PATCH 156/520] put wkt directly in example --- Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt | 6 ++++-- .../examples/Polygon_repair_2/draw_multipolygon.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 0ccbde4e058b..3953d64aa0e2 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -32,7 +32,7 @@ end points of the edges around its outer boundary. - A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is bounded by one outer boundary and zero or more inner boundaries, -where each inner boundary represents a hole in the polygon. Considered +where each inner boundary represents a hole in the polygon. Considered independently, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common edges), forming vertices with degrees larger than two at the tangential points. @@ -151,7 +151,9 @@ Result of the run of the draw_multipolygon program. A window shows the multipoly and allows to navigate through the scene. \cgalFigureEnd -\subsection SubsectionPolygonRepair2_Repair Repairing +\subsection SubsectionPolygonRepair2_Repair Repairing a (multi)polygon + +\cgalExample{Polygon_repair_2/repair_polygon_2.cpp} \subsection SubsectionPolygonRepair2_RepairHiddenApi Step by step repairing diff --git a/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp b/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp index 2e0e77128139..d7ec18926ffa 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -9,9 +9,10 @@ using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; int main(int argc, char* argv[]) { - std::ifstream in("data/nesting.wkt"); + std::string wkt = "MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3)))"; + std::istringstream iss(wkt); Multipolygon_with_holes_2 mp; - CGAL::IO::read_multi_polygon_WKT(in, mp); + CGAL::IO::read_multi_polygon_WKT(iss, mp); CGAL::draw(mp); return 0; From 55d2fe82b3fcf9fefecdcc7a00a9b881f3e76d7d Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 21 Aug 2023 11:40:43 +0200 Subject: [PATCH 157/520] c++17 from master --- Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt | 2 -- Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt | 2 -- 2 files changed, 4 deletions(-) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt index 3c6c418ecdee..417109e78275 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt @@ -4,8 +4,6 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Examples) -set(CMAKE_CXX_STANDARD 17) - find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) # create a target per cppfile diff --git a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt index 3f145cb62154..365fba1a84c2 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt @@ -4,8 +4,6 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_2_Tests) -set(CMAKE_CXX_STANDARD 17) - find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) # create a target per cppfile From b9ffb31bf67fffef4cbf9641b1dcd74470ba4904 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 21 Aug 2023 12:44:08 +0200 Subject: [PATCH 158/520] clipart in test --- Polygon_repair_2/{examples => test}/Polygon_repair_2/clipart.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Polygon_repair_2/{examples => test}/Polygon_repair_2/clipart.cpp (100%) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp b/Polygon_repair_2/test/Polygon_repair_2/clipart.cpp similarity index 100% rename from Polygon_repair_2/examples/Polygon_repair_2/clipart.cpp rename to Polygon_repair_2/test/Polygon_repair_2/clipart.cpp From 96007f246d57797bf5ef6a0e2761dcc7246320da Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 21 Aug 2023 12:45:35 +0200 Subject: [PATCH 159/520] data for example --- Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt | 1 + Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt diff --git a/Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt b/Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt new file mode 100644 index 000000000000..1e86cf795061 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt @@ -0,0 +1 @@ +POLYGON((0 0,1 0,1 1,0 1,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25,0.25 0.75,0 1,0 0)) \ No newline at end of file diff --git a/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp b/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp index aa77c84edf64..cc9b5df84179 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp @@ -26,4 +26,6 @@ int main(int argc, char* argv[]) { for (auto const& p: mp.polygons()) { std::cout << p << std::endl; } + + return 0; } \ No newline at end of file From 28cfa7e4dfdada53e9f6eb10422df1ed501500d2 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Mon, 21 Aug 2023 16:43:48 +0200 Subject: [PATCH 160/520] step by step example --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 26 ++++-- .../doc/Polygon_repair_2/examples.txt | 3 + .../Polygon_repair_2/data/nesting-spike.wkt | 1 + .../Polygon_repair_2/repair_polygon_2.cpp | 27 +++++++ .../write_labeled_triangulation.cpp | 80 +++++++------------ 5 files changed, 78 insertions(+), 59 deletions(-) create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/data/nesting-spike.wkt create mode 100644 Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index 3953d64aa0e2..e3ecf4f287d1 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -45,7 +45,7 @@ that is represented by a set of zero or more valid polygons with holes. The interiors of the polygons with holes should be pairwise disjoint, but they are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon -with holes. For simplicity, we call multipolygons with holes as multipolygons. +with holes. FIGURE @@ -89,6 +89,9 @@ an inner boundary. FIGURE +For the purposes of the repair operation, the input polygon, polygon with holes +or multipolygon is merely used as a container of input line segments. + If the input is already valid, the method will return a valid output representing the same area. However, the output might be different in order to conform to the stricter conditions to generate deterministic output (see @@ -130,33 +133,40 @@ of the polygons in it. \subsection SubsectionPolygonRepair2_WKTDraw Draw a multipolygon -A multipolygon can be visualized by calling the \link PkgPolygonRepair2Ref +A multipolygon with holes can be visualized by calling the \link PkgPolygonRepair2Ref CGAL::draw() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window. Versions for polygons and polygons with holes also exist, cf. \link PkgDrawPolygon2 CGAL::draw

() \endlink and \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink. -The multipolygon shown in this example is created using the well-known text (WKT) reader, -which can read and write multipolygons. +The multipolygon with holes shown in this example is created using the well-known text +(WKT) reader, which can read and write multipolygons with holes. \cgalExample{Polygon_repair_2/draw_multipolygon.cpp} -This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` -is defined. Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` -and add the definition `CGAL_USE_BASIC_VIEWER`. +This function requires `CGAL_Qt5`, and is only available if the macro +`CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target +`CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition +`CGAL_USE_BASIC_VIEWER`. \cgalFigureBegin{draw_multipolygon, draw_multipolygon.png} Result of the run of the draw_multipolygon program. A window shows the multipolygon -and allows to navigate through the scene. +with holes and allows to navigate through the scene. \cgalFigureEnd \subsection SubsectionPolygonRepair2_Repair Repairing a (multi)polygon +\cgalFigureBegin{bridge_edge, bridge_edge.png} +(a) Before repair (b) After repair +\cgalFigureEnd + \cgalExample{Polygon_repair_2/repair_polygon_2.cpp} \subsection SubsectionPolygonRepair2_RepairHiddenApi Step by step repairing +\cgalExample{Polygon_repair_2/write_labeled_triangulation.cpp} + \section SectionPolygonRepair2_Performance Performance */ diff --git a/Polygon_repair_2/doc/Polygon_repair_2/examples.txt b/Polygon_repair_2/doc/Polygon_repair_2/examples.txt index c3d2195f5d89..b1318b5691ee 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/examples.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/examples.txt @@ -1,3 +1,6 @@ /*! +\example Polygon_repair_2/multipolygon.cpp +\example Polygon_repair_2/draw_multipolygon.cpp \example Polygon_repair_2/repair_polygon_2.cpp +\example Polygon_repair_2/write_labeled_triangulation.cpp */ diff --git a/Polygon_repair_2/examples/Polygon_repair_2/data/nesting-spike.wkt b/Polygon_repair_2/examples/Polygon_repair_2/data/nesting-spike.wkt new file mode 100644 index 000000000000..e0df5a0dc7d8 --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/data/nesting-spike.wkt @@ -0,0 +1 @@ +MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.9 0.1,0.9 0.9,0.1 0.9,0.1 0.1)),((0.2 0.2,2 1,0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.7 0.3,0.7 0.7,0.3 0.7,0.3 0.3))) \ No newline at end of file diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp new file mode 100644 index 000000000000..89b4580519dd --- /dev/null +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; + +int main(int argc, char* argv[]) { + std::ifstream in("data/bridge-edge.wkt"); + Polygon_with_holes_2 pin; + CGAL::IO::read_polygon_WKT(in, pin); + + Multipolygon_with_holes_2 mp = CGAL::Polygon_repair_2::repair_odd_even(pin); + if (mp.number_of_polygons() > 1) { + CGAL::IO::write_multi_polygon_WKT(std::cout, mp); + } else { + CGAL::IO::write_polygon_WKT(std::cout, mp.polygons()[0]); + } + + return 0; +} diff --git a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp index d7e8333d075f..3dde85eb920a 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include #include #include @@ -16,68 +14,48 @@ using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; int main(int argc, char* argv[]) { - // std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); - // std::ofstream ofs("/Users/ken/Downloads/2.geojson"); + std::ifstream ifs("data/nesting-spike.wkt"); - // std::ifstream ifs("/Users/ken/Downloads/2018418.wkt"); - // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(ifs, mp); - std::ifstream ifs("../../test/Polygon_repair_2/data/in/nesting-spike.wkt"); - std::ofstream ofs("/Users/ken/Downloads/triangulation.geojson"); - - std::string in; - std::getline(ifs, in); - std::istringstream iss(in); Polygon_repair_2 pr; + pr.add_to_triangulation_odd_even(mp); + pr.label_triangulation_odd_even(); - if (in.find("POLYGON") == 0) { - Polygon_with_holes_2 p; - if (in != "POLYGON()") { // maybe should be checked in WKT reader - CGAL::IO::read_polygon_WKT(iss, p); - } pr.add_to_triangulation_odd_even(p); - } else if (in.find("MULTIPOLYGON") == 0) { - Multipolygon_with_holes_2 mp; - CGAL::IO::read_multi_polygon_WKT(iss, mp); - pr.add_to_triangulation_odd_even(mp); - } pr.label_triangulation_odd_even(); - - // ofs << std::fixed; - // ofs << std::setprecision(15); - - ofs << "{" << std::endl; - ofs << "\t\"type\": \"FeatureCollection\"," << std::endl; - ofs << "\t\"features\": [" << std::endl; + std::cout << "{" << std::endl; + std::cout << "\t\"type\": \"FeatureCollection\"," << std::endl; + std::cout << "\t\"features\": [" << std::endl; for (Polygon_repair_2::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); face != pr.triangulation().finite_faces_end(); ++face) { - ofs << "\t\t{" << std::endl; - ofs << "\t\t\t\"type\": \"Feature\"," << std::endl; - ofs << "\t\t\t\"properties\": {" << std::endl; - ofs << "\t\t\t\t\"label\": " << face->label() << std::endl; - ofs << "\t\t\t}," << std::endl; - ofs << "\t\t\t\"geometry\": {" << std::endl; - ofs << "\t\t\t\t\"type\": \"Polygon\"," << std::endl; - ofs << "\t\t\t\t\"coordinates\": [" << std::endl; - ofs << "\t\t\t\t\t[" << std::endl; - ofs << "\t\t\t\t\t\t[" << face->vertex(0)->point().x() << ", " << face->vertex(0)->point().y() << "]," << std::endl; - ofs << "\t\t\t\t\t\t[" << face->vertex(1)->point().x() << ", " << face->vertex(1)->point().y() << "]," << std::endl; - ofs << "\t\t\t\t\t\t[" << face->vertex(2)->point().x() << ", " << face->vertex(2)->point().y() << "]" << std::endl; - ofs << "\t\t\t\t\t]" << std::endl; - ofs << "\t\t\t\t]" << std::endl; - ofs << "\t\t\t}" << std::endl; - ofs << "\t\t}"; + std::cout << "\t\t{" << std::endl; + std::cout << "\t\t\t\"type\": \"Feature\"," << std::endl; + std::cout << "\t\t\t\"properties\": {" << std::endl; + std::cout << "\t\t\t\t\"label\": " << face->label() << std::endl; + std::cout << "\t\t\t}," << std::endl; + std::cout << "\t\t\t\"geometry\": {" << std::endl; + std::cout << "\t\t\t\t\"type\": \"Polygon\"," << std::endl; + std::cout << "\t\t\t\t\"coordinates\": [" << std::endl; + std::cout << "\t\t\t\t\t[" << std::endl; + std::cout << "\t\t\t\t\t\t[" << face->vertex(0)->point().x() << ", " << face->vertex(0)->point().y() << "]," << std::endl; + std::cout << "\t\t\t\t\t\t[" << face->vertex(1)->point().x() << ", " << face->vertex(1)->point().y() << "]," << std::endl; + std::cout << "\t\t\t\t\t\t[" << face->vertex(2)->point().x() << ", " << face->vertex(2)->point().y() << "]" << std::endl; + std::cout << "\t\t\t\t\t]" << std::endl; + std::cout << "\t\t\t\t]" << std::endl; + std::cout << "\t\t\t}" << std::endl; + std::cout << "\t\t}"; Polygon_repair_2::Triangulation::Finite_faces_iterator next_face = face; ++next_face; - if (next_face != pr.triangulation().finite_faces_end()) ofs << ","; - ofs << std::endl; + if (next_face != pr.triangulation().finite_faces_end()) std::cout << ","; + std::cout << std::endl; } - ofs << "\t]" << std::endl; - ofs << "}" << std::endl; + std::cout << "\t]" << std::endl; + std::cout << "}" << std::endl; pr.reconstruct_multipolygon(); - Multipolygon_with_holes_2 rmp = pr.multipolygon(); - CGAL::IO::write_multi_polygon_WKT(std::cout, rmp); + Multipolygon_with_holes_2 repaired = pr.multipolygon(); return 0; } From edfa1902162982211a7d143e7574d0fa351640d2 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 22 Aug 2023 13:59:16 +0200 Subject: [PATCH 161/520] more of the manual --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index e3ecf4f287d1..a648cf1b0315 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -89,6 +89,8 @@ an inner boundary. FIGURE +STEPS IN DETAIL + For the purposes of the repair operation, the input polygon, polygon with holes or multipolygon is merely used as a container of input line segments. @@ -157,14 +159,29 @@ with holes and allows to navigate through the scene. \subsection SubsectionPolygonRepair2_Repair Repairing a (multi)polygon +It's possible to repair a polygon, polygon with holes or multipolygon with holes +using the odd-even rule by calling the `repair_odd_even` function as shown in the +following example. This function returns a repaired multipolygon with holes. + \cgalFigureBegin{bridge_edge, bridge_edge.png} -(a) Before repair (b) After repair +(a) Before repair: A polygon with a single loop that implicitly creates a hole +using a bridge edge (b) After repair: the same area represented as a polygon with +a hole. \cgalFigureEnd \cgalExample{Polygon_repair_2/repair_polygon_2.cpp} \subsection SubsectionPolygonRepair2_RepairHiddenApi Step by step repairing +The following example shows to do the repair process step by step, including how +to extract the labeled triangulation, which is written as a GeoJSON file consisting +of the individual triangles with their labels. + +\cgalFigureBegin{triangulation, triangulation.png} +(a) A multipolygon consisting of two polygons with one hole each, where one polygon +is inside the hole of the other (b) The labeled triangulation of the multipolygon +\cgalFigureEnd + \cgalExample{Polygon_repair_2/write_labeled_triangulation.cpp} \section SectionPolygonRepair2_Performance Performance From f35625944af0b2e19619018702f35f8857cd400c Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 23 Aug 2023 15:57:15 +0200 Subject: [PATCH 162/520] more details about algorithm --- .../doc/Polygon_repair_2/Polygon_repair_2.txt | 82 ++++++++++++------- .../Polygon_repair_2/repair_polygon_2.cpp | 2 +- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt index a648cf1b0315..2dd4f985ec89 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt @@ -19,6 +19,10 @@ The method returns a valid output stored in a multipolygon with holes. Different triangulation and labelling heuristics are possible, but currently only the odd-even heuristic is implemented in the package. +This heuristic results in areas that are alternately assigned as polygon +interiors and exterior/holes each time that an input edge is passed. +It doesn't distinguish between edges that are part of outer boundaries +from those of inner boundaries. \section SectionPolygonRepair2_Definitions Definitions @@ -35,7 +39,7 @@ that is bounded by one outer boundary and zero or more inner boundaries, where each inner boundary represents a hole in the polygon. Considered independently, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common -edges), forming vertices with degrees larger than two at the tangential points. +edges), forming vertices with degrees of a multiple of two the tangential points. The interior of a polygon with holes should form a connected point set. Note that a valid polygon can also be represented as a valid polygon with holes (where the number of holes is zero). @@ -45,9 +49,12 @@ that is represented by a set of zero or more valid polygons with holes. The interiors of the polygons with holes should be pairwise disjoint, but they are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon -with holes. +with holes (with only one polygon). -FIGURE +\cgalFigureBegin{multipolygons, multipolygons.png} +Valid: (a) polygon, (b-c) polygons with holes, (d-e) multipolygons with holes. +(c) and (e) show cases where boundaries intersect tangentially at a single vertex. +\cgalFigureEnd \subsection SubsectionPolygonRepair2_Output Stricter Conditions for Output @@ -60,7 +67,7 @@ strict criteria: - Adjacent collinear edges touching at vertices of degree two are merged - The sequence of vertices representing a boundary starts from its lexicographically smallest vertex -- Outer boundaries are oriented counterclockwise and inner boundaries are +- Outer boundaries are oriented counter-clockwise and inner boundaries are oriented clockwise - The inner boundaries of a polygon with holes are stored in lexicographic order @@ -82,17 +89,55 @@ labeled as the exterior. -# Reconstruction of the multipolygon: each boundary of each polygon with holes is reconstructed by finding a polygon interior triangle that is adjacent to the exterior or a hole, then following the boundary in an -orientation that is counterclockwise for outer boundaries and clockwise for +orientation that is counter-clockwise for outer boundaries and clockwise for inner boundaries. This orientation and the polygon interior id is used to determine what polygon with holes it belongs to and whether it is an outer or an inner boundary. FIGURE -STEPS IN DETAIL +\subsection SubsectionPolygonRepair2_Triangulation Constrained triangulation For the purposes of the repair operation, the input polygon, polygon with holes -or multipolygon is merely used as a container of input line segments. +or multipolygon is merely used as a container of input line segments. These line +segments are added to the constrained triangulation as constraints. + +With the odd-even heuristic, only the edges that are present an odd number of +times in the input will be constrained edges in the triangulation. +When these edges are only partially overlapping, only the parts that overlap +an odd number of times will be constrained edges in the triangulation. + +This procedure is done in two steps: 1. preprocessing to eliminate identical +edges that are present an even number of times, and 2. an odd-even constraint +counting mechanism that erases existing constraints when new overlapping ones +are added. + +\subsection SubsectionPolygonRepair2_Labeling Labeling + +First, all of the polygon exterior is labeled. For this, the faces that can be +accessed by following the adjacency relationships from the infinite face without +passing through a constrained edge are labeled as exterior faces. + +Then, with the odd-even heuristic, groups of faces that are adjacent to each other +without passing through a constrained edge are labeled. The label applied alternates +between polygon interior and hole every time that a constrained edge is passed. + +\subsection SubsectionPolygonRepair2_Reconstruction Reconstruction of the multipolygon + +The algorithm reconstructs the multipolygon boundary by boundary, repeating the +process until all boundaries have been reconstructed. +Starting from a triangulation face with a polygon interior label that is adjacent +to a face with an exterior or hole label, the boundary of the polygon that contains +the edge between the two triangles is reconstructed. +The successive edges of the boundary is obtained by rotating counter-clockwise +around their common vertex, resulting in counter-clockwise cycles for outer +boundaries and clockwise cycles for inner boundaries. + +Finally, the boundaries are assembled into multipolygons using the unique labels +to know which polygon inner/outer boundaries belong to, and using the orientation +to distinguish between outer and inner boundaries. + +\subsection SubsectionPolygonRepair2_Output Notes on the Output If the input is already valid, the method will return a valid output representing the same area. However, the output might be different in order to conform to the @@ -102,27 +147,8 @@ stricter conditions to generate deterministic output (see Also, it is worth noting that even the repair of a single polygon without holes but with self-intersections can result in a multipolygon with holes. This is why the repair function will always return a multipolygon with holes. The user can -then check whether it consists of a single polygon with holes, and whether this -polygon with holes has zero holes and treat it appropriately. - -\subsection SubsectionPolygonRepair2_OddEven Odd-even repair - -The odd-even repair heuristic results in areas that are alternatingly assigned -as polygon interiors and exterior/holes each time that an input edge is passed. -It doesn't distinguish between edges that are part of outer boundaries or inner -boundaries. - -FIGURE - -In order to generate consistent output, the odd-even heuristic requires it to be -applied to both edges and faces. For the edges, this heuristic means that only -the line segments that are present an odd number of times in the input will be -constrained edges in the triangulation. For the face labels, this heuristic means -that after the exterior faces have been labeled, the other faces will be -alternatingly labeled with polygon interior and hole ids every time that a -constrained edge is passed. - -FIGURE +then check whether it consists of a single polygon with holes, and if a polygon +with holes has zero holes and extract these if needed. \section SectionPolygonRepair2_Examples Examples diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp index 89b4580519dd..06d0ccdbd125 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp @@ -22,6 +22,6 @@ int main(int argc, char* argv[]) { } else { CGAL::IO::write_polygon_WKT(std::cout, mp.polygons()[0]); } - + return 0; } From 6c703f7fd82c4d1778ee34900187b40641c911dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 24 Aug 2023 13:35:51 +0200 Subject: [PATCH 163/520] Polygon_repair_2 -> Polygon_repair --- Documentation/doc/Documentation/packages.txt | 2 +- .../{Polygon_repair_2.h => Polygon_repair.h} | 20 +++++----- .../Concepts/MultiPolygonWithHoles_2.h | 2 +- .../doc/Polygon_repair}/Doxyfile.in | 0 .../Polygon_repair}/PackageDescription.txt | 24 +++++------ .../doc/Polygon_repair/Polygon_repair.txt | 40 +++++++++---------- .../doc/Polygon_repair}/dependencies | 0 .../doc/Polygon_repair/examples.txt | 6 +++ .../examples/Polygon_repair}/CMakeLists.txt | 2 +- .../Polygon_repair}/data/bridge-edge.wkt | 0 .../Polygon_repair}/data/nesting-spike.wkt | 0 .../Polygon_repair}/draw_multipolygon.cpp | 2 +- .../examples/Polygon_repair}/multipolygon.cpp | 2 +- .../Polygon_repair}/nasty_polygons.cpp | 6 +-- .../Polygon_repair}/repair_polygon_2.cpp | 4 +- .../write_labeled_triangulation.cpp | 10 ++--- .../Multipolygon_with_holes_2.h | 4 +- .../CGAL/Polygon_repair/Polygon_repair.h | 40 +++++++++---------- ...iangulation_face_base_with_repair_info_2.h | 2 +- ...riangulation_with_odd_even_constraints_2.h | 4 +- .../draw_multipolygon_with_holes_2.h | 4 +- .../package_info/Polygon_repair}/copyright | 0 .../package_info/Polygon_repair}/dependencies | 2 +- .../Polygon_repair/description.txt | 1 + .../package_info/Polygon_repair}/license.txt | 0 .../Polygon_repair}/long_description.txt | 0 .../package_info/Polygon_repair}/maintainer | 0 .../test/Polygon_repair}/CMakeLists.txt | 2 +- .../test/Polygon_repair}/clipart.cpp | 12 +++--- .../data/in/back-and-forth.wkt | 0 .../test/Polygon_repair}/data/in/bowtie.wkt | 0 .../Polygon_repair}/data/in/bridge-edge.wkt | 0 .../data/in/crossing-polygons-nesting.wkt | 0 .../data/in/crossing-polygons.wkt | 0 .../test/Polygon_repair}/data/in/edge.wkt | 0 .../data/in/empty-multipolygon.wkt | 0 .../Polygon_repair}/data/in/empty-polygon.wkt | 0 .../test/Polygon_repair}/data/in/hole-all.wkt | 0 .../Polygon_repair}/data/in/hole-as-loop.wkt | 0 .../Polygon_repair}/data/in/hole-carved.wkt | 0 .../Polygon_repair}/data/in/hole-filled.wkt | 0 .../Polygon_repair}/data/in/hole-outside.wkt | 0 .../data/in/hole-partly-outside.wkt | 0 .../data/in/hole-touching-edge.wkt | 0 .../data/in/hole-touching-once.wkt | 0 .../data/in/hole-touching-twice.wkt | 0 .../test/Polygon_repair}/data/in/hole.wkt | 0 .../Polygon_repair}/data/in/nesting-spike.wkt | 0 .../test/Polygon_repair}/data/in/nesting.wkt | 0 .../Polygon_repair}/data/in/not-closed.wkt | 0 .../data/in/overlapping-edge-inside.wkt | 0 .../data/in/overlapping-edge-partial.wkt | 0 .../data/in/overlapping-edge.wkt | 0 .../Polygon_repair}/data/in/point-double.wkt | 0 .../test/Polygon_repair}/data/in/point.wkt | 0 .../data/in/spike-boundary.wkt | 0 .../test/Polygon_repair}/data/in/spike-in.wkt | 0 .../Polygon_repair}/data/in/spike-long.wkt | 0 .../Polygon_repair}/data/in/spike-out.wkt | 0 .../Polygon_repair}/data/in/spikes-fp.wkt | 0 .../test/Polygon_repair}/data/in/spikes.wkt | 0 .../test/Polygon_repair}/data/in/spiral.wkt | 0 .../data/in/square-hole-rhombus.wkt | 0 .../test/Polygon_repair}/data/in/square.wkt | 0 .../test/Polygon_repair}/data/in/star.wkt | 0 .../data/ref/back-and-forth.wkt | 0 .../test/Polygon_repair}/data/ref/bowtie.wkt | 0 .../Polygon_repair}/data/ref/bridge-edge.wkt | 0 .../data/ref/crossing-polygons-nesting.wkt | 0 .../data/ref/crossing-polygons.wkt | 0 .../test/Polygon_repair}/data/ref/edge.wkt | 0 .../data/ref/empty-multipolygon.wkt | 0 .../data/ref/empty-polygon.wkt | 0 .../Polygon_repair}/data/ref/hole-all.wkt | 0 .../Polygon_repair}/data/ref/hole-as-loop.wkt | 0 .../Polygon_repair}/data/ref/hole-carved.wkt | 0 .../Polygon_repair}/data/ref/hole-filled.wkt | 0 .../Polygon_repair}/data/ref/hole-outside.wkt | 0 .../data/ref/hole-partly-outside.wkt | 0 .../data/ref/hole-touching-edge.wkt | 0 .../data/ref/hole-touching-once.wkt | 0 .../data/ref/hole-touching-twice.wkt | 0 .../test/Polygon_repair}/data/ref/hole.wkt | 0 .../data/ref/nesting-spike.wkt | 0 .../test/Polygon_repair}/data/ref/nesting.wkt | 0 .../Polygon_repair}/data/ref/not-closed.wkt | 0 .../data/ref/overlapping-edge-inside.wkt | 0 .../data/ref/overlapping-edge-partial.wkt | 0 .../data/ref/overlapping-edge.wkt | 0 .../Polygon_repair}/data/ref/point-double.wkt | 0 .../test/Polygon_repair}/data/ref/point.wkt | 0 .../data/ref/spike-boundary.wkt | 0 .../Polygon_repair}/data/ref/spike-in.wkt | 0 .../Polygon_repair}/data/ref/spike-long.wkt | 0 .../Polygon_repair}/data/ref/spike-out.wkt | 0 .../Polygon_repair}/data/ref/spikes-fp.wkt | 0 .../test/Polygon_repair}/data/ref/spikes.wkt | 0 .../test/Polygon_repair}/data/ref/spiral.wkt | 0 .../data/ref/square-hole-rhombus.wkt | 0 .../test/Polygon_repair}/data/ref/square.wkt | 0 .../test/Polygon_repair}/data/ref/star.wkt | 0 .../Polygon_repair}/draw_test_polygons.cpp | 10 ++--- .../Polygon_repair}/repair_polygon_2_test.cpp | 8 ++-- .../doc/Polygon_repair_2/examples.txt | 6 --- .../Polygon_repair_2/description.txt | 1 - .../examples/Stream_support/Polygon_WKT.cpp | 2 +- Stream_support/include/CGAL/IO/WKT.h | 2 +- .../package_info/Stream_support/dependencies | 2 +- 108 files changed, 111 insertions(+), 111 deletions(-) rename Installation/include/CGAL/license/{Polygon_repair_2.h => Polygon_repair.h} (68%) rename {Polygon_repair_2/doc/Polygon_repair_2 => Polygon_repair/doc/Polygon_repair}/Concepts/MultiPolygonWithHoles_2.h (97%) rename {Polygon_repair_2/doc/Polygon_repair_2 => Polygon_repair/doc/Polygon_repair}/Doxyfile.in (100%) rename {Polygon_repair_2/doc/Polygon_repair_2 => Polygon_repair/doc/Polygon_repair}/PackageDescription.txt (65%) rename Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt => Polygon_repair/doc/Polygon_repair/Polygon_repair.txt (88%) rename {Polygon_repair_2/doc/Polygon_repair_2 => Polygon_repair/doc/Polygon_repair}/dependencies (100%) create mode 100644 Polygon_repair/doc/Polygon_repair/examples.txt rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/CMakeLists.txt (93%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/data/bridge-edge.wkt (100%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/data/nesting-spike.wkt (100%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/draw_multipolygon.cpp (90%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/multipolygon.cpp (94%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/nasty_polygons.cpp (94%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/repair_polygon_2.cpp (85%) rename {Polygon_repair_2/examples/Polygon_repair_2 => Polygon_repair/examples/Polygon_repair}/write_labeled_triangulation.cpp (86%) rename {Polygon_repair_2/include/CGAL/Polygon_repair_2 => Polygon_repair/include/CGAL/Polygon_repair}/Multipolygon_with_holes_2.h (98%) rename Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h => Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h (95%) rename {Polygon_repair_2/include/CGAL/Polygon_repair_2 => Polygon_repair/include/CGAL/Polygon_repair}/Triangulation_face_base_with_repair_info_2.h (97%) rename {Polygon_repair_2/include/CGAL/Polygon_repair_2 => Polygon_repair/include/CGAL/Polygon_repair}/Triangulation_with_odd_even_constraints_2.h (98%) rename {Polygon_repair_2/include/CGAL/Polygon_repair_2 => Polygon_repair/include/CGAL/Polygon_repair}/draw_multipolygon_with_holes_2.h (98%) rename {Polygon_repair_2/package_info/Polygon_repair_2 => Polygon_repair/package_info/Polygon_repair}/copyright (100%) rename {Polygon_repair_2/package_info/Polygon_repair_2 => Polygon_repair/package_info/Polygon_repair}/dependencies (95%) create mode 100644 Polygon_repair/package_info/Polygon_repair/description.txt rename {Polygon_repair_2/package_info/Polygon_repair_2 => Polygon_repair/package_info/Polygon_repair}/license.txt (100%) rename {Polygon_repair_2/package_info/Polygon_repair_2 => Polygon_repair/package_info/Polygon_repair}/long_description.txt (100%) rename {Polygon_repair_2/package_info/Polygon_repair_2 => Polygon_repair/package_info/Polygon_repair}/maintainer (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/CMakeLists.txt (94%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/clipart.cpp (92%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/back-and-forth.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/bowtie.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/bridge-edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/crossing-polygons-nesting.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/crossing-polygons.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/empty-multipolygon.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/empty-polygon.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-all.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-as-loop.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-carved.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-filled.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-outside.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-partly-outside.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-touching-edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-touching-once.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole-touching-twice.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/hole.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/nesting-spike.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/nesting.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/not-closed.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/overlapping-edge-inside.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/overlapping-edge-partial.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/overlapping-edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/point-double.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/point.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spike-boundary.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spike-in.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spike-long.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spike-out.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spikes-fp.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spikes.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/spiral.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/square-hole-rhombus.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/square.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/in/star.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/back-and-forth.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/bowtie.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/bridge-edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/crossing-polygons-nesting.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/crossing-polygons.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/empty-multipolygon.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/empty-polygon.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-all.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-as-loop.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-carved.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-filled.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-outside.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-partly-outside.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-touching-edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-touching-once.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole-touching-twice.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/hole.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/nesting-spike.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/nesting.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/not-closed.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/overlapping-edge-inside.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/overlapping-edge-partial.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/overlapping-edge.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/point-double.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/point.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spike-boundary.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spike-in.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spike-long.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spike-out.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spikes-fp.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spikes.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/spiral.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/square-hole-rhombus.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/square.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/data/ref/star.wkt (100%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/draw_test_polygons.cpp (83%) rename {Polygon_repair_2/test/Polygon_repair_2 => Polygon_repair/test/Polygon_repair}/repair_polygon_2_test.cpp (90%) delete mode 100644 Polygon_repair_2/doc/Polygon_repair_2/examples.txt delete mode 100644 Polygon_repair_2/package_info/Polygon_repair_2/description.txt diff --git a/Documentation/doc/Documentation/packages.txt b/Documentation/doc/Documentation/packages.txt index d89ade951422..7002020a1a07 100644 --- a/Documentation/doc/Documentation/packages.txt +++ b/Documentation/doc/Documentation/packages.txt @@ -31,7 +31,7 @@ \cgalPackageSection{PartPolygons,Polygons} \package_listing{Polygon} -\package_listing{Polygon_repair_2} +\package_listing{Polygon_repair} \package_listing{Boolean_set_operations_2} \package_listing{Nef_2} \package_listing{Nef_S2} diff --git a/Installation/include/CGAL/license/Polygon_repair_2.h b/Installation/include/CGAL/license/Polygon_repair.h similarity index 68% rename from Installation/include/CGAL/license/Polygon_repair_2.h rename to Installation/include/CGAL/license/Polygon_repair.h index 11c0f94e3c5a..b1fc77cedb6a 100644 --- a/Installation/include/CGAL/license/Polygon_repair_2.h +++ b/Installation/include/CGAL/license/Polygon_repair.h @@ -11,15 +11,15 @@ // // Warning: this file is generated, see include/CGAL/license/README.md -#ifndef CGAL_LICENSE_POLYGON_REPAIR_2_H -#define CGAL_LICENSE_POLYGON_REPAIR_2_H +#ifndef CGAL_LICENSE_POLYGON_REPAIR_H +#define CGAL_LICENSE_POLYGON_REPAIR_H #include #include -#ifdef CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE +#ifdef CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE -# if CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE +# if CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE # if defined(CGAL_LICENSE_WARNING) @@ -33,22 +33,22 @@ You get this error, as you defined CGAL_LICENSE_ERROR." # endif // CGAL_LICENSE_ERROR -# endif // CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE +# endif // CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE -#else // no CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE +#else // no CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE # if defined(CGAL_LICENSE_WARNING) - CGAL_pragma_warning("\nThe macro CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE is not defined." + CGAL_pragma_warning("\nThe macro CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE is not defined." "\nYou use the CGAL 2D Polygon Repair package under " "the terms of the GPLv3+.") # endif // CGAL_LICENSE_WARNING # ifdef CGAL_LICENSE_ERROR -# error "The macro CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE is not defined.\ +# error "The macro CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE is not defined.\ You use the CGAL 2D Polygon Repair package under the terms of \ the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR." # endif // CGAL_LICENSE_ERROR -#endif // no CGAL_POLYGON_REPAIR_2_COMMERCIAL_LICENSE +#endif // no CGAL_POLYGON_REPAIR_COMMERCIAL_LICENSE -#endif // CGAL_LICENSE_POLYGON_REPAIR_2_H +#endif // CGAL_LICENSE_POLYGON_REPAIR_H diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h b/Polygon_repair/doc/Polygon_repair/Concepts/MultiPolygonWithHoles_2.h similarity index 97% rename from Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h rename to Polygon_repair/doc/Polygon_repair/Concepts/MultiPolygonWithHoles_2.h index 1054217ddce7..d7f037be4d6d 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon_repair/doc/Polygon_repair/Concepts/MultiPolygonWithHoles_2.h @@ -1,4 +1,4 @@ -/*! \ingroup PkgPolygonRepair2Concepts +/*! \ingroup PkgPolygonRepairConcepts * \cgalConcept * * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in b/Polygon_repair/doc/Polygon_repair/Doxyfile.in similarity index 100% rename from Polygon_repair_2/doc/Polygon_repair_2/Doxyfile.in rename to Polygon_repair/doc/Polygon_repair/Doxyfile.in diff --git a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt similarity index 65% rename from Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt rename to Polygon_repair/doc/Polygon_repair/PackageDescription.txt index e74c1ef034f8..4259a9f0808f 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -1,12 +1,12 @@ // PRETTY PACKAGE NAME should equal the project title in Doxyfile.in -/// \defgroup PkgPolygonRepair2Ref 2D Polygon Repair Reference +/// \defgroup PkgPolygonRepairRef 2D Polygon Repair Reference -/// \defgroup PkgPolygonRepair2Concepts Concepts -/// \ingroup PkgPolygonRepair2Ref +/// \defgroup PkgPolygonRepairConcepts Concepts +/// \ingroup PkgPolygonRepairRef -/// \defgroup PkgPolygonRepair2Functions Functions -/// \ingroup PkgPolygonRepair2Ref +/// \defgroup PkgPolygonRepairFunctions Functions +/// \ingroup PkgPolygonRepairRef /*! \code @@ -14,19 +14,19 @@ \endcode */ /// \defgroup PkgDrawMultipolygonWithHoles2 Draw a 2D Multipolygon with holes -/// \ingroup PkgPolygonRepair2Ref +/// \ingroup PkgPolygonRepairRef /*! -\addtogroup PkgPolygonRepair2Ref +\addtogroup PkgPolygonRepairRef \todo check generated documentation -\cgalPkgDescriptionBegin{2D Polygon Repair,PkgPolygonRepair2} -\cgalPkgPicture{Polygon_repair_2-small.png} +\cgalPkgDescriptionBegin{2D Polygon Repair,PkgPolygonRepair} +\cgalPkgPicture{Polygon_repair-small.png} \cgalPkgSummaryBegin \cgalPkgAuthors{Ken Arroyo Ohori} \cgalPkgDesc{The package provides a 2D multipolygon class and algorithms to repair 2D (multi)polygons. } -\cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepair2Ref} +\cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepairRef} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin @@ -46,10 +46,10 @@ \cgalCRPSection{Classes} - `CGAL::Multipolygon_with_holes_2` -- `CGAL::Polygon_repair_2` +- `CGAL::Polygon_repair` \cgalCRPSection{Functions} -- `CGAL::Polygon_repair_2::repair()` +- `CGAL::Polygon_repair::repair()` \cgalCRPSection{Draw a Multipolygon_with_holes_2} - \link PkgDrawMultipolygonWithHoles2 CGAL::draw() \endlink diff --git a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt similarity index 88% rename from Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt rename to Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 2dd4f985ec89..53dd72489cd1 100644 --- a/Polygon_repair_2/doc/Polygon_repair_2/Polygon_repair_2.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -7,7 +7,7 @@ namespace CGAL { \cgalAutoToc \author Ken Arroyo Ohori -\section SectionPolygonRepair2_Introduction Introduction +\section SectionPolygonRepair_Introduction Introduction This package implements a polygon repair method based on a constrained triangulation \cgalCite{14cg}. Starting from a possibly invalid input @@ -24,7 +24,7 @@ interiors and exterior/holes each time that an input edge is passed. It doesn't distinguish between edges that are part of outer boundaries from those of inner boundaries. -\section SectionPolygonRepair2_Definitions Definitions +\section SectionPolygonRepair_Definitions Definitions - A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ that is bounded by a cycle of linear edges, which is known as its @@ -56,7 +56,7 @@ Valid: (a) polygon, (b-c) polygons with holes, (d-e) multipolygons with holes. (c) and (e) show cases where boundaries intersect tangentially at a single vertex. \cgalFigureEnd -\subsection SubsectionPolygonRepair2_Output Stricter Conditions for Output +\subsection SubsectionPolygonRepair_Output Stricter Conditions for Output The conditions listed above are sufficient to define valid polygons, polygons with holes and multipolygons with holes for most applications. However, in @@ -74,7 +74,7 @@ order - The polygons with holes of a multipolygon with holes are also stored in lexicographic order -\section SectionPolygonRepair2_Algorithm Algorithm +\section SectionPolygonRepair_Algorithm Algorithm Broadly, the algorithm consists of three steps: @@ -96,7 +96,7 @@ an inner boundary. FIGURE -\subsection SubsectionPolygonRepair2_Triangulation Constrained triangulation +\subsection SubsectionPolygonRepair_Triangulation Constrained triangulation For the purposes of the repair operation, the input polygon, polygon with holes or multipolygon is merely used as a container of input line segments. These line @@ -112,7 +112,7 @@ edges that are present an even number of times, and 2. an odd-even constraint counting mechanism that erases existing constraints when new overlapping ones are added. -\subsection SubsectionPolygonRepair2_Labeling Labeling +\subsection SubsectionPolygonRepair_Labeling Labeling First, all of the polygon exterior is labeled. For this, the faces that can be accessed by following the adjacency relationships from the infinite face without @@ -122,7 +122,7 @@ Then, with the odd-even heuristic, groups of faces that are adjacent to each oth without passing through a constrained edge are labeled. The label applied alternates between polygon interior and hole every time that a constrained edge is passed. -\subsection SubsectionPolygonRepair2_Reconstruction Reconstruction of the multipolygon +\subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the multipolygon The algorithm reconstructs the multipolygon boundary by boundary, repeating the process until all boundaries have been reconstructed. @@ -137,12 +137,12 @@ Finally, the boundaries are assembled into multipolygons using the unique labels to know which polygon inner/outer boundaries belong to, and using the orientation to distinguish between outer and inner boundaries. -\subsection SubsectionPolygonRepair2_Output Notes on the Output +\subsection SubsectionPolygonRepair_Notes Notes on the Output If the input is already valid, the method will return a valid output representing the same area. However, the output might be different in order to conform to the stricter conditions to generate deterministic output (see -\ref SubsectionPolygonRepair2_Output). +\ref SubsectionPolygonRepair_Output). Also, it is worth noting that even the repair of a single polygon without holes but with self-intersections can result in a multipolygon with holes. This is why @@ -150,18 +150,18 @@ the repair function will always return a multipolygon with holes. The user can then check whether it consists of a single polygon with holes, and if a polygon with holes has zero holes and extract these if needed. -\section SectionPolygonRepair2_Examples Examples +\section SectionPolygonRepair_Examples Examples -\subsection SubsectionPolygonRepair2_Multipolygon The multipolygon with holes class +\subsection SubsectionPolygonRepair_Multipolygon The multipolygon with holes class The following example shows the creation of a multipolygon with holes and the traversal of the polygons in it. -\cgalExample{Polygon_repair_2/multipolygon.cpp} +\cgalExample{Polygon_repair/multipolygon.cpp} -\subsection SubsectionPolygonRepair2_WKTDraw Draw a multipolygon +\subsection SubsectionPolygonRepair_WKTDraw Draw a multipolygon -A multipolygon with holes can be visualized by calling the \link PkgPolygonRepair2Ref +A multipolygon with holes can be visualized by calling the \link PkgPolygonRepairRef CGAL::draw() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window. @@ -171,7 +171,7 @@ CGAL::draw

() \endlink and \link PkgDrawPolygonWithHoles2 CGAL::draw() \en The multipolygon with holes shown in this example is created using the well-known text (WKT) reader, which can read and write multipolygons with holes. -\cgalExample{Polygon_repair_2/draw_multipolygon.cpp} +\cgalExample{Polygon_repair/draw_multipolygon.cpp} This function requires `CGAL_Qt5`, and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target @@ -183,7 +183,7 @@ Result of the run of the draw_multipolygon program. A window shows the multipoly with holes and allows to navigate through the scene. \cgalFigureEnd -\subsection SubsectionPolygonRepair2_Repair Repairing a (multi)polygon +\subsection SubsectionPolygonRepair_Repair Repairing a (multi)polygon It's possible to repair a polygon, polygon with holes or multipolygon with holes using the odd-even rule by calling the `repair_odd_even` function as shown in the @@ -195,9 +195,9 @@ using a bridge edge (b) After repair: the same area represented as a polygon wit a hole. \cgalFigureEnd -\cgalExample{Polygon_repair_2/repair_polygon_2.cpp} +\cgalExample{Polygon_repair/repair_polygon_2.cpp} -\subsection SubsectionPolygonRepair2_RepairHiddenApi Step by step repairing +\subsection SubsectionPolygonRepair_RepairHiddenApi Step by step repairing The following example shows to do the repair process step by step, including how to extract the labeled triangulation, which is written as a GeoJSON file consisting @@ -208,9 +208,9 @@ of the individual triangles with their labels. is inside the hole of the other (b) The labeled triangulation of the multipolygon \cgalFigureEnd -\cgalExample{Polygon_repair_2/write_labeled_triangulation.cpp} +\cgalExample{Polygon_repair/write_labeled_triangulation.cpp} -\section SectionPolygonRepair2_Performance Performance +\section SectionPolygonRepair_Performance Performance */ } /* namespace CGAL */ diff --git a/Polygon_repair_2/doc/Polygon_repair_2/dependencies b/Polygon_repair/doc/Polygon_repair/dependencies similarity index 100% rename from Polygon_repair_2/doc/Polygon_repair_2/dependencies rename to Polygon_repair/doc/Polygon_repair/dependencies diff --git a/Polygon_repair/doc/Polygon_repair/examples.txt b/Polygon_repair/doc/Polygon_repair/examples.txt new file mode 100644 index 000000000000..c0a827972362 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/examples.txt @@ -0,0 +1,6 @@ +/*! +\example Polygon_repair/multipolygon.cpp +\example Polygon_repair/draw_multipolygon.cpp +\example Polygon_repair/repair_polygon_2.cpp +\example Polygon_repair/write_labeled_triangulation.cpp +*/ diff --git a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt b/Polygon_repair/examples/Polygon_repair/CMakeLists.txt similarity index 93% rename from Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt rename to Polygon_repair/examples/Polygon_repair/CMakeLists.txt index 417109e78275..930b4dc25676 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair/examples/Polygon_repair/CMakeLists.txt @@ -2,7 +2,7 @@ # This is the CMake script for compiling a CGAL application. cmake_minimum_required(VERSION 3.1...3.23) -project(Polygon_repair_2_Examples) +project(Polygon_repair_Examples) find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) diff --git a/Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt b/Polygon_repair/examples/Polygon_repair/data/bridge-edge.wkt similarity index 100% rename from Polygon_repair_2/examples/Polygon_repair_2/data/bridge-edge.wkt rename to Polygon_repair/examples/Polygon_repair/data/bridge-edge.wkt diff --git a/Polygon_repair_2/examples/Polygon_repair_2/data/nesting-spike.wkt b/Polygon_repair/examples/Polygon_repair/data/nesting-spike.wkt similarity index 100% rename from Polygon_repair_2/examples/Polygon_repair_2/data/nesting-spike.wkt rename to Polygon_repair/examples/Polygon_repair/data/nesting-spike.wkt diff --git a/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp b/Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp similarity index 90% rename from Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp rename to Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp index d7ec18926ffa..3451b98de51a 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/draw_multipolygon.cpp +++ b/Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp b/Polygon_repair/examples/Polygon_repair/multipolygon.cpp similarity index 94% rename from Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp rename to Polygon_repair/examples/Polygon_repair/multipolygon.cpp index cc9b5df84179..ce0cdf27f411 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/multipolygon.cpp +++ b/Polygon_repair/examples/Polygon_repair/multipolygon.cpp @@ -1,5 +1,5 @@ #include -#include +#include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Point_2 = Kernel::Point_2; diff --git a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp b/Polygon_repair/examples/Polygon_repair/nasty_polygons.cpp similarity index 94% rename from Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp rename to Polygon_repair/examples/Polygon_repair/nasty_polygons.cpp index 97ed92f25150..2e663b265f61 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/nasty_polygons.cpp +++ b/Polygon_repair/examples/Polygon_repair/nasty_polygons.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -12,7 +12,7 @@ using Point_2 = Kernel::Point_2; using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; int main(int argc, char* argv[]) { @@ -27,7 +27,7 @@ int main(int argc, char* argv[]) { std::istringstream iss(in); Polygon_with_holes_2 p; CGAL::IO::read_polygon_WKT(iss, p); - Polygon_repair_2 pr; + Polygon_repair pr; pr.add_to_triangulation_odd_even(p); pr.label_triangulation_odd_even(); pr.reconstruct_multipolygon(); diff --git a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp similarity index 85% rename from Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp rename to Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp index 06d0ccdbd125..c8a91fb0c1b7 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/repair_polygon_2.cpp +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 pin; CGAL::IO::read_polygon_WKT(in, pin); - Multipolygon_with_holes_2 mp = CGAL::Polygon_repair_2::repair_odd_even(pin); + Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair_odd_even(pin); if (mp.number_of_polygons() > 1) { CGAL::IO::write_multi_polygon_WKT(std::cout, mp); } else { diff --git a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp b/Polygon_repair/examples/Polygon_repair/write_labeled_triangulation.cpp similarity index 86% rename from Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp rename to Polygon_repair/examples/Polygon_repair/write_labeled_triangulation.cpp index 3dde85eb920a..24837d7c14ad 100644 --- a/Polygon_repair_2/examples/Polygon_repair_2/write_labeled_triangulation.cpp +++ b/Polygon_repair/examples/Polygon_repair/write_labeled_triangulation.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -10,7 +10,7 @@ using Point_2 = Kernel::Point_2; using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; int main(int argc, char* argv[]) { @@ -19,7 +19,7 @@ int main(int argc, char* argv[]) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(ifs, mp); - Polygon_repair_2 pr; + Polygon_repair pr; pr.add_to_triangulation_odd_even(mp); pr.label_triangulation_odd_even(); @@ -27,7 +27,7 @@ int main(int argc, char* argv[]) { std::cout << "\t\"type\": \"FeatureCollection\"," << std::endl; std::cout << "\t\"features\": [" << std::endl; - for (Polygon_repair_2::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); + for (Polygon_repair::Triangulation::Finite_faces_iterator face = pr.triangulation().finite_faces_begin(); face != pr.triangulation().finite_faces_end(); ++face) { std::cout << "\t\t{" << std::endl; std::cout << "\t\t\t\"type\": \"Feature\"," << std::endl; @@ -45,7 +45,7 @@ int main(int argc, char* argv[]) { std::cout << "\t\t\t\t]" << std::endl; std::cout << "\t\t\t}" << std::endl; std::cout << "\t\t}"; - Polygon_repair_2::Triangulation::Finite_faces_iterator next_face = face; + Polygon_repair::Triangulation::Finite_faces_iterator next_face = face; ++next_face; if (next_face != pr.triangulation().finite_faces_end()) std::cout << ","; std::cout << std::endl; diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h b/Polygon_repair/include/CGAL/Polygon_repair/Multipolygon_with_holes_2.h similarity index 98% rename from Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/Multipolygon_with_holes_2.h index 8d13989b4e82..2035b8fb84ce 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Multipolygon_with_holes_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Multipolygon_with_holes_2.h @@ -12,13 +12,13 @@ #ifndef CGAL_MULTIPOLYGON_WITH_HOLES_2_H #define CGAL_MULTIPOLYGON_WITH_HOLES_2_H -#include +#include #include namespace CGAL { -/*! \ingroup PkgPolygonRepair2Ref +/*! \ingroup PkgPolygonRepairRef * * The class `Multipolygon_with_holes_2` models the concept `MultipolygonWithHoles_2`. * It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h similarity index 95% rename from Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 85ef9d1673de..6a06b95ac28f 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Polygon_repair_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -10,32 +10,32 @@ // Author(s) : Ken Arroyo Ohori // Guillaume Damiand -#ifndef CGAL_POLYGON_REPAIR_2_H -#define CGAL_POLYGON_REPAIR_2_H +#ifndef CGAL_POLYGON_REPAIR_H +#define CGAL_POLYGON_REPAIR_H -#include +#include #include #include #include #include -#include -#include -#include +#include +#include +#include namespace CGAL { -namespace Polygon_repair_2 { +namespace Polygon_repair { template -class Polygon_repair_2; +class Polygon_repair; -/// \ingroup PkgPolygonRepair2Functions +/// \ingroup PkgPolygonRepairFunctions /// Repair a polygon without holes template Multipolygon_with_holes_2 repair_odd_even(const Polygon_2& p) { - CGAL::Polygon_repair_2::Polygon_repair_2 pr; + CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_odd_even(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_odd_even(); @@ -43,11 +43,11 @@ Multipolygon_with_holes_2 repair_odd_even(const Polygo } return pr.multipolygon(); } -/// \ingroup PkgPolygonRepair2Functions +/// \ingroup PkgPolygonRepairFunctions /// Repair a polygon with holes template Multipolygon_with_holes_2 repair_odd_even(const Polygon_with_holes_2& p) { - CGAL::Polygon_repair_2::Polygon_repair_2 pr; + CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_odd_even(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_odd_even(); @@ -55,11 +55,11 @@ Multipolygon_with_holes_2 repair_odd_even(const Polygo } return pr.multipolygon(); } -/// \ingroup PkgPolygonRepair2Functions +/// \ingroup PkgPolygonRepairFunctions /// Repair a multipolygon with holes template Multipolygon_with_holes_2 repair_odd_even(const Multipolygon_with_holes_2& mp) { - CGAL::Polygon_repair_2::Polygon_repair_2 pr; + CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_odd_even(mp); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_odd_even(); @@ -67,14 +67,14 @@ Multipolygon_with_holes_2 repair_odd_even(const Multip } return pr.multipolygon(); } -/*! \ingroup PkgPolygonRepair2Ref +/*! \ingroup PkgPolygonRepairRef * - * The class `Polygon_repair_2` builds on a constrained + * The class `Polygon_repair` builds on a constrained * triangulation to remove the parts of constraints that overlap an even number of times */ template > -class Polygon_repair_2 { +class Polygon_repair { public: using Vertex_base = CGAL::Triangulation_vertex_base_2; using Face_base = CGAL::Constrained_triangulation_face_base_2; @@ -117,7 +117,7 @@ class Polygon_repair_2 { }; /// \name Creation - Polygon_repair_2() : number_of_polygons(0), number_of_holes(0) {} + Polygon_repair() : number_of_polygons(0), number_of_holes(0) {} /// \name Modifiers /// @{ @@ -464,7 +464,7 @@ class Polygon_repair_2 { typename Triangulation::Face_handle search_start; }; -} // namespace Polygon_repair_2 +} // namespace Polygon_repair } // namespace CGAL -#endif // CGAL_POLYGON_REPAIR_2_H +#endif // CGAL_POLYGON_REPAIR_H diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_face_base_with_repair_info_2.h similarity index 97% rename from Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/Triangulation_face_base_with_repair_info_2.h index 68143c4a13bc..61389d4e33ee 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_face_base_with_repair_info_2.h @@ -12,7 +12,7 @@ #ifndef CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H #define CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H -#include +#include #include diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h similarity index 98% rename from Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h index f496d1154092..9a693be3d94d 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h @@ -12,12 +12,12 @@ #ifndef CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H #define CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H -#include +#include #include namespace CGAL { -/*! \ingroup PkgPolygonRepair2Ref +/*! \ingroup PkgPolygonRepairRef * * The class `Triangulation_with_odd_even_constraints_2` builds on a constrained * triangulation to remove the parts of constraints that overlap an even number of times diff --git a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h b/Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h similarity index 98% rename from Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h index 703e89b73e0a..749a30579c40 100644 --- a/Polygon_repair_2/include/CGAL/Polygon_repair_2/draw_multipolygon_with_holes_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h @@ -12,7 +12,7 @@ #ifndef CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H #define CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H -#include +#include #include @@ -43,7 +43,7 @@ void draw(const PH& aph); #ifdef CGAL_USE_BASIC_VIEWER #include -#include +#include namespace CGAL { diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/copyright b/Polygon_repair/package_info/Polygon_repair/copyright similarity index 100% rename from Polygon_repair_2/package_info/Polygon_repair_2/copyright rename to Polygon_repair/package_info/Polygon_repair/copyright diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/dependencies b/Polygon_repair/package_info/Polygon_repair/dependencies similarity index 95% rename from Polygon_repair_2/package_info/Polygon_repair_2/dependencies rename to Polygon_repair/package_info/Polygon_repair/dependencies index f1acb2ceddc5..f30877d02528 100644 --- a/Polygon_repair_2/package_info/Polygon_repair_2/dependencies +++ b/Polygon_repair/package_info/Polygon_repair/dependencies @@ -17,7 +17,7 @@ Kernel_d Modular_arithmetic Number_types Polygon -Polygon_repair_2 +Polygon_repair Profiling_tools Property_map STL_Extension diff --git a/Polygon_repair/package_info/Polygon_repair/description.txt b/Polygon_repair/package_info/Polygon_repair/description.txt new file mode 100644 index 000000000000..faa7c2ab3f9c --- /dev/null +++ b/Polygon_repair/package_info/Polygon_repair/description.txt @@ -0,0 +1 @@ +Package Polygon_repair provides functions to repair polygons. diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/license.txt b/Polygon_repair/package_info/Polygon_repair/license.txt similarity index 100% rename from Polygon_repair_2/package_info/Polygon_repair_2/license.txt rename to Polygon_repair/package_info/Polygon_repair/license.txt diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/long_description.txt b/Polygon_repair/package_info/Polygon_repair/long_description.txt similarity index 100% rename from Polygon_repair_2/package_info/Polygon_repair_2/long_description.txt rename to Polygon_repair/package_info/Polygon_repair/long_description.txt diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/maintainer b/Polygon_repair/package_info/Polygon_repair/maintainer similarity index 100% rename from Polygon_repair_2/package_info/Polygon_repair_2/maintainer rename to Polygon_repair/package_info/Polygon_repair/maintainer diff --git a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt b/Polygon_repair/test/Polygon_repair/CMakeLists.txt similarity index 94% rename from Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt rename to Polygon_repair/test/Polygon_repair/CMakeLists.txt index 365fba1a84c2..858e342b24ab 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/CMakeLists.txt +++ b/Polygon_repair/test/Polygon_repair/CMakeLists.txt @@ -2,7 +2,7 @@ # This is the CMake script for compiling a CGAL application. cmake_minimum_required(VERSION 3.1...3.23) -project(Polygon_repair_2_Tests) +project(Polygon_repair_Tests) find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) diff --git a/Polygon_repair_2/test/Polygon_repair_2/clipart.cpp b/Polygon_repair/test/Polygon_repair/clipart.cpp similarity index 92% rename from Polygon_repair_2/test/Polygon_repair_2/clipart.cpp rename to Polygon_repair/test/Polygon_repair/clipart.cpp index 421e68e7334e..68b9f86e001d 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/clipart.cpp +++ b/Polygon_repair/test/Polygon_repair/clipart.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -12,7 +12,7 @@ using Point_2 = Kernel::Point_2; using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; void print_timer(clock_t start_time) { clock_t stop_time = clock(); @@ -40,7 +40,7 @@ int main(int argc, char* argv[]) { // } start_time = clock(); - Polygon_repair_2 pr; + Polygon_repair pr; std::vector vertices; std::vector> edges; @@ -80,10 +80,10 @@ int main(int argc, char* argv[]) { std::cout << std::endl; start_time = clock(); - Polygon_repair_2::Triangulation::Face_handle search_start; + Polygon_repair::Triangulation::Face_handle search_start; for (auto const& edge: edges_to_insert) { - Polygon_repair_2::Triangulation::Vertex_handle va = pr.triangulation().insert(edge.first, search_start); - Polygon_repair_2::Triangulation::Vertex_handle vb = pr.triangulation().insert(edge.second, va->face()); // vb is likely close to va + Polygon_repair::Triangulation::Vertex_handle va = pr.triangulation().insert(edge.first, search_start); + Polygon_repair::Triangulation::Vertex_handle vb = pr.triangulation().insert(edge.second, va->face()); // vb is likely close to va pr.triangulation().odd_even_insert_constraint(va, vb); search_start = vb->face(); } std::cout << "Inserted constraints in "; diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/back-and-forth.wkt b/Polygon_repair/test/Polygon_repair/data/in/back-and-forth.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/back-and-forth.wkt rename to Polygon_repair/test/Polygon_repair/data/in/back-and-forth.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/bowtie.wkt b/Polygon_repair/test/Polygon_repair/data/in/bowtie.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/bowtie.wkt rename to Polygon_repair/test/Polygon_repair/data/in/bowtie.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/bridge-edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/bridge-edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/bridge-edge.wkt rename to Polygon_repair/test/Polygon_repair/data/in/bridge-edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt b/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons-nesting.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons-nesting.wkt rename to Polygon_repair/test/Polygon_repair/data/in/crossing-polygons-nesting.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons.wkt b/Polygon_repair/test/Polygon_repair/data/in/crossing-polygons.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/crossing-polygons.wkt rename to Polygon_repair/test/Polygon_repair/data/in/crossing-polygons.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/edge.wkt rename to Polygon_repair/test/Polygon_repair/data/in/edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-multipolygon.wkt b/Polygon_repair/test/Polygon_repair/data/in/empty-multipolygon.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/empty-multipolygon.wkt rename to Polygon_repair/test/Polygon_repair/data/in/empty-multipolygon.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/empty-polygon.wkt b/Polygon_repair/test/Polygon_repair/data/in/empty-polygon.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/empty-polygon.wkt rename to Polygon_repair/test/Polygon_repair/data/in/empty-polygon.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-all.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-all.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-all.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-as-loop.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-as-loop.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-as-loop.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-as-loop.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-carved.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-carved.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-carved.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-carved.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-filled.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-filled.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-filled.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-filled.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-outside.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-outside.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-outside.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-outside.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-partly-outside.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-partly-outside.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-partly-outside.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-partly-outside.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-edge.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-touching-edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-once.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-once.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-touching-once.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-twice.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole-touching-twice.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole-touching-twice.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole-touching-twice.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/hole.wkt b/Polygon_repair/test/Polygon_repair/data/in/hole.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/hole.wkt rename to Polygon_repair/test/Polygon_repair/data/in/hole.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt b/Polygon_repair/test/Polygon_repair/data/in/nesting-spike.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/nesting-spike.wkt rename to Polygon_repair/test/Polygon_repair/data/in/nesting-spike.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt b/Polygon_repair/test/Polygon_repair/data/in/nesting.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/nesting.wkt rename to Polygon_repair/test/Polygon_repair/data/in/nesting.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt b/Polygon_repair/test/Polygon_repair/data/in/not-closed.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/not-closed.wkt rename to Polygon_repair/test/Polygon_repair/data/in/not-closed.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-inside.wkt b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-inside.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-inside.wkt rename to Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-inside.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-partial.wkt b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-partial.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge-partial.wkt rename to Polygon_repair/test/Polygon_repair/data/in/overlapping-edge-partial.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge.wkt b/Polygon_repair/test/Polygon_repair/data/in/overlapping-edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/overlapping-edge.wkt rename to Polygon_repair/test/Polygon_repair/data/in/overlapping-edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt b/Polygon_repair/test/Polygon_repair/data/in/point-double.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/point-double.wkt rename to Polygon_repair/test/Polygon_repair/data/in/point-double.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt b/Polygon_repair/test/Polygon_repair/data/in/point.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/point.wkt rename to Polygon_repair/test/Polygon_repair/data/in/point.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-boundary.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-boundary.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spike-boundary.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spike-boundary.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-in.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-in.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spike-in.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spike-in.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-long.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spike-long.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spike-long.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spike-out.wkt b/Polygon_repair/test/Polygon_repair/data/in/spike-out.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spike-out.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spike-out.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes-fp.wkt b/Polygon_repair/test/Polygon_repair/data/in/spikes-fp.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spikes-fp.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spikes-fp.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt b/Polygon_repair/test/Polygon_repair/data/in/spikes.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spikes.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spikes.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/spiral.wkt b/Polygon_repair/test/Polygon_repair/data/in/spiral.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/spiral.wkt rename to Polygon_repair/test/Polygon_repair/data/in/spiral.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/square-hole-rhombus.wkt b/Polygon_repair/test/Polygon_repair/data/in/square-hole-rhombus.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/square-hole-rhombus.wkt rename to Polygon_repair/test/Polygon_repair/data/in/square-hole-rhombus.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/square.wkt b/Polygon_repair/test/Polygon_repair/data/in/square.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/square.wkt rename to Polygon_repair/test/Polygon_repair/data/in/square.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/in/star.wkt b/Polygon_repair/test/Polygon_repair/data/in/star.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/in/star.wkt rename to Polygon_repair/test/Polygon_repair/data/in/star.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/back-and-forth.wkt b/Polygon_repair/test/Polygon_repair/data/ref/back-and-forth.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/back-and-forth.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/back-and-forth.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/bowtie.wkt b/Polygon_repair/test/Polygon_repair/data/ref/bowtie.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/bowtie.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/bowtie.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/bridge-edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/bridge-edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/bridge-edge.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/bridge-edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt b/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons-nesting.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons-nesting.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons-nesting.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons.wkt b/Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/crossing-polygons.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/crossing-polygons.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/edge.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-multipolygon.wkt b/Polygon_repair/test/Polygon_repair/data/ref/empty-multipolygon.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-multipolygon.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/empty-multipolygon.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-polygon.wkt b/Polygon_repair/test/Polygon_repair/data/ref/empty-polygon.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/empty-polygon.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/empty-polygon.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-all.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-all.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-all.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-as-loop.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-as-loop.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-as-loop.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-as-loop.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-carved.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-carved.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-carved.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-carved.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-filled.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-filled.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-filled.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-filled.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-outside.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-outside.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-outside.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-outside.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-partly-outside.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-partly-outside.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-partly-outside.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-partly-outside.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-edge.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-touching-edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-once.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-once.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-once.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-touching-once.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-twice.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole-touching-twice.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole-touching-twice.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole-touching-twice.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/hole.wkt b/Polygon_repair/test/Polygon_repair/data/ref/hole.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/hole.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/hole.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting-spike.wkt b/Polygon_repair/test/Polygon_repair/data/ref/nesting-spike.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting-spike.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/nesting-spike.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt b/Polygon_repair/test/Polygon_repair/data/ref/nesting.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/nesting.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/nesting.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt b/Polygon_repair/test/Polygon_repair/data/ref/not-closed.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/not-closed.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/not-closed.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-inside.wkt b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-inside.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-inside.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-inside.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-partial.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge-partial.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge-partial.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt b/Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/overlapping-edge.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/overlapping-edge.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt b/Polygon_repair/test/Polygon_repair/data/ref/point-double.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/point-double.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/point-double.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt b/Polygon_repair/test/Polygon_repair/data/ref/point.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/point.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/point.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-boundary.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-boundary.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-boundary.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spike-boundary.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-in.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-in.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-in.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spike-in.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-long.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-long.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spike-long.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-out.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spike-out.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spike-out.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spike-out.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes-fp.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spikes-fp.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes-fp.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spikes-fp.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spikes.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spikes.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spikes.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/spiral.wkt b/Polygon_repair/test/Polygon_repair/data/ref/spiral.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/spiral.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/spiral.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/square-hole-rhombus.wkt b/Polygon_repair/test/Polygon_repair/data/ref/square-hole-rhombus.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/square-hole-rhombus.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/square-hole-rhombus.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/square.wkt b/Polygon_repair/test/Polygon_repair/data/ref/square.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/square.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/square.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/data/ref/star.wkt b/Polygon_repair/test/Polygon_repair/data/ref/star.wkt similarity index 100% rename from Polygon_repair_2/test/Polygon_repair_2/data/ref/star.wkt rename to Polygon_repair/test/Polygon_repair/data/ref/star.wkt diff --git a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp similarity index 83% rename from Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp rename to Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp index 8b6d05807446..c20f298f847b 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/draw_test_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -4,10 +4,10 @@ #include #include -#include +#include #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -15,7 +15,7 @@ using Point_2 = Kernel::Point_2; using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; int main(int argc, char* argv[]) { @@ -35,12 +35,12 @@ int main(int argc, char* argv[]) { if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); } CGAL::draw(p); - rmp = CGAL::Polygon_repair_2::repair_odd_even(p); + rmp = CGAL::Polygon_repair::repair_odd_even(p); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); CGAL::draw(mp); - rmp = CGAL::Polygon_repair_2::repair_odd_even(mp); + rmp = CGAL::Polygon_repair::repair_odd_even(mp); } std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); diff --git a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp similarity index 90% rename from Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp rename to Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index bc3e937d9dec..a4d38c88e587 100644 --- a/Polygon_repair_2/test/Polygon_repair_2/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -12,7 +12,7 @@ using Point_2 = Kernel::Point_2; using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -using Polygon_repair_2 = CGAL::Polygon_repair_2::Polygon_repair_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; int main(int argc, char* argv[]) { @@ -31,11 +31,11 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p; if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); - } rmp = CGAL::Polygon_repair_2::repair_odd_even(p); + } rmp = CGAL::Polygon_repair::repair_odd_even(p); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); - rmp = CGAL::Polygon_repair_2::repair_odd_even(mp); + rmp = CGAL::Polygon_repair::repair_odd_even(mp); } std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); diff --git a/Polygon_repair_2/doc/Polygon_repair_2/examples.txt b/Polygon_repair_2/doc/Polygon_repair_2/examples.txt deleted file mode 100644 index b1318b5691ee..000000000000 --- a/Polygon_repair_2/doc/Polygon_repair_2/examples.txt +++ /dev/null @@ -1,6 +0,0 @@ -/*! -\example Polygon_repair_2/multipolygon.cpp -\example Polygon_repair_2/draw_multipolygon.cpp -\example Polygon_repair_2/repair_polygon_2.cpp -\example Polygon_repair_2/write_labeled_triangulation.cpp -*/ diff --git a/Polygon_repair_2/package_info/Polygon_repair_2/description.txt b/Polygon_repair_2/package_info/Polygon_repair_2/description.txt deleted file mode 100644 index 4fee1c792710..000000000000 --- a/Polygon_repair_2/package_info/Polygon_repair_2/description.txt +++ /dev/null @@ -1 +0,0 @@ -Package Polygon_repair_2 provides functions to repair polygons. diff --git a/Stream_support/examples/Stream_support/Polygon_WKT.cpp b/Stream_support/examples/Stream_support/Polygon_WKT.cpp index 7c7acf1b95d6..f725dcd37494 100644 --- a/Stream_support/examples/Stream_support/Polygon_WKT.cpp +++ b/Stream_support/examples/Stream_support/Polygon_WKT.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index e91af554199e..51cf67aea6c6 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/Stream_support/package_info/Stream_support/dependencies b/Stream_support/package_info/Stream_support/dependencies index 33648bd70333..825609cbfe86 100644 --- a/Stream_support/package_info/Stream_support/dependencies +++ b/Stream_support/package_info/Stream_support/dependencies @@ -7,7 +7,7 @@ Kernel_23 Modular_arithmetic Number_types Polygon -Polygon_repair_2 +Polygon_repair Profiling_tools Property_map STL_Extension From 1e77a2900348080518a443c60645f5a299bed3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 24 Aug 2023 13:38:41 +0200 Subject: [PATCH 164/520] add bibtex and fix cite --- Documentation/doc/biblio/geom.bib | 10 ++++++++++ Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Documentation/doc/biblio/geom.bib b/Documentation/doc/biblio/geom.bib index 969a1a79b5ba..f434a4aed0bd 100644 --- a/Documentation/doc/biblio/geom.bib +++ b/Documentation/doc/biblio/geom.bib @@ -152054,3 +152054,13 @@ @inproceedings{tang2009interactive year={2009}, organization={ACM} } + +@article{ledoux2014triangulation, + title={A triangulation-based approach to automatically repair GIS polygons}, + author={Ledoux, Hugo and Ohori, Ken Arroyo and Meijers, Martijn}, + journal={Computers \& Geosciences}, + volume={66}, + pages={121--131}, + year={2014}, + publisher={Elsevier} +} diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 53dd72489cd1..f18f5a803953 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -10,7 +10,7 @@ namespace CGAL { \section SectionPolygonRepair_Introduction Introduction This package implements a polygon repair method based on a constrained -triangulation \cgalCite{14cg}. Starting from a possibly invalid input +triangulation \cgalCite{ledoux2014triangulation}. Starting from a possibly invalid input in the form of a polygon, polygon with holes or multipolygon with holes, the method performs a constrained triangulation of the input edges, labels each triangle according to what it represents (exterior, polygon interior From 4613aa78a4f9b46b421a283fe6c2412720957dcd Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 24 Aug 2023 15:59:19 +0200 Subject: [PATCH 165/520] move multipolygon to polygon package --- .../Concepts/MultiPolygonWithHoles_2.h | 2 +- Polygon/doc/Polygon/PackageDescription.txt | 13 +- Polygon/doc/Polygon/Polygon.txt | 3 +- Polygon/doc/Polygon/examples.txt | 2 + Polygon/examples/Polygon/CMakeLists.txt | 1 + .../Polygon/draw_multipolygon_with_holes.cpp | 2 +- .../examples/Polygon}/multipolygon.cpp | 2 +- .../include/CGAL}/Multipolygon_with_holes_2.h | 17 +- .../CGAL/draw_multipolygon_with_holes_2.h | 211 ++++++++++++++++++ .../doc/Polygon_repair/PackageDescription.txt | 17 +- .../doc/Polygon_repair/examples.txt | 2 - .../examples/Polygon_repair/CMakeLists.txt | 8 +- .../CGAL/Polygon_repair/Polygon_repair.h | 2 +- .../Polygon_repair/draw_test_polygons.cpp | 2 +- Stream_support/include/CGAL/IO/WKT.h | 2 +- 15 files changed, 247 insertions(+), 39 deletions(-) rename {Polygon_repair/doc/Polygon_repair => Polygon/doc/Polygon}/Concepts/MultiPolygonWithHoles_2.h (97%) rename Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp => Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp (90%) rename {Polygon_repair/examples/Polygon_repair => Polygon/examples/Polygon}/multipolygon.cpp (94%) rename {Polygon_repair/include/CGAL/Polygon_repair => Polygon/include/CGAL}/Multipolygon_with_holes_2.h (91%) create mode 100644 Polygon/include/CGAL/draw_multipolygon_with_holes_2.h diff --git a/Polygon_repair/doc/Polygon_repair/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h similarity index 97% rename from Polygon_repair/doc/Polygon_repair/Concepts/MultiPolygonWithHoles_2.h rename to Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index d7f037be4d6d..1c315df5a6bb 100644 --- a/Polygon_repair/doc/Polygon_repair/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -1,4 +1,4 @@ -/*! \ingroup PkgPolygonRepairConcepts +/*! \ingroup PkgPolygon2Concepts * \cgalConcept * * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} diff --git a/Polygon/doc/Polygon/PackageDescription.txt b/Polygon/doc/Polygon/PackageDescription.txt index 1d48e74493c8..7748ee535ee9 100644 --- a/Polygon/doc/Polygon/PackageDescription.txt +++ b/Polygon/doc/Polygon/PackageDescription.txt @@ -20,7 +20,13 @@ \endcode */ /// \defgroup PkgDrawPolygonWithHoles2 Draw a 2D Polygon with Holes -/// \ingroup PkgPolygon2Ref + +/*! + \code + #include + \endcode +*/ +/// \defgroup PkgDrawMultipolygonWithHoles2 Draw a 2D Multipolygon with Holes /*! \addtogroup PkgPolygon2Ref @@ -46,11 +52,13 @@ \cgalCRPSection{Concepts} - `PolygonTraits_2` - `GeneralPolygonWithHoles_2` +- `MultipolygonWithHoles_2` \cgalCRPSection{Classes} - `CGAL::Polygon_2` - `CGAL::Polygon_with_holes_2` - `CGAL::General_polygon_with_holes_2` +- `CGAL::Multipolygon_with_holes_2` \cgalCRPSection{Global Functions} - `CGAL::area_2()` @@ -71,4 +79,7 @@ \cgalCRPSection{Draw a Polygon_with_holes_2} - \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink +\cgalCRPSection{Draw a Multipolygon_with_holes_2} +- \link PkgDrawMultipolygonWithHoles2 CGAL::draw() \endlink + */ diff --git a/Polygon/doc/Polygon/Polygon.txt b/Polygon/doc/Polygon/Polygon.txt index b21d57325e70..9e50406194b3 100644 --- a/Polygon/doc/Polygon/Polygon.txt +++ b/Polygon/doc/Polygon/Polygon.txt @@ -82,7 +82,8 @@ vertices. It additionally provides a member function `Polygon_2::vertices()` tha \subsection subsecPolygonDraw Draw a Polygon -A polygon can be visualized by calling the \link PkgDrawPolygon2 CGAL::draw

() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window (a version exists for polygon with holes, cf. \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink). +A polygon can be visualized by calling the \link PkgDrawPolygon2 CGAL::draw

() \endlink function as shown in the following example. This function opens a new window showing the given polygon. A call to this function is blocking, that is the program continues as soon as the user closes the window. Versions for polygons with holes and multipolygons with holes also exist, cf. \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink and \link PkgDrawMultipolygonWithHoles2 +CGAL::draw() \endlink. \cgalExample{Polygon/draw_polygon.cpp} diff --git a/Polygon/doc/Polygon/examples.txt b/Polygon/doc/Polygon/examples.txt index 3a608da09f40..2341aa5721d6 100644 --- a/Polygon/doc/Polygon/examples.txt +++ b/Polygon/doc/Polygon/examples.txt @@ -7,4 +7,6 @@ \example Stream_support/Polygon_WKT.cpp \example Polygon/draw_polygon.cpp \example Polygon/draw_polygon_with_holes.cpp +\example Polygon/multipolygon.cpp +\example Polygon/draw_multipolygon_with_holes.cpp */ diff --git a/Polygon/examples/Polygon/CMakeLists.txt b/Polygon/examples/Polygon/CMakeLists.txt index c9fdf0aa007a..e774514708e0 100644 --- a/Polygon/examples/Polygon/CMakeLists.txt +++ b/Polygon/examples/Polygon/CMakeLists.txt @@ -18,4 +18,5 @@ endforeach() if(CGAL_Qt5_FOUND) target_link_libraries(draw_polygon PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(draw_polygon_with_holes PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(draw_multipolygon_with_holes PUBLIC CGAL::CGAL_Basic_viewer) endif() diff --git a/Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp similarity index 90% rename from Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp rename to Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp index 3451b98de51a..5db6ececb9ee 100644 --- a/Polygon_repair/examples/Polygon_repair/draw_multipolygon.cpp +++ b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Polygon_repair/examples/Polygon_repair/multipolygon.cpp b/Polygon/examples/Polygon/multipolygon.cpp similarity index 94% rename from Polygon_repair/examples/Polygon_repair/multipolygon.cpp rename to Polygon/examples/Polygon/multipolygon.cpp index ce0cdf27f411..b98a6fda3cfd 100644 --- a/Polygon_repair/examples/Polygon_repair/multipolygon.cpp +++ b/Polygon/examples/Polygon/multipolygon.cpp @@ -1,5 +1,5 @@ #include -#include +#include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Point_2 = Kernel::Point_2; diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h similarity index 91% rename from Polygon_repair/include/CGAL/Polygon_repair/Multipolygon_with_holes_2.h rename to Polygon/include/CGAL/Multipolygon_with_holes_2.h index 2035b8fb84ce..d2744339c7ef 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -1,19 +1,22 @@ -// Copyright (c) 2023 GeometryFactory. -// All rights reserved. +// Copyright (c) 2005 +// Utrecht University (The Netherlands), +// ETH Zurich (Switzerland), +// INRIA Sophia-Antipolis (France), +// Max-Planck-Institute Saarbruecken (Germany), +// and Tel-Aviv University (Israel). All rights reserved. // -// This file is part of CGAL (www.cgal.org). +// This file is part of CGAL (www.cgal.org) // // $URL$ // $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // -// Author(s) : Ken Arroyo Ohori +// +// Author(s): Ken Arroyo Ohori #ifndef CGAL_MULTIPOLYGON_WITH_HOLES_2_H #define CGAL_MULTIPOLYGON_WITH_HOLES_2_H -#include - #include namespace CGAL { diff --git a/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h new file mode 100644 index 000000000000..814723411c7a --- /dev/null +++ b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h @@ -0,0 +1,211 @@ +// Copyright (c) 1997 +// Utrecht University (The Netherlands), +// ETH Zurich (Switzerland), +// INRIA Sophia-Antipolis (France), +// Max-Planck-Institute Saarbruecken (Germany), +// and Tel-Aviv University (Israel). All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Ken Arroyo Ohori +// Guillaume Damiand + +#ifndef CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H +#define CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H + +#include + +#ifdef DOXYGEN_RUNNING +namespace CGAL { + +/*! + * \ingroup PkgDrawMultipolygonWithHoles2 + * + * opens a new window and draws `aph`, an instance of the + * `CGAL::Multipolygon_with_holes_2` class. A call to this function is blocking, that + * is the program continues as soon as the user closes the window. This function + * requires `CGAL_Qt5`, and is only available if the macro + * `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target + * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition + * `CGAL_USE_BASIC_VIEWER`. + * \tparam PH an instance of the `CGAL::Multipolygon_with_holes_2` class. + * \param aph the multipolygon with holes to draw. + */ + +template +void draw(const MPH& aph); + +} /* namespace CGAL */ + +#endif + +#ifdef CGAL_USE_BASIC_VIEWER + +#include +#include + +namespace CGAL { + +// Viewer class for Multipolygon_with_holes_2 +template +class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { + using Base = Basic_viewer_qt; + using Mpwh = Multipolygon; + using Pwh = typename Mpwh::Polygon_with_holes_2; + using Pgn = typename Mpwh::Polygon_2; + using Pnt = typename Pgn::Point_2; + +public: + /// Construct the viewer. + /// @param parent the active window to draw + /// @param mpwh the multipolygon to view + /// @param title the title of the window + Mpwh_2_basic_viewer_qt(QWidget* parent, const Mpwh& mpwh, + const char* title = "Basic Multipolygon_with_holes_2 Viewer") : + Base(parent, title, true, true, true, false, false), + m_mpwh(mpwh) { + if (mpwh.number_of_polygons() == 0) return; + + // mimic the computation of Camera::pixelGLRatio() + auto bbox = bounding_box(); + CGAL::qglviewer::Vec minv(bbox.xmin(), bbox.ymin(), 0); + CGAL::qglviewer::Vec maxv(bbox.xmax(), bbox.ymax(), 0); + auto diameter = (maxv - minv).norm(); + m_pixel_ratio = diameter / m_height; + } + + /*! Intercept the resizing of the window. + */ + virtual void resizeGL(int width, int height) { + CGAL::QGLViewer::resizeGL(width, height); + m_width = width; + m_height = height; + CGAL::qglviewer::Vec p; + auto ratio = camera()->pixelGLRatio(p); + m_pixel_ratio = ratio; + add_elements(); + } + + /*! Obtain the pixel ratio. + */ + double pixel_ratio() const { return m_pixel_ratio; } + + /*! Compute the bounding box. + */ + CGAL::Bbox_2 bounding_box() { + Bbox_2 bbox; + if (m_mpwh.number_of_polygons() > 0) bbox = m_mpwh.polygons().front().outer_boundary().bbox(); + for (auto const& pwh: m_mpwh.polygons()) { + bbox += pwh.outer_boundary().bbox(); + } + return bbox; + } + + /*! Compute the elements of a multipolygon with holes. + */ + void add_elements() { + clear(); + + for (auto const& p: m_mpwh.polygons()) { + CGAL::IO::Color c(rand()%255,rand()%255,rand()%255); + face_begin(c); + + const Pnt* point_in_face; + const auto& outer_boundary = p.outer_boundary(); + compute_loop(outer_boundary, false); + point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); + + for (auto it = p.holes_begin(); it != p.holes_end(); ++it) { + compute_loop(*it, true); + add_point_in_face(*point_in_face); + } + + face_end(); + } + } + +protected: + /*! Compute the face + */ + void compute_loop(const Pgn& p, bool hole) { + if (hole) add_point_in_face(p.vertex(p.size()-1)); + + auto prev = p.vertices_begin(); + auto it = prev; + add_point(*it); + add_point_in_face(*it); + for (++it; it != p.vertices_end(); ++it) { + add_segment(*prev, *it); // add segment with previous point + add_point(*it); + add_point_in_face(*it); // add point in face + prev = it; + } + + // Add the last segment between the last point and the first one + add_segment(*prev, *(p.vertices_begin())); + } + + virtual void keyPressEvent(QKeyEvent* e) { + // Test key pressed: + // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); + // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } + + // Call: * add_elements() if the model changed, followed by + // * redraw() if some viewing parameters changed that implies some + // modifications of the buffers + // (eg. type of normal, color/mono) + // * update() just to update the drawing + + // Call the base method to process others/classicals key + Base::keyPressEvent(e); + } + +private: + //! The window width in pixels. + int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; + + //! The window height in pixels. + int m_height = CGAL_BASIC_VIEWER_INIT_SIZE_Y; + + //! The ratio between pixel and opengl units (in world coordinate system). + double m_pixel_ratio = 1; + + //! The polygon with holes to draw. + const Mpwh& m_mpwh; +}; + +// Specialization of draw function. +template +void draw(const CGAL::Multipolygon_with_holes_2& mpwh, + const char* title = "Multipolygon_with_holes_2 Basic Viewer") +{ +#if defined(CGAL_TEST_SUITE) + bool cgal_test_suite = true; +#else + bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); +#endif + + if (! cgal_test_suite) { + using Mpwh = CGAL::Multipolygon_with_holes_2; + using Viewer = Mpwh_2_basic_viewer_qt; + CGAL::Qt::init_ogl_context(4,3); + int argc = 1; + const char* argv[2] = {"t2_viewer", nullptr}; + QApplication app(argc, const_cast(argv)); + Viewer mainwindow(app.activeWindow(), mpwh, title); + mainwindow.add_elements(); + mainwindow.show(); + app.exec(); + } +} + +} // End namespace CGAL + +#endif // CGAL_USE_BASIC_VIEWER + +#endif // CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index 4259a9f0808f..0f63f0844b7d 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -8,14 +8,6 @@ /// \defgroup PkgPolygonRepairFunctions Functions /// \ingroup PkgPolygonRepairRef -/*! - \code - #include - \endcode -*/ -/// \defgroup PkgDrawMultipolygonWithHoles2 Draw a 2D Multipolygon with holes -/// \ingroup PkgPolygonRepairRef - /*! \addtogroup PkgPolygonRepairRef \todo check generated documentation @@ -25,7 +17,7 @@ \cgalPkgSummaryBegin \cgalPkgAuthors{Ken Arroyo Ohori} -\cgalPkgDesc{The package provides a 2D multipolygon class and algorithms to repair 2D (multi)polygons. } +\cgalPkgDesc{The package provides algorithms to repair 2D (multi)polygons. } \cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepairRef} \cgalPkgSummaryEnd @@ -41,17 +33,10 @@ \cgalClassifedRefPages -\cgalCRPSection{Concepts} -- `MultipolygonWithHoles_2` - \cgalCRPSection{Classes} -- `CGAL::Multipolygon_with_holes_2` - `CGAL::Polygon_repair` \cgalCRPSection{Functions} - `CGAL::Polygon_repair::repair()` -\cgalCRPSection{Draw a Multipolygon_with_holes_2} -- \link PkgDrawMultipolygonWithHoles2 CGAL::draw() \endlink - */ diff --git a/Polygon_repair/doc/Polygon_repair/examples.txt b/Polygon_repair/doc/Polygon_repair/examples.txt index c0a827972362..1797f7165775 100644 --- a/Polygon_repair/doc/Polygon_repair/examples.txt +++ b/Polygon_repair/doc/Polygon_repair/examples.txt @@ -1,6 +1,4 @@ /*! -\example Polygon_repair/multipolygon.cpp -\example Polygon_repair/draw_multipolygon.cpp \example Polygon_repair/repair_polygon_2.cpp \example Polygon_repair/write_labeled_triangulation.cpp */ diff --git a/Polygon_repair/examples/Polygon_repair/CMakeLists.txt b/Polygon_repair/examples/Polygon_repair/CMakeLists.txt index 930b4dc25676..4350cb892272 100644 --- a/Polygon_repair/examples/Polygon_repair/CMakeLists.txt +++ b/Polygon_repair/examples/Polygon_repair/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_Examples) -find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) +find_package(CGAL REQUIRED) # create a target per cppfile file( @@ -13,8 +13,4 @@ file( ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") -endforeach() - -if(CGAL_Qt5_FOUND) - target_link_libraries(draw_multipolygon PUBLIC CGAL::CGAL_Basic_viewer) -endif() \ No newline at end of file +endforeach() \ No newline at end of file diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 6a06b95ac28f..ac08e4ad0ef3 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -18,9 +18,9 @@ #include #include #include +#include #include -#include #include #include diff --git a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp index c20f298f847b..db22777f457a 100644 --- a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index 51cf67aea6c6..ef8c7dee1f0e 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include From 9bfe91fd405841b5653081f01da58c2cc6aee6c4 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 24 Aug 2023 16:00:55 +0200 Subject: [PATCH 166/520] missed draw function for multipolygons --- .../draw_multipolygon_with_holes_2.h | 207 ------------------ 1 file changed, 207 deletions(-) delete mode 100644 Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h diff --git a/Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h b/Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h deleted file mode 100644 index 749a30579c40..000000000000 --- a/Polygon_repair/include/CGAL/Polygon_repair/draw_multipolygon_with_holes_2.h +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) 2023 GeometryFactory. -// 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) : Ken Arroyo Ohori - -#ifndef CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H -#define CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H - -#include - -#include - -#ifdef DOXYGEN_RUNNING -namespace CGAL { - -/*! - * \ingroup PkgDrawMultipolygonWithHoles2 - * - * opens a new window and draws `aph`, an instance of the - * `CGAL::Multipolygon_with_holes_2` class. A call to this function is blocking, that - * is the program continues as soon as the user closes the window. This function - * requires `CGAL_Qt5`, and is only available if the macro - * `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target - * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition - * `CGAL_USE_BASIC_VIEWER`. - * \tparam PH an instance of the `CGAL::Multipolygon_with_holes_2` class. - * \param aph the polygon with holes to draw. - */ - -template -void draw(const PH& aph); - -} /* namespace CGAL */ - -#endif - -#ifdef CGAL_USE_BASIC_VIEWER - -#include -#include - -namespace CGAL { - -// Viewer class for Multipolygon_with_holes_2 -template -class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { - using Base = Basic_viewer_qt; - using Mpwh = Multipolygon; - using Pwh = typename Mpwh::Polygon_with_holes_2; - using Pgn = typename Mpwh::Polygon_2; - using Pnt = typename Pgn::Point_2; - -public: - /// Construct the viewer. - /// @param parent the active window to draw - /// @param mpwh the multipolygon to view - /// @param title the title of the window - Mpwh_2_basic_viewer_qt(QWidget* parent, const Mpwh& mpwh, - const char* title = "Basic Multipolygon_with_holes_2 Viewer") : - Base(parent, title, true, true, true, false, false), - m_mpwh(mpwh) { - if (mpwh.number_of_polygons() == 0) return; - - // mimic the computation of Camera::pixelGLRatio() - auto bbox = bounding_box(); - CGAL::qglviewer::Vec minv(bbox.xmin(), bbox.ymin(), 0); - CGAL::qglviewer::Vec maxv(bbox.xmax(), bbox.ymax(), 0); - auto diameter = (maxv - minv).norm(); - m_pixel_ratio = diameter / m_height; - } - - /*! Intercept the resizing of the window. - */ - virtual void resizeGL(int width, int height) { - CGAL::QGLViewer::resizeGL(width, height); - m_width = width; - m_height = height; - CGAL::qglviewer::Vec p; - auto ratio = camera()->pixelGLRatio(p); - m_pixel_ratio = ratio; - add_elements(); - } - - /*! Obtain the pixel ratio. - */ - double pixel_ratio() const { return m_pixel_ratio; } - - /*! Compute the bounding box. - */ - CGAL::Bbox_2 bounding_box() { - Bbox_2 bbox; - if (m_mpwh.number_of_polygons() > 0) bbox = m_mpwh.polygons().front().outer_boundary().bbox(); - for (auto const& pwh: m_mpwh.polygons()) { - bbox += pwh.outer_boundary().bbox(); - } - return bbox; - } - - /*! Compute the elements of a multipolygon with holes. - */ - void add_elements() { - clear(); - - for (auto const& p: m_mpwh.polygons()) { - CGAL::IO::Color c(rand()%255,rand()%255,rand()%255); - face_begin(c); - - const Pnt* point_in_face; - const auto& outer_boundary = p.outer_boundary(); - compute_loop(outer_boundary, false); - point_in_face = &(outer_boundary.vertex(outer_boundary.size()-1)); - - for (auto it = p.holes_begin(); it != p.holes_end(); ++it) { - compute_loop(*it, true); - add_point_in_face(*point_in_face); - } - - face_end(); - } - } - -protected: - /*! Compute the face - */ - void compute_loop(const Pgn& p, bool hole) { - if (hole) add_point_in_face(p.vertex(p.size()-1)); - - auto prev = p.vertices_begin(); - auto it = prev; - add_point(*it); - add_point_in_face(*it); - for (++it; it != p.vertices_end(); ++it) { - add_segment(*prev, *it); // add segment with previous point - add_point(*it); - add_point_in_face(*it); // add point in face - prev = it; - } - - // Add the last segment between the last point and the first one - add_segment(*prev, *(p.vertices_begin())); - } - - virtual void keyPressEvent(QKeyEvent* e) { - // Test key pressed: - // const ::Qt::KeyboardModifiers modifiers = e->modifiers(); - // if ((e->key()==Qt::Key_PageUp) && (modifiers==Qt::NoButton)) { ... } - - // Call: * add_elements() if the model changed, followed by - // * redraw() if some viewing parameters changed that implies some - // modifications of the buffers - // (eg. type of normal, color/mono) - // * update() just to update the drawing - - // Call the base method to process others/classicals key - Base::keyPressEvent(e); - } - -private: - //! The window width in pixels. - int m_width = CGAL_BASIC_VIEWER_INIT_SIZE_X; - - //! The window height in pixels. - int m_height = CGAL_BASIC_VIEWER_INIT_SIZE_Y; - - //! The ratio between pixel and opengl units (in world coordinate system). - double m_pixel_ratio = 1; - - //! The polygon with holes to draw. - const Mpwh& m_mpwh; -}; - -// Specialization of draw function. -template -void draw(const CGAL::Multipolygon_with_holes_2& mpwh, - const char* title = "Multipolygon_with_holes_2 Basic Viewer") -{ -#if defined(CGAL_TEST_SUITE) - bool cgal_test_suite = true; -#else - bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE"); -#endif - - if (! cgal_test_suite) { - using Mpwh = CGAL::Multipolygon_with_holes_2; - using Viewer = Mpwh_2_basic_viewer_qt; - CGAL::Qt::init_ogl_context(4,3); - int argc = 1; - const char* argv[2] = {"t2_viewer", nullptr}; - QApplication app(argc, const_cast(argv)); - Viewer mainwindow(app.activeWindow(), mpwh, title); - mainwindow.add_elements(); - mainwindow.show(); - app.exec(); - } -} - -} // End namespace CGAL - -#endif // CGAL_USE_BASIC_VIEWER - -#endif // CGAL_DRAW_MULTIPOLYGON_WITH_HOLES_2_H From 3e1537dfbb451c25a74882e5543a37c9311d1c58 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 24 Aug 2023 16:01:35 +0200 Subject: [PATCH 167/520] move old examples to tests --- Polygon_repair/doc/Polygon_repair/examples.txt | 1 - .../Polygon_repair/write_labeled_triangulation.cpp | 0 .../Polygon_repair/write_repaired_polygons.cpp} | 0 3 files changed, 1 deletion(-) rename Polygon_repair/{examples => test}/Polygon_repair/write_labeled_triangulation.cpp (100%) rename Polygon_repair/{examples/Polygon_repair/nasty_polygons.cpp => test/Polygon_repair/write_repaired_polygons.cpp} (100%) diff --git a/Polygon_repair/doc/Polygon_repair/examples.txt b/Polygon_repair/doc/Polygon_repair/examples.txt index 1797f7165775..278e5049cf22 100644 --- a/Polygon_repair/doc/Polygon_repair/examples.txt +++ b/Polygon_repair/doc/Polygon_repair/examples.txt @@ -1,4 +1,3 @@ /*! \example Polygon_repair/repair_polygon_2.cpp -\example Polygon_repair/write_labeled_triangulation.cpp */ diff --git a/Polygon_repair/examples/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp similarity index 100% rename from Polygon_repair/examples/Polygon_repair/write_labeled_triangulation.cpp rename to Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp diff --git a/Polygon_repair/examples/Polygon_repair/nasty_polygons.cpp b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp similarity index 100% rename from Polygon_repair/examples/Polygon_repair/nasty_polygons.cpp rename to Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp From 982059556a7bc00f5b02d6f26687b6ada8da6152 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 24 Aug 2023 16:34:46 +0200 Subject: [PATCH 168/520] doc with history and w/o triangulation, mp --- .../doc/Polygon_repair/Polygon_repair.txt | 137 ++++++------------ 1 file changed, 46 insertions(+), 91 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index f18f5a803953..39f44deed4e8 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -9,15 +9,14 @@ namespace CGAL { \section SectionPolygonRepair_Introduction Introduction -This package implements a polygon repair method based on a constrained -triangulation \cgalCite{ledoux2014triangulation}. Starting from a possibly invalid input -in the form of a polygon, polygon with holes or multipolygon with holes, -the method performs a constrained triangulation of the input edges, labels -each triangle according to what it represents (exterior, polygon interior -or hole), and reconstructs the polygon(s) represented by the triangulation. -The method returns a valid output stored in a multipolygon with holes. - -Different triangulation and labelling heuristics are possible, but +This package implements a polygon repair method. Starting from possibly +invalid input in the form of a polygon, polygon with holes or multipolygon +with holes, the method computes an arrangement of the input edges, labels +each face according to what it represents (exterior, polygon interior +or hole), and reconstructs the polygon(s) represented by the arrangement. +The method returns valid output stored in a multipolygon with holes. + +Different arrangement and labelling heuristics are possible, but currently only the odd-even heuristic is implemented in the package. This heuristic results in areas that are alternately assigned as polygon interiors and exterior/holes each time that an input edge is passed. @@ -56,6 +55,8 @@ Valid: (a) polygon, (b-c) polygons with holes, (d-e) multipolygons with holes. (c) and (e) show cases where boundaries intersect tangentially at a single vertex. \cgalFigureEnd +FIGURE with invalid + \subsection SubsectionPolygonRepair_Output Stricter Conditions for Output The conditions listed above are sufficient to define valid polygons, polygons @@ -78,64 +79,50 @@ lexicographic order Broadly, the algorithm consists of three steps: --# Constrained triangulation: the edges in the polygon, polygon with -holes or multipolygon with holes are added as constraints in the triangulation. --# Labeling of the faces: sets of faces forming contiguous regions that -are accessible from each other by following adjacency relationships without -passing through a constrained edge are labeled with ids according to -what they represent (exterior, polygon interior or hole). The faces iteratively -adjacent to the infinite face without passing through a constrained edge are -labeled as the exterior. --# Reconstruction of the multipolygon: each boundary of each polygon -with holes is reconstructed by finding a polygon interior triangle that is -adjacent to the exterior or a hole, then following the boundary in an -orientation that is counter-clockwise for outer boundaries and clockwise for -inner boundaries. This orientation and the polygon interior id is used to -determine what polygon with holes it belongs to and whether it is an outer or -an inner boundary. +-# Arrangement: the edges in the polygon, polygon with +holes or multipolygon with holes are added as edges in the arrangement. +-# Labeling of the faces: the resulting faces are labeled with ids +according to what they represent (exterior, polygon interior or hole). +-# Reconstruction of the multipolygon: each boundary is reconstructed, +then these are assembled into individual polygons with holes and put into a +single multipolygon with holes. FIGURE -\subsection SubsectionPolygonRepair_Triangulation Constrained triangulation +\subsection SubsectionPolygonRepair_Arrangement Arrangement For the purposes of the repair operation, the input polygon, polygon with holes or multipolygon is merely used as a container of input line segments. These line -segments are added to the constrained triangulation as constraints. +segments are added to the arrangement as edges. Internally, this is done using +a constrained triangulation where they added as constraints. With the odd-even heuristic, only the edges that are present an odd number of -times in the input will be constrained edges in the triangulation. +times in the input will be edges in the final arrangement. When these edges are only partially overlapping, only the parts that overlap -an odd number of times will be constrained edges in the triangulation. +an odd number of times will be edges in the final arrangement. This procedure is done in two steps: 1. preprocessing to eliminate identical -edges that are present an even number of times, and 2. an odd-even constraint -counting mechanism that erases existing constraints when new overlapping ones -are added. +edges that are present an even number of times, and 2. adding edges incrementally +while applying an odd-even counting mechanism, which erases existing (parts of) +edges when new overlapping ones are added. \subsection SubsectionPolygonRepair_Labeling Labeling -First, all of the polygon exterior is labeled. For this, the faces that can be -accessed by following the adjacency relationships from the infinite face without -passing through a constrained edge are labeled as exterior faces. +First, the polygon exterior is labeled. For this, all of the faces that can be +accessed from the exterior without passing through an edge are labeled as exterior +faces. -Then, with the odd-even heuristic, groups of faces that are adjacent to each other -without passing through a constrained edge are labeled. The label applied alternates -between polygon interior and hole every time that a constrained edge is passed. +Then, all other faces are labeled. For the odd-even heuristic, the label applied +alternates between polygon interior and hole every time that an edge is passed. \subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the multipolygon -The algorithm reconstructs the multipolygon boundary by boundary, repeating the -process until all boundaries have been reconstructed. -Starting from a triangulation face with a polygon interior label that is adjacent -to a face with an exterior or hole label, the boundary of the polygon that contains -the edge between the two triangles is reconstructed. -The successive edges of the boundary is obtained by rotating counter-clockwise -around their common vertex, resulting in counter-clockwise cycles for outer -boundaries and clockwise cycles for inner boundaries. - -Finally, the boundaries are assembled into multipolygons using the unique labels -to know which polygon inner/outer boundaries belong to, and using the orientation -to distinguish between outer and inner boundaries. +The algorithm reconstructs the multipolygon boundary by boundary, obtaining +counter-clockwise cycles for outer boundaries and clockwise cycles for inner +boundaries. Once all boundaries have been reconstructed, the boundaries are assembled +into multipolygons using the face labels to know which polygon with holes inner/outer +boundaries belong to, and using the orientation to distinguish between the outer and +inner boundaries of each polygon with holes. \subsection SubsectionPolygonRepair_Notes Notes on the Output @@ -152,37 +139,6 @@ with holes has zero holes and extract these if needed. \section SectionPolygonRepair_Examples Examples -\subsection SubsectionPolygonRepair_Multipolygon The multipolygon with holes class - -The following example shows the creation of a multipolygon with holes and the traversal -of the polygons in it. - -\cgalExample{Polygon_repair/multipolygon.cpp} - -\subsection SubsectionPolygonRepair_WKTDraw Draw a multipolygon - -A multipolygon with holes can be visualized by calling the \link PkgPolygonRepairRef -CGAL::draw() \endlink function as shown in the following example. -This function opens a new window showing the given polygon. A call to this function -is blocking, that is the program continues as soon as the user closes the window. -Versions for polygons and polygons with holes also exist, cf. \link PkgDrawPolygon2 -CGAL::draw

() \endlink and \link PkgDrawPolygonWithHoles2 CGAL::draw() \endlink. - -The multipolygon with holes shown in this example is created using the well-known text -(WKT) reader, which can read and write multipolygons with holes. - -\cgalExample{Polygon_repair/draw_multipolygon.cpp} - -This function requires `CGAL_Qt5`, and is only available if the macro -`CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target -`CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition -`CGAL_USE_BASIC_VIEWER`. - -\cgalFigureBegin{draw_multipolygon, draw_multipolygon.png} -Result of the run of the draw_multipolygon program. A window shows the multipolygon -with holes and allows to navigate through the scene. -\cgalFigureEnd - \subsection SubsectionPolygonRepair_Repair Repairing a (multi)polygon It's possible to repair a polygon, polygon with holes or multipolygon with holes @@ -197,20 +153,19 @@ a hole. \cgalExample{Polygon_repair/repair_polygon_2.cpp} -\subsection SubsectionPolygonRepair_RepairHiddenApi Step by step repairing - -The following example shows to do the repair process step by step, including how -to extract the labeled triangulation, which is written as a GeoJSON file consisting -of the individual triangles with their labels. +\section SectionPolygonRepair_Performance Performance -\cgalFigureBegin{triangulation, triangulation.png} -(a) A multipolygon consisting of two polygons with one hole each, where one polygon -is inside the hole of the other (b) The labeled triangulation of the multipolygon -\cgalFigureEnd +\section SectionPolygonRepair_History History -\cgalExample{Polygon_repair/write_labeled_triangulation.cpp} +The polygon repair method as originally developed is described by Ledoux et al. +\cgalCite{ledoux2014triangulation} and implemented in the prepair software. +This package is a reimplementation of the method with a new approach to label +and reconstruct the multipolygons. It also incorporates improvements later +added to prepair, such as the application of the odd-even counting heuristic +to edges. -\section SectionPolygonRepair_Performance Performance +Ken Arroyo Ohori made a prototype version of this package for the Google Summer of +Code 2023 under the mentorship of Sébastien Loriot and Andreas Fabri. */ } /* namespace CGAL */ From 5c4e48e5bf310a5d46772475cf70e5126809bd35 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 24 Aug 2023 17:10:12 +0200 Subject: [PATCH 169/520] fix dependencies --- Polygon_repair/package_info/Polygon_repair/dependencies | 1 - Stream_support/package_info/Stream_support/dependencies | 1 - 2 files changed, 2 deletions(-) diff --git a/Polygon_repair/package_info/Polygon_repair/dependencies b/Polygon_repair/package_info/Polygon_repair/dependencies index f30877d02528..3678863c6925 100644 --- a/Polygon_repair/package_info/Polygon_repair/dependencies +++ b/Polygon_repair/package_info/Polygon_repair/dependencies @@ -5,7 +5,6 @@ Circulator Distance_2 Distance_3 Filtered_kernel -GraphicsView Hash_map Homogeneous_kernel Installation diff --git a/Stream_support/package_info/Stream_support/dependencies b/Stream_support/package_info/Stream_support/dependencies index 825609cbfe86..86fd53b13ace 100644 --- a/Stream_support/package_info/Stream_support/dependencies +++ b/Stream_support/package_info/Stream_support/dependencies @@ -7,7 +7,6 @@ Kernel_23 Modular_arithmetic Number_types Polygon -Polygon_repair Profiling_tools Property_map STL_Extension From 8192774070ac9205a89ba325fd258f8d907f288b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 25 Aug 2023 11:19:20 +0200 Subject: [PATCH 170/520] info about multipolygons in polygon manual --- Polygon/doc/Polygon/Polygon.txt | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Polygon/doc/Polygon/Polygon.txt b/Polygon/doc/Polygon/Polygon.txt index 9e50406194b3..284157f95ef7 100644 --- a/Polygon/doc/Polygon/Polygon.txt +++ b/Polygon/doc/Polygon/Polygon.txt @@ -37,6 +37,19 @@ points, but little more. Especially, computed values are not cached. That is, when the `Polygon_2::is_simple()` member function is called twice or more, the result is computed each time anew. +\section secPolygonWithHole Polygons with Holes and Multipolygons with Holes + +This package also provides classes to represent polygons with holes and multipolygons with holes. + +For polygons with holes, these are `Polygon_with_holes_2` and `General_polygon_with_holes_2`. They can store a polygon that represents +the outer boundary and a sequence of polygons that represent the holes. + +For multipolygons with holes, there is `Multipolygon_with_holes_2`. +It stores a sequence of polygons with holes. + +These classes do not add any semantic requirements on the simplicity +or orientation of their boundary polygons. + \section secPolygonExamples Examples \subsection subsecPolygon The Polygon Class @@ -46,6 +59,13 @@ some member functions. \cgalExample{Polygon/Polygon.cpp} +\subsection SubsectionPolygonRepair_Multipolygon The multipolygon with holes class + +The following example shows the creation of a multipolygon with holes and the traversal +of the polygons in it. + +\cgalExample{Polygon/multipolygon.cpp} + \cgalFigureBegin{polygon2_algo,pgn_algos.png} A polygon and some points \cgalFigureEnd @@ -95,15 +115,4 @@ Result of the run of the draw_polygon program. A window shows the polygon and al \cgalFigureEnd */ - -\section secPolygonWithHole Polygons with Holes - -This package also provides two classes to represent polygons with holes, -namely `Polygon_with_holes_2` and `General_polygon_with_holes_2`. They -can store a polygon that represents the outer boundary and a sequence -of polygons that represent the holes. - -These classes do not add any semantic requirements on the simplicity -or orientation of their boundary polygons. - } From 9ae9f54eeb3e694cef1fa2bed4547726c1bae51a Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 25 Aug 2023 11:26:17 +0200 Subject: [PATCH 171/520] remove old figs From 188188e1e70bf2ddc89a8c68d530747a01743652 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 25 Aug 2023 11:26:51 +0200 Subject: [PATCH 172/520] svg --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 39f44deed4e8..25c54989794b 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -50,7 +50,7 @@ are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon with holes (with only one polygon). -\cgalFigureBegin{multipolygons, multipolygons.png} +\cgalFigureBegin{multipolygons, multipolygons.svg} Valid: (a) polygon, (b-c) polygons with holes, (d-e) multipolygons with holes. (c) and (e) show cases where boundaries intersect tangentially at a single vertex. \cgalFigureEnd From f4c602144faa5d1c36041c5bf43dd3d8445628fd Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 25 Aug 2023 11:27:15 +0200 Subject: [PATCH 173/520] in/out will be in table --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 25c54989794b..137c3a40f3ff 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -87,7 +87,7 @@ according to what they represent (exterior, polygon interior or hole). then these are assembled into individual polygons with holes and put into a single multipolygon with holes. -FIGURE +TABLE IN/OUT \subsection SubsectionPolygonRepair_Arrangement Arrangement @@ -145,12 +145,6 @@ It's possible to repair a polygon, polygon with holes or multipolygon with holes using the odd-even rule by calling the `repair_odd_even` function as shown in the following example. This function returns a repaired multipolygon with holes. -\cgalFigureBegin{bridge_edge, bridge_edge.png} -(a) Before repair: A polygon with a single loop that implicitly creates a hole -using a bridge edge (b) After repair: the same area represented as a polygon with -a hole. -\cgalFigureEnd - \cgalExample{Polygon_repair/repair_polygon_2.cpp} \section SectionPolygonRepair_Performance Performance From ca3ad80390adaaee2f97a4ca789562951993a4fe Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 25 Aug 2023 12:03:38 +0200 Subject: [PATCH 174/520] valid and invalid examples --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 137c3a40f3ff..bf89b53a1da4 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -50,12 +50,14 @@ are allowed to intersect tangentially at their common vertices. Note that a valid polygon with holes can also be represented as a valid multipolygon with holes (with only one polygon). -\cgalFigureBegin{multipolygons, multipolygons.svg} -Valid: (a) polygon, (b-c) polygons with holes, (d-e) multipolygons with holes. +\cgalFigureBegin{valid, valid.svg} +Valid: (a) polygon, (b-c) polygons with holes, and (d-e) multipolygons with holes. (c) and (e) show cases where boundaries intersect tangentially at a single vertex. \cgalFigureEnd -FIGURE with invalid +\cgalFigureBegin{invalid, invalid.svg} +Invalid: (a) self-intersecting polygon self-intersection, (b) self-touching polygon, (c-d) polygons with badly nested holes, (e) polygon with hole touching at edge, (f) polygon with hole that separates interior into two parts, (g) multipolygon with overlapping polygons, and (h) multipolygon with polygons that touch at an edge. +\cgalFigureEnd \subsection SubsectionPolygonRepair_Output Stricter Conditions for Output From fff924b5def5ab8a96ac7a891dc45e25b8562054 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 25 Aug 2023 15:57:14 +0200 Subject: [PATCH 175/520] in/out examples --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index bf89b53a1da4..5235a77243dc 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -89,7 +89,9 @@ according to what they represent (exterior, polygon interior or hole). then these are assembled into individual polygons with holes and put into a single multipolygon with holes. -TABLE IN/OUT +\cgalFigureBegin{inout, inout.svg} +Examples of polygons with holes (a-d) and multipolygons with holes (e-h) before (left) and after (right) being repaired. +\cgalFigureEnd \subsection SubsectionPolygonRepair_Arrangement Arrangement From db09bc96ce6933af2b49083f1b5361a309e75ba5 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 29 Aug 2023 17:07:52 +0200 Subject: [PATCH 176/520] some performance info --- .../doc/Polygon_repair/Polygon_repair.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 5235a77243dc..cd3969ba832e 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -153,10 +153,24 @@ following example. This function returns a repaired multipolygon with holes. \section SectionPolygonRepair_Performance Performance +The method can repair large invalid polygons of millions of vertices in a few +seconds as long as the number of intersections between line segments is limited. +This is a realistic assumption with many invalid data sets, which only have +relatively minor issues involving a small number of their vertices/edges. +However, it is worth noting that there can be a potentially quadratic number of +intersection between edges in the worst case, leading to much worse performance +since all of these intersections need to be calculated in the overlay. + +| Polygon | Vertices | Holes | Time | +| :----: | :----: | :----: | | +| ![ ](Corine180927.jpg) | 101973 | 298 | 0.652 sec | +| ![ ](Corine2018418.jpg) | 43925 | 125 | 0.190 sec | + \section SectionPolygonRepair_History History The polygon repair method as originally developed is described by Ledoux et al. -\cgalCite{ledoux2014triangulation} and implemented in the prepair software. +\cgalCite{ledoux2014triangulation} and implemented in the +prepair software. This package is a reimplementation of the method with a new approach to label and reconstruct the multipolygons. It also incorporates improvements later added to prepair, such as the application of the odd-even counting heuristic From 423156595803f263e1b29e41a70bfa63a0454e41 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 29 Aug 2023 17:37:10 +0200 Subject: [PATCH 177/520] detect kernel automatically --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index ac08e4ad0ef3..2af593720ae9 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -80,7 +80,7 @@ class Polygon_repair { using Face_base = CGAL::Constrained_triangulation_face_base_2; using Face_base_with_repair_info = CGAL::Triangulation_face_base_with_repair_info_2; using Triangulation_data_structure = CGAL::Triangulation_data_structure_2; - using Tag = CGAL::Exact_predicates_tag; // assumed for now + using Tag = typename std::conditional::value, CGAL::Exact_predicates_tag, CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; using Triangulation = Triangulation_with_odd_even_constraints_2; From 07c671e6e951e507397531374a7a78cffa018709 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 29 Aug 2023 19:13:11 +0200 Subject: [PATCH 178/520] Convert methods to functor objects for consistency (proof-of-concept) --- Orthtree/include/CGAL/Orthtree.h | 8 +- .../include/CGAL/Orthtree_traits_3_base.h | 2 +- .../include/CGAL/Orthtree_traits_point_2.h | 74 ++++++++++-------- .../include/CGAL/Orthtree_traits_point_3.h | 78 +++++++++++-------- .../include/CGAL/Orthtree_traits_point_d.h | 77 ++++++++++-------- 5 files changed, 135 insertions(+), 104 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index acc045bc0e7f..5c465ff914ee 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -225,7 +225,7 @@ class Orthtree { // init bbox with first values found - auto [bbox_min, bbox_max] = m_traits.root_node_bbox(); + auto [bbox_min, bbox_max] = m_traits.root_node_bbox_object()(); // Dilate the bounding box Array bbox_centroid; @@ -246,7 +246,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = construct_point_d_from_array(bbox_min); m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); - data(root()) = m_traits.root_node_contents(); + data(root()) = m_traits.root_node_contents_object()(); } /// @} @@ -830,7 +830,7 @@ class Orthtree { Point center = barycenter(n); // Add the node's points to its children - m_traits.distribute_node_contents(n, *this, center); + m_traits.distribute_node_contents_object()(n, *this, center); } /*! @@ -1031,7 +1031,7 @@ class Orthtree { // Pair that point with its distance from the search point Node_element_with_distance current_point_with_distance = - {p, squared_distance(m_traits.get_element(p), search_bounds.center())}; + {p, squared_distance(m_traits.get_element_object()(p), search_bounds.center())}; // Check if the new point is within the bounds if (current_point_with_distance.distance < search_bounds.squared_radius()) { diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h index 1702da407745..781fcd99e32f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -45,7 +45,7 @@ struct Orthtree_traits_3_base { using Bbox_d = Bbox_3; using FT = typename K::FT; using Point_d = typename K::Point_3; - using Sphere_d = typename K::Circle_3; + using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; using Array = std::array; // todo: This should have a more descriptive name diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index 5a4db5041f52..9820b49e1ed5 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -51,6 +51,7 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { /// @{ using Self = Orthtree_traits_point_2; + using Tree = Orthtree; // todo: looking for better names using Node_data = boost::iterator_range; @@ -108,46 +109,55 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { */ Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - std::pair root_node_bbox() const { - - typename Self::Array bbox_min; - typename Self::Array bbox_max; - Orthtrees::internal::Cartesian_ranges cartesian_range; - - // init bbox with first values found - { - const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = x; - bbox_max[i] = x; - ++i; + auto root_node_bbox_object() const { + return [&]() -> std::pair { + + typename Self::Array bbox_min; + typename Self::Array bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } } - } - // Expand bbox to contain all points - for (const auto& p: m_point_set) { - const typename Self::Point_d& point = get(m_point_map, p); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++i; + // Expand bbox to contain all points + for (const auto& p: m_point_set) { + const typename Self::Point_d& point = get(m_point_map, p); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } } - } - return {bbox_min, bbox_max}; + return {bbox_min, bbox_max}; + }; } - Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + auto root_node_contents_object() const { + return [&]() -> typename Self::Node_data { + return {m_point_set.begin(), m_point_set.end()}; + }; + } - template - void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) { - CGAL_precondition(!tree.is_leaf(n)); - reassign_points(tree, m_point_map, n, center, tree.data(n)); + auto distribute_node_contents_object() const { + return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); + }; } - typename Self::Point_d get_element(const Node_data_element& index) const { - return get(m_point_map, index); + auto get_element_object() const { + return [&](const Node_data_element& index) -> typename Self::Point_d { + return get(m_point_map, index); + }; } /// @} diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index be0717a87117..294acfb6950c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -51,6 +51,7 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { /// @{ using Self = Orthtree_traits_point_3; + using Tree = Orthtree; // todo: looking for better names using Node_data = boost::iterator_range; @@ -80,8 +81,8 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { struct Construct_bbox_d { typename Self::Bbox_d operator()(const typename Self::Array& min, - const typename Self::Array& max) const { - return Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); + const typename Self::Array& max) const { + return typename Self::Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); } }; @@ -107,46 +108,55 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { */ Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - std::pair root_node_bbox() const { - - typename Self::Array bbox_min; - typename Self::Array bbox_max; - Orthtrees::internal::Cartesian_ranges cartesian_range; - - // init bbox with first values found - { - const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = x; - bbox_max[i] = x; - ++i; + auto root_node_bbox_object() const { + return [&]() -> std::pair { + + typename Self::Array bbox_min; + typename Self::Array bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } } - } - // Expand bbox to contain all points - for (const auto& p: m_point_set) { - const typename Self::Point_d& point = get(m_point_map, p); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++i; + // Expand bbox to contain all points + for (const auto& p: m_point_set) { + const typename Self::Point_d& point = get(m_point_map, p); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } } - } - return {bbox_min, bbox_max}; + return {bbox_min, bbox_max}; + }; } - typename Self::Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + auto root_node_contents_object() const { + return [&]() -> typename Self::Node_data { + return {m_point_set.begin(), m_point_set.end()}; + }; + } - template - void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) { - CGAL_precondition(!tree.is_leaf(n)); - reassign_points(tree, m_point_map, n, center, tree.data(n)); + auto distribute_node_contents_object() const { + return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); + }; } - typename Self::Point_d get_element(const Node_data_element& index) const { - return get(m_point_map, index); + auto get_element_object() const { + return [&](const Node_data_element& index) -> typename Self::Point_d { + return get(m_point_map, index); + }; } /// @} diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 1937b96e28e8..52ef0e7413be 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -88,6 +88,7 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base; + using Tree = Orthtree; using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; @@ -116,7 +117,8 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base root_node_bbox() const { - - typename Self::Array bbox_min; - typename Self::Array bbox_max; - Orthtrees::internal::Cartesian_ranges cartesian_range; - - // init bbox with first values found - { - const auto& point = get(m_point_map, *(m_point_set.begin())); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = x; - bbox_max[i] = x; - ++i; + auto root_node_bbox_object() const { + return [&]() -> std::pair { + + typename Self::Array bbox_min; + typename Self::Array bbox_max; + Orthtrees::internal::Cartesian_ranges cartesian_range; + + // init bbox with first values found + { + const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = x; + bbox_max[i] = x; + ++i; + } } - } - // Expand bbox to contain all points - for (const auto& p: m_point_set) { - const auto& point = get(m_point_map, p); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++i; + // Expand bbox to contain all points + for (const auto& p: m_point_set) { + const typename Self::Point_d& point = get(m_point_map, p); + std::size_t i = 0; + for (const typename Self::FT& x: cartesian_range(point)) { + bbox_min[i] = (std::min)(x, bbox_min[i]); + bbox_max[i] = (std::max)(x, bbox_max[i]); + ++i; + } } - } - return {bbox_min, bbox_max}; + return {bbox_min, bbox_max}; + }; } - Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } + auto root_node_contents_object() const { + return [&]() -> typename Self::Node_data { + return {m_point_set.begin(), m_point_set.end()}; + }; + } - template - void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) { - CGAL_precondition(!tree.is_leaf(n)); - reassign_points(tree, m_point_map, n, center, tree.data(n)); + auto distribute_node_contents_object() const { + return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) { + CGAL_precondition(!tree.is_leaf(n)); + reassign_points(tree, m_point_map, n, center, tree.data(n)); + }; } - typename Self::Point_d get_element(const Node_data_element& index) const { - return get(m_point_map, index); + auto get_element_object() const { + return [&](const Node_data_element& index) -> typename Self::Point_d { + return get(m_point_map, index); + }; } /// @} From 69a01c7eed8a1d97c1a2800773560082463661ed Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 13:27:02 +0200 Subject: [PATCH 179/520] unique_edges should be in class, clear all variables --- .../include/CGAL/Polygon_repair/Polygon_repair.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 2af593720ae9..9281bad46d97 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -124,8 +124,6 @@ class Polygon_repair { // Add edges of the polygon to the triangulation void add_to_triangulation_odd_even(const Polygon_2& polygon) { - std::unordered_set, - boost::hash>> unique_edges; // Get unique edges for (auto const& edge: polygon.edges()) { @@ -167,8 +165,6 @@ class Polygon_repair { // Add edges of the polygon to the triangulation void add_to_triangulation_odd_even(const Polygon_with_holes_2& polygon) { - std::unordered_set, - boost::hash>> unique_edges; // Get unique edges for (auto const& edge: polygon.outer_boundary().edges()) { @@ -219,8 +215,6 @@ class Polygon_repair { // Add edges of the polygon to the triangulation void add_to_triangulation_odd_even(const Multipolygon_with_holes_2& multipolygon) { - std::unordered_set, - boost::hash>> unique_edges; // Get unique edges for (auto const& polygon: multipolygon.polygons()) { @@ -439,6 +433,11 @@ class Polygon_repair { // Erases the triangulation. void clear() { t.clear(); + unique_edges.clear(); + mp.clear(); + number_of_polygons = 0; + number_of_holes = 0; + search_start = Triangulation::Face_handle(); } /// @} @@ -459,6 +458,8 @@ class Polygon_repair { protected: Triangulation t; + std::unordered_set, + boost::hash>> unique_edges; Multipolygon_with_holes_2 mp; int number_of_polygons, number_of_holes; typename Triangulation::Face_handle search_start; From 79edf301c950bd27bd4c3930389026da1ce117e0 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 13:27:11 +0200 Subject: [PATCH 180/520] explain counting --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index cd3969ba832e..f950d5630d67 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -174,7 +174,7 @@ The polygon repair method as originally developed is described by Ledoux et al. This package is a reimplementation of the method with a new approach to label and reconstruct the multipolygons. It also incorporates improvements later added to prepair, such as the application of the odd-even counting heuristic -to edges. +to edges, which enables correct counting even on partially overlapping edges. Ken Arroyo Ohori made a prototype version of this package for the Google Summer of Code 2023 under the mentorship of Sébastien Loriot and Andreas Fabri. From d174f6a44502e3f2e203631e0df1ea461d98278d Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 13:27:23 +0200 Subject: [PATCH 181/520] doc formatting --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index f950d5630d67..6547a97d482f 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -56,7 +56,10 @@ Valid: (a) polygon, (b-c) polygons with holes, and (d-e) multipolygons with hole \cgalFigureEnd \cgalFigureBegin{invalid, invalid.svg} -Invalid: (a) self-intersecting polygon self-intersection, (b) self-touching polygon, (c-d) polygons with badly nested holes, (e) polygon with hole touching at edge, (f) polygon with hole that separates interior into two parts, (g) multipolygon with overlapping polygons, and (h) multipolygon with polygons that touch at an edge. +Invalid: (a) self-intersecting polygon self-intersection, (b) self-touching polygon, +(c-d) polygons with badly nested holes, (e) polygon with hole touching at edge, +(f) polygon with hole that separates interior into two parts, (g) multipolygon +with overlapping polygons, and (h) multipolygon with polygons that touch at an edge. \cgalFigureEnd \subsection SubsectionPolygonRepair_Output Stricter Conditions for Output @@ -90,7 +93,8 @@ then these are assembled into individual polygons with holes and put into a single multipolygon with holes. \cgalFigureBegin{inout, inout.svg} -Examples of polygons with holes (a-d) and multipolygons with holes (e-h) before (left) and after (right) being repaired. +Examples of polygons with holes (a-d) and multipolygons with holes +(e-h) before (left) and after (right) being repaired. \cgalFigureEnd \subsection SubsectionPolygonRepair_Arrangement Arrangement From 2d8f7d483db00dce36c44c1b543120b375e5024d Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 30 Aug 2023 15:32:38 +0200 Subject: [PATCH 182/520] Rename Properties.h for clarity --- Orthtree/include/CGAL/Orthtree.h | 2 +- Point_set_3/include/CGAL/Point_set_3.h | 2 +- .../include/CGAL/{Properties.h => Property_container.h} | 0 Property_map/test/Property_map/test_Properties.cpp | 2 +- Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h | 2 +- Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename Property_map/include/CGAL/{Properties.h => Property_container.h} (100%) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 5c465ff914ee..f1c7acdd73a4 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 4da5ac0c522e..8f314d5fc6b5 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -17,7 +17,7 @@ #include -#include +#include #include diff --git a/Property_map/include/CGAL/Properties.h b/Property_map/include/CGAL/Property_container.h similarity index 100% rename from Property_map/include/CGAL/Properties.h rename to Property_map/include/CGAL/Property_container.h diff --git a/Property_map/test/Property_map/test_Properties.cpp b/Property_map/test/Property_map/test_Properties.cpp index a0407eb6ebf1..4a5a2bc42738 100644 --- a/Property_map/test/Property_map/test_Properties.cpp +++ b/Property_map/test/Property_map/test_Properties.cpp @@ -1,5 +1,5 @@ -#include +#include using namespace CGAL::Properties; diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 01a6e121e205..455bca346c0c 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h index a446c589eab9..c0f9bffa0c44 100644 --- a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h +++ b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include From 4a379e575ba1e5137d7c1cd608803df3314764f0 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 30 Aug 2023 15:45:46 +0200 Subject: [PATCH 183/520] Delete old Properties system, no longer in use anywhere --- .../include/CGAL/Surface_mesh/Properties.h | 683 ------------------ 1 file changed, 683 deletions(-) delete mode 100644 Surface_mesh/include/CGAL/Surface_mesh/Properties.h diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Properties.h b/Surface_mesh/include/CGAL/Surface_mesh/Properties.h deleted file mode 100644 index 31eea166ee58..000000000000 --- a/Surface_mesh/include/CGAL/Surface_mesh/Properties.h +++ /dev/null @@ -1,683 +0,0 @@ -// Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen -// Copyright (C) 2011 by Graphics & Geometry Group, Bielefeld University -// Copyright (C) 2014 GeometryFactory -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial -// - -#ifndef CGAL_SURFACE_MESH_PROPERTY_H -#define CGAL_SURFACE_MESH_PROPERTY_H - -#include - -#ifndef DOXYGEN_RUNNING - -#include -#include - -#include -#include -#include -#include - -namespace CGAL { - -namespace Properties { - -/// \addtogroup PkgSurface_mesh -/// -/// @{ - -/// @cond CGAL_DOCUMENT_INTERNALS -class Base_property_array -{ -public: - - /// Default constructor - Base_property_array(const std::string& name) : name_(name) {} - - /// Destructor. - virtual ~Base_property_array() {} - - /// Reserve memory for n elements. - virtual void reserve(size_t n) = 0; - - /// Resize storage to hold n elements. - virtual void resize(size_t n) = 0; - - /// Free unused memory. - virtual void shrink_to_fit() = 0; - - /// Extend the number of elements by one. - virtual void push_back() = 0; - - /// Reset element to default value - virtual void reset(size_t idx) = 0; - - virtual bool transfer(const Base_property_array& other) = 0; - virtual bool transfer(const Base_property_array& other, std::size_t from, std::size_t to) = 0; - - /// Let two elements swap their storage place. - virtual void swap(size_t i0, size_t i1) = 0; - - /// Return a deep copy of self. - virtual Base_property_array* clone () const = 0; - - /// Return a empty copy of self. - virtual Base_property_array* empty_clone () const = 0; - - /// Return the type_info of the property - virtual const std::type_info& type() const = 0; - - /// Return the name of the property - const std::string& name() const { return name_; } - - bool is_same (const Base_property_array& other) - { - return (name() == other.name() && type() == other.type()); - } - -protected: - - std::string name_; -}; - - /// @endcond - - -//== CLASS DEFINITION ========================================================= - -/// @cond CGAL_DOCUMENT_INTERNALS - -template -class Property_array : public Base_property_array -{ -public: - - typedef T value_type; - typedef std::vector vector_type; - typedef typename vector_type::reference reference; - typedef typename vector_type::const_reference const_reference; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; - - Property_array(const std::string& name, T t=T()) : Base_property_array(name), value_(t) {} - -public: // virtual interface of Base_property_array - - virtual void reserve(size_t n) - { - data_.reserve(n); - } - - virtual void resize(size_t n) - { - data_.resize(n, value_); - } - - virtual void push_back() - { - data_.push_back(value_); - } - - virtual void reset(size_t idx) - { - data_[idx] = value_; - } - - bool transfer(const Base_property_array& other) - { - const Property_array* pa = dynamic_cast(&other); - if(pa != nullptr){ - std::copy((*pa).data_.begin(), (*pa).data_.end(), data_.end()-(*pa).data_.size()); - return true; - } - return false; - } - - bool transfer(const Base_property_array& other, std::size_t from, std::size_t to) - { - const Property_array* pa = dynamic_cast(&other); - if (pa != nullptr) - { - data_[to] = (*pa)[from]; - return true; - } - - return false; - } - - virtual void shrink_to_fit() - { - vector_type(data_).swap(data_); - } - - virtual void swap(size_t i0, size_t i1) - { - T d(data_[i0]); - data_[i0]=data_[i1]; - data_[i1]=d; - } - - virtual Base_property_array* clone() const - { - Property_array* p = new Property_array(this->name_, this->value_); - p->data_ = data_; - return p; - } - - virtual Base_property_array* empty_clone() const - { - Property_array* p = new Property_array(this->name_, this->value_); - return p; - } - - virtual const std::type_info& type() const { return typeid(T); } - - -public: - - /// Get pointer to array (does not work for T==bool) - const T* data() const - { - return &data_[0]; - } - - /// Access the i'th element. No range check is performed! - reference operator[](std::size_t _idx) - { - CGAL_assertion( _idx < data_.size() ); - return data_[_idx]; - } - - /// Const access to the i'th element. No range check is performed! - const_reference operator[](std::size_t _idx) const - { - CGAL_assertion( _idx < data_.size()); - return data_[_idx]; - } - - iterator begin() { return data_.begin(); } - iterator end() { return data_.end(); } - const_iterator begin() const { return data_.begin(); } - const_iterator end() const { return data_.end(); } - -private: - vector_type data_; - value_type value_; -}; - - - /// @endcond - -//== CLASS DEFINITION ========================================================= - -/// @cond CGAL_DOCUMENT_INTERNALS - -template -class Property_container; -/// @endcond - - - - -//== CLASS DEFINITION ========================================================= -/// @cond CGAL_DOCUMENT_INTERNALS - -template -class Property_container -{ -public: - - // default constructor - Property_container() = default; - - // destructor (deletes all property arrays) - virtual ~Property_container() { clear(); } - - // copy constructor: performs deep copy of property arrays - Property_container(const Property_container& _rhs) { operator=(_rhs); } - - Property_container(Property_container&& c) noexcept - { - c.swap(*this); - } - - // assignment: performs deep copy of property arrays - Property_container& operator=(const Property_container& _rhs) - { - if (this != &_rhs) - { - clear(); - parrays_.resize(_rhs.n_properties()); - size_ = _rhs.size(); - capacity_ = _rhs.capacity(); - for (std::size_t i=0; iclone(); - } - return *this; - } - - Property_container& operator=(Property_container&& c) noexcept - { - Property_container tmp(std::move(c)); - tmp.swap(*this); - return *this; - } - - - void transfer(const Property_container& _rhs) - { - for(std::size_t i=0; iis_same (*(_rhs.parrays_[j]))){ - parrays_[i]->transfer(* _rhs.parrays_[j]); - break; - } - } - } - } - - // Copy properties that don't already exist from another container - void copy_properties (const Property_container& _rhs) - { - for (std::size_t i = 0; i < _rhs.parrays_.size(); ++ i) - { - bool property_already_exists = false; - for (std::size_t j = 0; j < parrays_.size(); ++ j) - if (_rhs.parrays_[i]->is_same (*(parrays_[j]))) - { - property_already_exists = true; - break; - } - - if (property_already_exists) - continue; - - parrays_.push_back (_rhs.parrays_[i]->empty_clone()); - parrays_.back()->reserve(capacity_); - parrays_.back()->resize(size_); - } - } - - // Transfer one element with all properties - // WARNING: properties must be the same in the two containers - bool transfer(const Property_container& _rhs, std::size_t from, std::size_t to) - { - bool out = true; - for(std::size_t i=0; itransfer(* _rhs.parrays_[i], from, to))) - out = false; - return out; - } - - // returns the current size of the property arrays - size_t size() const { return size_; } - - // returns the current capacity of the property arrays - size_t capacity() const { return capacity_; } - - // returns the number of property arrays - size_t n_properties() const { return parrays_.size(); } - - // returns a vector of all property names - std::vector properties() const - { - std::vector names; - for (std::size_t i=0; iname()); - return names; - } - - template - struct Get_pmap_type { - typedef typename Ref_class::template Get_property_map::type type; - }; - - template - std::pair::type, bool> - get(const std::string& name, std::size_t i) const - { - typedef typename Ref_class::template Get_property_map::type Pmap; - if (parrays_[i]->name() == name) - { - if (Property_array* array = dynamic_cast*>(parrays_[i])) - return std::make_pair (Pmap(array), true); - } - return std::make_pair(Pmap(), false); - } - - // add a property with name \c name and default value \c t - template - std::pair::type, bool> - add(const std::string& name, const T t=T()) - { - typedef typename Ref_class::template Get_property_map::type Pmap; - for (std::size_t i=0; i out = get(name, i); - if (out.second) - { - out.second = false; - return out; - } - } - - // otherwise add the property - Property_array* p = new Property_array(name, t); - p->reserve(capacity_); - p->resize(size_); - parrays_.push_back(p); - return std::make_pair(Pmap(p), true); - } - - - // get a property by its name. returns invalid property if it does not exist. - template - std::pair::type, bool> - get(const std::string& name) const - { - typedef typename Ref_class::template Get_property_map::type Pmap; - for (std::size_t i=0; i out = get(name, i); - if (out.second) - return out; - } - return std::make_pair(Pmap(), false); - } - - - // returns a property if it exists, otherwise it creates it first. - template - typename Get_pmap_type::type - get_or_add(const std::string& name, const T t=T()) - { - typename Ref_class::template Get_property_map::type p; - bool b; - boost::tie(p,b)= get(name); - if (!b) p = add(name, t).first; - return p; - } - - - // get the type of property by its name. returns typeid(void) if it does not exist. - const std::type_info& - get_type(const std::string& name) const - { - for (std::size_t i=0; iname() == name) - return parrays_[i]->type(); - return typeid(void); - } - - - // delete a property - template - bool - remove(typename Get_pmap_type::type& h) - { - typename std::vector::iterator it=parrays_.begin(), end=parrays_.end(); - for (; it!=end; ++it) - { - if (*it == h.parray_) - { - delete *it; - parrays_.erase(it); - h.reset(); - return true; - } - } - return false; - } - - - // delete all properties - void clear() - { - for (std::size_t i=0; ireserve(n); - capacity_ = (std::max)(n, capacity_); - } - - // resize all arrays to size n - void resize(size_t n) - { - for (std::size_t i=0; iresize(n); - size_ = n; - } - - // resize the vector of properties to n, deleting all other properties - void resize_property_array(size_t n) - { - if (parrays_.size()<=n) - return; - for (std::size_t i=n; ishrink_to_fit(); - capacity_ = size_; - } - - // add a new element to each vector - void push_back() - { - for (std::size_t i=0; ipush_back(); - ++size_; - capacity_ = ((std::max)(size_, capacity_)); - } - - // reset element to its default property values - void reset(size_t idx) - { - for (std::size_t i=0; ireset(idx); - } - - // swap elements i0 and i1 in all arrays - void swap(size_t i0, size_t i1) const - { - for (std::size_t i=0; iswap(i0, i1); - } - - // swap content with other Property_container - void swap (Property_container& other) - { - this->parrays_.swap (other.parrays_); - std::swap(this->size_, other.size_); - std::swap(this->capacity_, other.capacity_); - } - -private: - std::vector parrays_; - size_t size_ = 0; - size_t capacity_ = 0; -}; - - /// @endcond - -#ifndef DOXYGEN_RUNNING -/// -/// -/// `Property_map` enables to attach properties to the simplices of a -/// surface mesh. -/// -/// @tparam Key The key type of the property map. It must be a model of `Index`. -/// @tparam Value The value type of the property. -/// -/// \cgalModels `LvaluePropertyMap` -/// -template -class Property_map_base -/// @cond CGAL_DOCUMENT_INTERNALS - : public boost::put_get_helper< - typename Property_array::reference, - CRTP_derived_class> -/// @endcond -{ -public: - typedef I key_type; - typedef T value_type; - typedef boost::lvalue_property_map_tag category; - -#ifndef DOXYGEN_RUNNING - - typedef typename Property_array::reference reference; - typedef typename Property_array::const_reference const_reference; - typedef typename Property_array::iterator iterator; - typedef typename Property_array::const_iterator const_iterator; -#else - /// A reference to the value type of the property. - typedef unspecified_type reference; - - /// A const reference to the value type of the property. - typedef unspecified_type const_reference; -#endif - -#ifndef DOXYGEN_RUNNING - template - friend class Property_container; -#endif - -public: -/// @cond CGAL_DOCUMENT_INTERNALS - Property_map_base(Property_array* p=nullptr) : parray_(p) {} - - Property_map_base(Property_map_base&& pm) noexcept - : parray_(std::exchange(pm.parray_, nullptr)) - {} - - Property_map_base(const Property_map_base& pm) - : parray_(pm.parray_) - {} - - Property_map_base& operator=(const Property_map_base& pm) - { - parray_ = pm.parray_; - return *this; - } - - void reset() - { - parray_ = nullptr; - } - /// @endcond - -public: - /// \name Accessing Properties - //@{ -#ifdef DOXYGEN_RUNNING - /// Conversion to a Boolean. It is \c true when the property map - /// can be used, and \c false otherwise. - operator bool () const; -#else - explicit operator bool() const { - return parray_ != nullptr; - } -#endif - - bool operator==(const Property_map_base& pm) const { - return parray_ == pm.parray_; - } - - bool operator!=(const Property_map_base& pm) const { - return parray_ != pm.parray_; - } - - /// Access the property associated with the key \c i. - reference operator[](const I& i) - { - CGAL_assertion(parray_ != nullptr); - return (*parray_)[i]; - } - - /// Access the property associated with the key \c i. - reference operator[](const I& i) const - { - CGAL_assertion(parray_ != nullptr); - return (*parray_)[i]; - } - - iterator begin() { return parray_->begin(); } - iterator end() { return parray_->end(); } - const_iterator begin() const { return parray_->begin(); } - const_iterator end() const { return parray_->end(); } - - bool transfer (const Property_map_base& other) - { - return parray_->transfer(*(other.parray_)); - } - - bool transfer (const Property_map_base& other, std::size_t from, std::size_t to) - { - return parray_->transfer(*(other.parray_), from, to); - } - - /// Allows access to the underlying storage of the property. This - /// is useful when the key associated with the properties is - /// unimportant and only the properties are of interest - /// (e.g. rendering). - /// - /// \returns a pointer to the underlying storage of the property. - const T* data() const - { - CGAL_assertion(parray_ != nullptr); - return parray_->data(); - } - - //@} -#ifndef CGAL_TEST_SURFACE_MESH -private: -#endif - - Property_array& array() - { - CGAL_assertion(parray_ != nullptr); - return *parray_; - } - - const Property_array& array() const - { - CGAL_assertion(parray_ != nullptr); - return *parray_; - } - - Property_array* parray_; -}; - -#endif // DOXYGEN_RUNNING - -///@} - -} // Properties - -} // CGAL - -#endif // DOXYGEN_RUNNING - -//============================================================================= -#endif // CGAL_SURFACE_MESH_PROPERTY_H -//============================================================================= From f2cb4edce86319031cc5ed2ce32ec96f6ec1a10e Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 18:37:30 +0200 Subject: [PATCH 184/520] draft (multi)polygon validation --- .../CGAL/Polygon_repair/Polygon_repair.h | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 9281bad46d97..e14673dcb613 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -83,6 +83,7 @@ class Polygon_repair { using Tag = typename std::conditional::value, CGAL::Exact_predicates_tag, CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; using Triangulation = Triangulation_with_odd_even_constraints_2; + using Validation_triangulation = CGAL::Constrained_triangulation_2; struct Polygon_less { using Polygon_2 = CGAL::Polygon_2; @@ -429,6 +430,110 @@ class Polygon_repair { mp.add_polygon(polygon); } } + + // Validation + bool is_valid(const Polygon_2& polygon) { + Validation_triangulation vt; + + // Intersections between edges + for (auto const& edge: polygon.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices" << std::endl; + return false; + } try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersecting edges" << std::endl; + return false; + } + } + + // Connected interior with no holes + for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list to_check; + std::list to_check_added_by; + label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions > 1) { + std::cout << "Invalid: disconnected interior" << std::endl; + return false; + } if (holes > 0) { + std::cout << "Invalid: hole(s)" << std::endl; + return false; + } + + return true; + } + + bool is_valid(const Polygon_with_holes_2& polygon) { + Validation_triangulation vt; + + // Intersections between edges of outer boundary + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices" << std::endl; + return false; + } try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersecting edges" << std::endl; + return false; + } + } + + // Connected interior with no holes + for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list to_check; + std::list to_check_added_by; + label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions > 1) { + std::cout << "Invalid: disconnected interior" << std::endl; + return false; + } if (holes > 0) { + std::cout << "Invalid: hole(s)" << std::endl; + return false; + } + + // Hole nesting + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + + } + } + + return true; + } + + bool is_valid(const Multipolygon_with_holes_2& multipolygon) { + return true; + } // Erases the triangulation. void clear() { From d5f0f7748d76486af9861d201ecca0afcc18b87b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 18:38:06 +0200 Subject: [PATCH 185/520] missed a part --- .../CGAL/Polygon_repair/Polygon_repair.h | 162 +++++++++++++++++- 1 file changed, 155 insertions(+), 7 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index e14673dcb613..342f557e3f9f 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -83,7 +83,8 @@ class Polygon_repair { using Tag = typename std::conditional::value, CGAL::Exact_predicates_tag, CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; using Triangulation = Triangulation_with_odd_even_constraints_2; - using Validation_triangulation = CGAL::Constrained_triangulation_2; + using Validation_tag = CGAL::No_constraint_intersection_tag; + using Validation_triangulation = CGAL::Constrained_triangulation_2; struct Polygon_less { using Polygon_2 = CGAL::Polygon_2; @@ -467,6 +468,9 @@ class Polygon_repair { } } to_check.pop_front(); to_check_added_by.pop_front(); + } if (regions < 1) { + std::cout << "Invalid: no interior" << std::endl; + return false; } if (regions > 1) { std::cout << "Invalid: disconnected interior" << std::endl; return false; @@ -484,17 +488,17 @@ class Polygon_repair { // Intersections between edges of outer boundary for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices" << std::endl; + std::cout << "Invalid: duplicate vertices (outer boundary)" << std::endl; return false; } try { vt.insert_constraint(edge.source(), edge.target()); } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: intersecting edges" << std::endl; + std::cout << "Invalid: intersecting edges (outer boundary)" << std::endl; return false; } } - // Connected interior with no holes + // Connected interior ignoring holes for (auto const face: t.all_face_handles()) { face->label() = 0; face->processed() = false; @@ -513,25 +517,169 @@ class Polygon_repair { } } to_check.pop_front(); to_check_added_by.pop_front(); + } if (regions < 1) { + std::cout << "Invalid: no interior (outer boundary)" << std::endl; + return false; } if (regions > 1) { - std::cout << "Invalid: disconnected interior" << std::endl; + std::cout << "Invalid: disconnected interior (outer boundary)" << std::endl; return false; } if (holes > 0) { - std::cout << "Invalid: hole(s)" << std::endl; + std::cout << "Invalid: hole(s) formed by outer boundary" << std::endl; return false; } // Hole nesting for (auto const& hole: polygon.holes()) { for (auto const& vertex: hole.vertices()) { - + typename Validation_triangulation::Locate_type lt; + int li; + typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == Validation_triangulation::Locate_type::FACE && f->label() != 1) { + std::cout << "Invalid: hole outside outer boundary" << std::endl; + return false; + } + } for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices (hole)" << std::endl; + return false; + } try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersecting edges (involving hole)" << std::endl; + return false; + } } + } for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } to_check.clear(); + to_check_added_by.clear(); + label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior + regions = 0; + holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions < 1) { + std::cout << "Invalid: no interior" << std::endl; + return false; + } if (regions > 1) { + std::cout << "Invalid: disconnected interior" << std::endl; + return false; + } if (holes != polygon.number_of_holes()) { + std::cout << "Invalid: hole(s) with disconnected interior" << std::endl; + return false; } return true; } bool is_valid(const Multipolygon_with_holes_2& multipolygon) { + Validation_triangulation vt; + + // Intersections between edges of outer boundary + for (auto const& polygon: multipolygon.polygons()) { + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices (outer boundary)" << std::endl; + return false; + } try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersecting edges (outer boundary)" << std::endl; + return false; + } + } + } + + // Connected interior ignoring holes + for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list to_check; + std::list to_check_added_by; + label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions != multipolygon.number_of_polygons()) { + std::cout << "Invalid: polygon(s) with disconnected interior (outer boundary)" << std::endl; + return false; + } if (holes > 0) { + std::cout << "Invalid: hole(s) formed by outer boundary" << std::endl; + return false; + } + + // Hole nesting + for (auto const& polygon: multipolygon.polygons()) { + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + typename Validation_triangulation::Locate_type lt; + int li; + typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == Validation_triangulation::Locate_type::FACE && f->label() < 1) { + std::cout << "Invalid: hole outside outer boundary" << std::endl; + return false; + } + } for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices (hole)" << std::endl; + return false; + } try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersecting edges (involving hole)" << std::endl; + return false; + } + } + } + } for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } to_check.clear(); + to_check_added_by.clear(); + label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior + regions = 0; + holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions != multipolygon.number_of_polygons()) { + std::cout << "Invalid: polygon(s) with disconnected interior (involving holes)" << std::endl; + return false; + } int total_holes = 0; + for (auto const& polygon: multipolygon.number_of_polygons()) { + total_holes += polygon.number_of_holes(); + } if (holes != total_holes) { + std::cout << "Invalid: hole(s) with disconnected interior" << std::endl; + return false; + } + return true; } From fdc5fa5fec4e953275f61d8c86db2c2ede4a0e4a Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 18:41:20 +0200 Subject: [PATCH 186/520] trim whitespace --- .../CGAL/Polygon_repair/Polygon_repair.h | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 342f557e3f9f..a1a80a1aa633 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -431,11 +431,11 @@ class Polygon_repair { mp.add_polygon(polygon); } } - + // Validation bool is_valid(const Polygon_2& polygon) { Validation_triangulation vt; - + // Intersections between edges for (auto const& edge: polygon.edges()) { if (edge.source() == edge.target()) { @@ -448,7 +448,7 @@ class Polygon_repair { return false; } } - + // Connected interior with no holes for (auto const face: t.all_face_handles()) { face->label() = 0; @@ -478,13 +478,13 @@ class Polygon_repair { std::cout << "Invalid: hole(s)" << std::endl; return false; } - + return true; } - + bool is_valid(const Polygon_with_holes_2& polygon) { Validation_triangulation vt; - + // Intersections between edges of outer boundary for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) { @@ -497,7 +497,7 @@ class Polygon_repair { return false; } } - + // Connected interior ignoring holes for (auto const face: t.all_face_handles()) { face->label() = 0; @@ -527,7 +527,7 @@ class Polygon_repair { std::cout << "Invalid: hole(s) formed by outer boundary" << std::endl; return false; } - + // Hole nesting for (auto const& hole: polygon.holes()) { for (auto const& vertex: hole.vertices()) { @@ -578,13 +578,13 @@ class Polygon_repair { std::cout << "Invalid: hole(s) with disconnected interior" << std::endl; return false; } - + return true; } - + bool is_valid(const Multipolygon_with_holes_2& multipolygon) { Validation_triangulation vt; - + // Intersections between edges of outer boundary for (auto const& polygon: multipolygon.polygons()) { for (auto const& edge: polygon.outer_boundary().edges()) { @@ -599,7 +599,7 @@ class Polygon_repair { } } } - + // Connected interior ignoring holes for (auto const face: t.all_face_handles()) { face->label() = 0; @@ -626,7 +626,7 @@ class Polygon_repair { std::cout << "Invalid: hole(s) formed by outer boundary" << std::endl; return false; } - + // Hole nesting for (auto const& polygon: multipolygon.polygons()) { for (auto const& hole: polygon.holes()) { @@ -679,7 +679,7 @@ class Polygon_repair { std::cout << "Invalid: hole(s) with disconnected interior" << std::endl; return false; } - + return true; } From 6626b5205bc8162fac3dc6fabfd4bf4bd4d7f801 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 21:25:39 +0200 Subject: [PATCH 187/520] better approach reusing existing functions + testing more cases --- .../CGAL/Polygon_repair/Polygon_repair.h | 250 +++++++----------- 1 file changed, 96 insertions(+), 154 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index a1a80a1aa633..cad6670b3645 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -434,71 +434,48 @@ class Polygon_repair { // Validation bool is_valid(const Polygon_2& polygon) { - Validation_triangulation vt; - - // Intersections between edges for (auto const& edge: polygon.edges()) { if (edge.source() == edge.target()) { std::cout << "Invalid: duplicate vertices" << std::endl; return false; - } try { - vt.insert_constraint(edge.source(), edge.target()); - } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: intersecting edges" << std::endl; - return false; } - } - - // Connected interior with no holes - for (auto const face: t.all_face_handles()) { - face->label() = 0; - face->processed() = false; - } std::list to_check; - std::list to_check_added_by; - label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior - int regions = 0, holes = 0; - while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet - if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); - ++regions; - } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); - ++holes; - } - } to_check.pop_front(); - to_check_added_by.pop_front(); - } if (regions < 1) { - std::cout << "Invalid: no interior" << std::endl; - return false; - } if (regions > 1) { - std::cout << "Invalid: disconnected interior" << std::endl; - return false; - } if (holes > 0) { - std::cout << "Invalid: hole(s)" << std::endl; + } if (!polygon.is_simple()) { + std::cout << "Invalid: not simple" << std::endl; return false; - } - - return true; + } return true; } bool is_valid(const Polygon_with_holes_2& polygon) { - Validation_triangulation vt; - // Intersections between edges of outer boundary + // Validate outer boundary for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices (outer boundary)" << std::endl; + std::cout << "Invalid: duplicate vertices in outer boundary" << std::endl; return false; - } try { - vt.insert_constraint(edge.source(), edge.target()); - } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: intersecting edges (outer boundary)" << std::endl; + } + } if (!polygon.outer_boundary().is_simple()) { + std::cout << "Invalid: outer boundary not simple" << std::endl; + return false; + } + + // Validate holes + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices in hole" << std::endl; + return false; + } + } if (!hole.is_simple()) { + std::cout << "Invalid: hole not simple" << std::endl; return false; } } - // Connected interior ignoring holes + // Create triangulation of outer boundary + Validation_triangulation vt; + for (auto const& edge: polygon.outer_boundary().edges()) { + vt.insert_constraint(edge.source(), edge.target()); + } for (auto const face: t.all_face_handles()) { face->label() = 0; face->processed() = false; @@ -517,16 +494,7 @@ class Polygon_repair { } } to_check.pop_front(); to_check_added_by.pop_front(); - } if (regions < 1) { - std::cout << "Invalid: no interior (outer boundary)" << std::endl; - return false; - } if (regions > 1) { - std::cout << "Invalid: disconnected interior (outer boundary)" << std::endl; - return false; - } if (holes > 0) { - std::cout << "Invalid: hole(s) formed by outer boundary" << std::endl; - return false; - } + } CGAL_assertion(regions == 1 && holes == 0); // Hole nesting for (auto const& hole: polygon.holes()) { @@ -535,21 +503,22 @@ class Polygon_repair { int li; typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); if (lt == Validation_triangulation::Locate_type::FACE && f->label() != 1) { - std::cout << "Invalid: hole outside outer boundary" << std::endl; + std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; return false; } - } for (auto const& edge: hole.edges()) { - if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices (hole)" << std::endl; - return false; - } try { + } + for (auto const& edge: hole.edges()) { + try { vt.insert_constraint(edge.source(), edge.target()); } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: intersecting edges (involving hole)" << std::endl; + std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; return false; } } - } for (auto const face: t.all_face_handles()) { + } + + // Connected interior + for (auto const face: t.all_face_handles()) { face->label() = 0; face->processed() = false; } to_check.clear(); @@ -568,116 +537,89 @@ class Polygon_repair { } } to_check.pop_front(); to_check_added_by.pop_front(); - } if (regions < 1) { - std::cout << "Invalid: no interior" << std::endl; - return false; - } if (regions > 1) { + } if (regions != 1) { std::cout << "Invalid: disconnected interior" << std::endl; return false; - } if (holes != polygon.number_of_holes()) { - std::cout << "Invalid: hole(s) with disconnected interior" << std::endl; - return false; - } + } CGAL_assertion(holes == polygon.number_of_holes()); return true; } bool is_valid(const Multipolygon_with_holes_2& multipolygon) { - Validation_triangulation vt; - // Intersections between edges of outer boundary + // Validate polygons for (auto const& polygon: multipolygon.polygons()) { - for (auto const& edge: polygon.outer_boundary().edges()) { - if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices (outer boundary)" << std::endl; - return false; - } try { - vt.insert_constraint(edge.source(), edge.target()); - } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: intersecting edges (outer boundary)" << std::endl; - return false; - } - } + if (!is_valid(polygon)) return false; } - // Connected interior ignoring holes - for (auto const face: t.all_face_handles()) { - face->label() = 0; - face->processed() = false; - } std::list to_check; - std::list to_check_added_by; - label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior - int regions = 0, holes = 0; - while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet - if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); - ++regions; - } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); - ++holes; + Validation_triangulation vt; + typename Validation_triangulation::Locate_type lt; + int li; + for (auto const& polygon: multipolygon.polygons()) { + + + if (vt.number_of_triangles() > 0) { + + // Relabel + for (auto const face: t.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list to_check; + std::list to_check_added_by; + label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); } - } to_check.pop_front(); - to_check_added_by.pop_front(); - } if (regions != multipolygon.number_of_polygons()) { - std::cout << "Invalid: polygon(s) with disconnected interior (outer boundary)" << std::endl; - return false; - } if (holes > 0) { - std::cout << "Invalid: hole(s) formed by outer boundary" << std::endl; - return false; - } - // Hole nesting - for (auto const& polygon: multipolygon.polygons()) { - for (auto const& hole: polygon.holes()) { - for (auto const& vertex: hole.vertices()) { - typename Validation_triangulation::Locate_type lt; - int li; + // Test vertices in labelled triangulation + for (auto const& vertex: polygon.outer_boundary().vertices()) { typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == Validation_triangulation::Locate_type::FACE && f->label() < 1) { - std::cout << "Invalid: hole outside outer boundary" << std::endl; + if (lt == Validation_triangulation::Locate_type::FACE && f->label() != -1) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } - } for (auto const& edge: hole.edges()) { - if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices (hole)" << std::endl; - return false; - } try { + } + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == Validation_triangulation::Locate_type::FACE && f->label() != -1) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + } + + } + + // Insert constraints while checking for intersections + for (auto const& edge: polygon.outer_boundary().edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + try { vt.insert_constraint(edge.source(), edge.target()); } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: intersecting edges (involving hole)" << std::endl; + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } } } - } for (auto const face: t.all_face_handles()) { - face->label() = 0; - face->processed() = false; - } to_check.clear(); - to_check_added_by.clear(); - label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior - regions = 0; - holes = 0; - while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet - if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); - ++regions; - } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); - ++holes; - } - } to_check.pop_front(); - to_check_added_by.pop_front(); - } if (regions != multipolygon.number_of_polygons()) { - std::cout << "Invalid: polygon(s) with disconnected interior (involving holes)" << std::endl; - return false; - } int total_holes = 0; - for (auto const& polygon: multipolygon.number_of_polygons()) { - total_holes += polygon.number_of_holes(); - } if (holes != total_holes) { - std::cout << "Invalid: hole(s) with disconnected interior" << std::endl; - return false; } return true; From 448e611e7f61ebaf83c83139574bfc8aa1e61a11 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 30 Aug 2023 21:26:32 +0200 Subject: [PATCH 188/520] images for doc From 77f240b1b638f68bb1c830b6801d54d57598c951 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 31 Aug 2023 12:48:52 +0200 Subject: [PATCH 189/520] Delete Orthtree::Node class, now used nowhere --- Orthtree/include/CGAL/Orthtree.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index f1c7acdd73a4..a606bd93702b 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -137,19 +137,13 @@ class Orthtree { */ typedef std::array Global_coordinates; - /*! - * \brief The Sub-tree / Orthant type. - * todo: this should be removed - */ - class Node; - /*! * \brief A predicate that determines whether a node must be split when refining a tree. */ typedef std::function Split_predicate; /*! - * \brief A model of `ConstRange` whose value type is `Node`. + * \brief A model of `ConstRange` whose value type is `Node_index`. */ #ifdef DOXYGEN_RUNNING typedef unspecified_type Node_range; From 1b4ce1c96598f79b99639c0123da4aa120f78c31 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 31 Aug 2023 15:30:30 +0200 Subject: [PATCH 190/520] put is_valid outside, bugfixes --- .../CGAL/Polygon_repair/Polygon_repair.h | 388 +++++++++--------- 1 file changed, 195 insertions(+), 193 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index cad6670b3645..be6a006fe0c1 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -67,6 +67,201 @@ Multipolygon_with_holes_2 repair_odd_even(const Multip } return pr.multipolygon(); } +template +bool is_valid(const Polygon_2& polygon) { + for (auto const& edge: polygon.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices" << std::endl; + return false; + } + } if (!polygon.is_simple()) { + std::cout << "Invalid: not simple" << std::endl; + return false; + } return true; +} + +template +bool is_valid(const Polygon_with_holes_2& polygon) { + + // Validate outer boundary + for (auto const& edge: polygon.outer_boundary().edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices in outer boundary" << std::endl; + return false; + } + } if (!polygon.outer_boundary().is_simple()) { + std::cout << "Invalid: outer boundary not simple" << std::endl; + return false; + } + + // Validate holes + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + if (edge.source() == edge.target()) { + std::cout << "Invalid: duplicate vertices in hole" << std::endl; + return false; + } + } if (!hole.is_simple()) { + std::cout << "Invalid: hole not simple" << std::endl; + return false; + } + } + + // Create triangulation of outer boundary + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; + for (auto const& edge: polygon.outer_boundary().edges()) { + vt.insert_constraint(edge.source(), edge.target()); + } + for (auto const face: vt.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list::Validation_triangulation::Face_handle> to_check; + std::list to_check_added_by; + label_region(vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } CGAL_assertion(regions == 1 && holes == 0); + + // Hole nesting + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; + int li; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != 1) { + std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; + return false; + } + } + for (auto const& edge: hole.edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; + return false; + } + } + } + + // Connected interior + for (auto const face: vt.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } to_check.clear(); + to_check_added_by.clear(); + label_region(vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + regions = 0; + holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } if (regions != 1) { + std::cout << "Invalid: disconnected interior" << std::endl; + return false; + } CGAL_assertion(holes == polygon.number_of_holes()); + + return true; +} + +template +bool is_valid(const Multipolygon_with_holes_2& multipolygon) { + + // Validate polygons + for (auto const& polygon: multipolygon.polygons()) { + if (!is_valid(polygon)) return false; + } + + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; + int li; + for (auto const& polygon: multipolygon.polygons()) { + + + if (vt.number_of_triangles() > 0) { + + // Relabel + for (auto const face: vt.all_face_handles()) { + face->label() = 0; + face->processed() = false; + } std::list::Validation_triangulation::Face_handle> to_check; + std::list to_check_added_by; + label_region(vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + int regions = 0, holes = 0; + while (!to_check.empty()) { + if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check_added_by.front() < 0) { + label_region(to_check.front(), regions+1, to_check, to_check_added_by); + ++regions; + } else { + label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + ++holes; + } + } to_check.pop_front(); + to_check_added_by.pop_front(); + } + + // Test vertices in labelled triangulation + for (auto const& vertex: polygon.outer_boundary().vertices()) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != -1) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + for (auto const& hole: polygon.holes()) { + for (auto const& vertex: hole.vertices()) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != -1) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + } + + } + + // Insert constraints while checking for intersections + for (auto const& edge: polygon.outer_boundary().edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + for (auto const& hole: polygon.holes()) { + for (auto const& edge: hole.edges()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: (partly) overlapping polygons" << std::endl; + return false; + } + } + } + } + + return true; +} + /*! \ingroup PkgPolygonRepairRef * * The class `Polygon_repair` builds on a constrained @@ -432,199 +627,6 @@ class Polygon_repair { } } - // Validation - bool is_valid(const Polygon_2& polygon) { - for (auto const& edge: polygon.edges()) { - if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices" << std::endl; - return false; - } - } if (!polygon.is_simple()) { - std::cout << "Invalid: not simple" << std::endl; - return false; - } return true; - } - - bool is_valid(const Polygon_with_holes_2& polygon) { - - // Validate outer boundary - for (auto const& edge: polygon.outer_boundary().edges()) { - if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices in outer boundary" << std::endl; - return false; - } - } if (!polygon.outer_boundary().is_simple()) { - std::cout << "Invalid: outer boundary not simple" << std::endl; - return false; - } - - // Validate holes - for (auto const& hole: polygon.holes()) { - for (auto const& edge: hole.edges()) { - if (edge.source() == edge.target()) { - std::cout << "Invalid: duplicate vertices in hole" << std::endl; - return false; - } - } if (!hole.is_simple()) { - std::cout << "Invalid: hole not simple" << std::endl; - return false; - } - } - - // Create triangulation of outer boundary - Validation_triangulation vt; - for (auto const& edge: polygon.outer_boundary().edges()) { - vt.insert_constraint(edge.source(), edge.target()); - } - for (auto const face: t.all_face_handles()) { - face->label() = 0; - face->processed() = false; - } std::list to_check; - std::list to_check_added_by; - label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior - int regions = 0, holes = 0; - while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet - if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); - ++regions; - } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); - ++holes; - } - } to_check.pop_front(); - to_check_added_by.pop_front(); - } CGAL_assertion(regions == 1 && holes == 0); - - // Hole nesting - for (auto const& hole: polygon.holes()) { - for (auto const& vertex: hole.vertices()) { - typename Validation_triangulation::Locate_type lt; - int li; - typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == Validation_triangulation::Locate_type::FACE && f->label() != 1) { - std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; - return false; - } - } - for (auto const& edge: hole.edges()) { - try { - vt.insert_constraint(edge.source(), edge.target()); - } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; - return false; - } - } - } - - // Connected interior - for (auto const face: t.all_face_handles()) { - face->label() = 0; - face->processed() = false; - } to_check.clear(); - to_check_added_by.clear(); - label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior - regions = 0; - holes = 0; - while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet - if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); - ++regions; - } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); - ++holes; - } - } to_check.pop_front(); - to_check_added_by.pop_front(); - } if (regions != 1) { - std::cout << "Invalid: disconnected interior" << std::endl; - return false; - } CGAL_assertion(holes == polygon.number_of_holes()); - - return true; - } - - bool is_valid(const Multipolygon_with_holes_2& multipolygon) { - - // Validate polygons - for (auto const& polygon: multipolygon.polygons()) { - if (!is_valid(polygon)) return false; - } - - Validation_triangulation vt; - typename Validation_triangulation::Locate_type lt; - int li; - for (auto const& polygon: multipolygon.polygons()) { - - - if (vt.number_of_triangles() > 0) { - - // Relabel - for (auto const face: t.all_face_handles()) { - face->label() = 0; - face->processed() = false; - } std::list to_check; - std::list to_check_added_by; - label_region(t.infinite_face(), -1, to_check, to_check_added_by); // exterior - int regions = 0, holes = 0; - while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet - if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); - ++regions; - } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); - ++holes; - } - } to_check.pop_front(); - to_check_added_by.pop_front(); - } - - // Test vertices in labelled triangulation - for (auto const& vertex: polygon.outer_boundary().vertices()) { - typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == Validation_triangulation::Locate_type::FACE && f->label() != -1) { - std::cout << "Invalid: (partly) overlapping polygons" << std::endl; - return false; - } - } - for (auto const& hole: polygon.holes()) { - for (auto const& vertex: hole.vertices()) { - typename Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == Validation_triangulation::Locate_type::FACE && f->label() != -1) { - std::cout << "Invalid: (partly) overlapping polygons" << std::endl; - return false; - } - } - } - - } - - // Insert constraints while checking for intersections - for (auto const& edge: polygon.outer_boundary().edges()) { - try { - vt.insert_constraint(edge.source(), edge.target()); - } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: (partly) overlapping polygons" << std::endl; - return false; - } - } - for (auto const& hole: polygon.holes()) { - for (auto const& edge: hole.edges()) { - try { - vt.insert_constraint(edge.source(), edge.target()); - } catch (typename Validation_triangulation::Intersection_of_constraints_exception ice) { - std::cout << "Invalid: (partly) overlapping polygons" << std::endl; - return false; - } - } - } - } - - return true; - } - // Erases the triangulation. void clear() { t.clear(); From bc16dda92fe75beacea9497eb9a4d01f292e12d4 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 31 Aug 2023 15:58:03 +0200 Subject: [PATCH 191/520] label region static, bugfixes --- .../CGAL/Polygon_repair/Polygon_repair.h | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index be6a006fe0c1..f618f618a81b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -117,15 +117,15 @@ bool is_valid(const Polygon_with_holes_2& polygon) { face->processed() = false; } std::list::Validation_triangulation::Face_handle> to_check; std::list to_check_added_by; - label_region(vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior int regions = 0, holes = 0; while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labelled yet if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); ++holes; } } to_check.pop_front(); @@ -159,16 +159,16 @@ bool is_valid(const Polygon_with_holes_2& polygon) { face->processed() = false; } to_check.clear(); to_check_added_by.clear(); - label_region(vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior regions = 0; holes = 0; while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labelled yet if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); ++holes; } } to_check.pop_front(); @@ -195,7 +195,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo for (auto const& polygon: multipolygon.polygons()) { - if (vt.number_of_triangles() > 0) { + if (vt.number_of_faces() > 0) { // Relabel for (auto const face: vt.all_face_handles()) { @@ -203,15 +203,15 @@ bool is_valid(const Multipolygon_with_holes_2& multipo face->processed() = false; } std::list::Validation_triangulation::Face_handle> to_check; std::list to_check_added_by; - label_region(vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior int regions = 0, holes = 0; while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labelled yet if (to_check_added_by.front() < 0) { - label_region(to_check.front(), regions+1, to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; } else { - label_region(to_check.front(), -(holes+2), to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); ++holes; } } to_check.pop_front(); @@ -464,9 +464,10 @@ class Polygon_repair { // Label a region of adjacent triangles without passing through constraints // adjacent triangles that involve passing through constraints are added to to_check - void label_region(typename Triangulation::Face_handle face, int label, - std::list& to_check, - std::list& to_check_added_by) { + template + static void label_region(T tt, typename T::Face_handle face, int label, + std::list& to_check, + std::list& to_check_added_by) { // std::cout << "Labelling region with " << label << std::endl; std::list to_check_in_region; face->label() = label; @@ -475,7 +476,7 @@ class Polygon_repair { while (!to_check_in_region.empty()) { for (int neighbour = 0; neighbour < 3; ++neighbour) { - if (!t.is_constrained(typename Triangulation::Edge(to_check_in_region.front(), neighbour))) { + if (!tt.is_constrained(typename Triangulation::Edge(to_check_in_region.front(), neighbour))) { if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { // unlabelled to_check_in_region.front()->neighbor(neighbour)->label() = label; to_check_in_region.push_back(to_check_in_region.front()->neighbor(neighbour)); @@ -530,17 +531,17 @@ class Polygon_repair { // putting interior triangles adjacent to it in to_check std::list to_check; std::list to_check_added_by; - label_region(t.infinite_face(), -1, to_check, to_check_added_by); + label_region(t, t.infinite_face(), -1, to_check, to_check_added_by); // Label region of front element to_check list while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labelled yet if (to_check_added_by.front() < 0) { - label_region(to_check.front(), number_of_polygons+1, to_check, to_check_added_by); + label_region(t, to_check.front(), number_of_polygons+1, to_check, to_check_added_by); ++number_of_polygons; } else { - label_region(to_check.front(), -(number_of_holes+2), to_check, to_check_added_by); + label_region(t, to_check.front(), -(number_of_holes+2), to_check, to_check_added_by); ++number_of_holes; } } to_check.pop_front(); From e331136a5bcc9926f4b99d395a08960697358cbf Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 31 Aug 2023 16:05:29 +0200 Subject: [PATCH 192/520] polygons can be in any hole, not just exterior --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index f618f618a81b..6436db920391 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -221,7 +221,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo // Test vertices in labelled triangulation for (auto const& vertex: polygon.outer_boundary().vertices()) { typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != -1) { + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } @@ -229,7 +229,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo for (auto const& hole: polygon.holes()) { for (auto const& vertex: hole.vertices()) { typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != -1) { + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } From 2ab6ad84f35790e17fd8a6df3361ef7d6248ec7d Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 31 Aug 2023 16:23:37 +0200 Subject: [PATCH 193/520] some more checks --- .../CGAL/Polygon_repair/Polygon_repair.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 6436db920391..42bfa04fae2e 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -69,7 +69,10 @@ Multipolygon_with_holes_2 repair_odd_even(const Multip template bool is_valid(const Polygon_2& polygon) { - for (auto const& edge: polygon.edges()) { + if (polygon.vertices().size() < 3) { + std::cout << "Invalid: less than 3 vertices" << std::endl; + return false; + } for (auto const& edge: polygon.edges()) { if (edge.source() == edge.target()) { std::cout << "Invalid: duplicate vertices" << std::endl; return false; @@ -110,9 +113,16 @@ bool is_valid(const Polygon_with_holes_2& polygon) { // Create triangulation of outer boundary typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; for (auto const& edge: polygon.outer_boundary().edges()) { - vt.insert_constraint(edge.source(), edge.target()); - } - for (auto const face: vt.all_face_handles()) { + try { + vt.insert_constraint(edge.source(), edge.target()); + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + std::cout << "Invalid: intersection in outer boundary" << std::endl; + return false; + } + } if (vt.number_of_faces() == 0) { + std::cout << "Invalid: no outer boundary" << std::endl; + return false; + } for (auto const face: vt.all_face_handles()) { face->label() = 0; face->processed() = false; } std::list::Validation_triangulation::Face_handle> to_check; From c402ba7de4a5d9c83786f00502ea0e50408f9112 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 31 Aug 2023 17:56:18 +0200 Subject: [PATCH 194/520] wkt validation --- .../test/Polygon_repair/validate_wkt.cpp | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Polygon_repair/test/Polygon_repair/validate_wkt.cpp diff --git a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp new file mode 100644 index 000000000000..c4781315c624 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main(int argc, char* argv[]) { + +// std::string folder = "/Users/ken/Downloads/big polygons/"; + std::string folder = "/Users/ken/Versioned/cgal-public-dev/Polygon_repair/test/Polygon_repair/data/in"; + + for (const auto& file: std::filesystem::directory_iterator(folder)) { + if (file.path().filename().extension() != ".wkt") continue; + std::cout << "Testing " << file.path().filename() << "... "; + + // Read test file + std::string in; + std::getline(std::ifstream(file.path()), in); + + // Load test file + std::istringstream iss(in); + bool valid = true; + if (in.find("POLYGON") == 0) { + Polygon_with_holes_2 p; + if (in != "POLYGON()") { // maybe should be checked in WKT reader + CGAL::IO::read_polygon_WKT(iss, p); + valid = CGAL::Polygon_repair::is_valid(p); + } + } else if (in.find("MULTIPOLYGON") == 0) { + Multipolygon_with_holes_2 mp; + CGAL::IO::read_multi_polygon_WKT(iss, mp); + valid = CGAL::Polygon_repair::is_valid(mp); + } if (valid) std::cout << "Valid" << std::endl; + } + + return 0; +} From aae6c1ba9589a2e12ecd46657fd64f93bb90bb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 1 Sep 2023 13:07:00 +0200 Subject: [PATCH 195/520] add missing license/copyright and fix protection macro --- Property_map/include/CGAL/Property_container.h | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index e75e2c4dda43..c3659d7db16c 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -1,5 +1,16 @@ -#ifndef PROPERTIES_H -#define PROPERTIES_H +// Copyright (c) 2023 INRIA +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Jackson Campolattaro + +#ifndef CGAL_PROPERTY_CONTAINTER_H +#define CGAL_PROPERTY_CONTAINTER_H #include @@ -589,4 +600,4 @@ class Property_container { } -#endif //ORTHTREE_TESTS_PROPERTIES_H +#endif //CGAL_PROPERTY_CONTAINTER_H From 6de261e5f4d5d2da79b4703edfcaebcad7589cbb Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 14:48:08 +0200 Subject: [PATCH 196/520] Rename test_Properties to resolve naming collision --- Property_map/test/Property_map/CMakeLists.txt | 2 +- .../{test_Properties.cpp => test_Property_container.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename Property_map/test/Property_map/{test_Properties.cpp => test_Property_container.cpp} (100%) diff --git a/Property_map/test/Property_map/CMakeLists.txt b/Property_map/test/Property_map/CMakeLists.txt index 842e795b6494..9f920ea96110 100644 --- a/Property_map/test/Property_map/CMakeLists.txt +++ b/Property_map/test/Property_map/CMakeLists.txt @@ -8,7 +8,7 @@ create_single_source_cgal_program("test_property_map.cpp") create_single_source_cgal_program("dynamic_property_map.cpp") create_single_source_cgal_program("dynamic_properties_test.cpp") create_single_source_cgal_program("kernel_converter_properties_test.cpp") -create_single_source_cgal_program("test_Properties.cpp") +create_single_source_cgal_program("test_Property_container.cpp") find_package(OpenMesh QUIET) if(OpenMesh_FOUND) diff --git a/Property_map/test/Property_map/test_Properties.cpp b/Property_map/test/Property_map/test_Property_container.cpp similarity index 100% rename from Property_map/test/Property_map/test_Properties.cpp rename to Property_map/test/Property_map/test_Property_container.cpp From 40a08f14164208f7deb3fe8c6c16b4f4e8d7a409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 1 Sep 2023 14:52:26 +0200 Subject: [PATCH 197/520] add missing license include --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index cb851b1fcb37..948e5525e98a 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -13,6 +13,8 @@ #ifndef CGAL_ORTHREE_TRAITS_FACE_GRAPH_H #define CGAL_ORTHREE_TRAITS_FACE_GRAPH_H +#include + #include #include From 59fafe8e3a87ba1d9c4c8050d2ce67afcd958e29 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 16:16:30 +0200 Subject: [PATCH 198/520] Fix issues with superfluous Node typedefs --- Orthtree/examples/Orthtree/octree_traversal_custom.cpp | 1 - Orthtree/test/Orthtree/test_node_adjacent.cpp | 1 - Orthtree/test/Orthtree/test_octree_refine.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index dd6de668c8b0..5e784559e7c3 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -14,7 +14,6 @@ typedef CGAL::Point_set_3 Point_set; typedef Point_set::Point_map Point_map; typedef CGAL::Octree Octree; -typedef Octree::Node Node; template struct First_branch_traversal { diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 84a28774fd11..48bfb8d98f28 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -11,7 +11,6 @@ typedef Kernel::Point_3 Point; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; -typedef Octree::Node Node; typedef Octree::Traits Traits; int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index afea2e7734ba..487b9ce11eda 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -12,7 +12,6 @@ typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_3 Point; typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; -typedef Octree::Node Node; class Split_nth_child_of_root { std::size_t m_n; From e9ca3cc5612770e376d08bdb69f01e5e62257a2f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 16:43:26 +0200 Subject: [PATCH 199/520] Update face graph traits to use functors --- .../include/CGAL/Orthtree_traits_face_graph.h | 112 +++++++++--------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 948e5525e98a..bbdd2ac7c151 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -15,6 +15,8 @@ #include +#include + #include #include @@ -23,25 +25,28 @@ namespace CGAL template struct Orthtree_traits_face_graph -{ + : public Orthtree_traits_3_base::value_type>::type> { + Orthtree_traits_face_graph(const PolygonMesh& pm, VPM vpm) - : m_pm(pm) - , m_vpm(vpm) - {} + : m_pm(pm), m_vpm(vpm) {} + using Self = Orthtree_traits_face_graph; + using Tree = Orthtree; + + using Point_d = typename Self::Point_d; + using Dimension = typename Self::Dimension; + using Bbox_d = typename Self::Bbox_d; + using FT = typename Self::FT; + using Sphere_d = typename Self::Sphere_d; // SL: why? + using Array = typename Self::Array; // SL: why? + using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; - using Point_d = typename boost::property_traits::value_type; - using Geom_traits = typename Kernel_traits::type; - using Dimension = Dimension_tag<3>; - using Bbox_d = Bbox_3; - using FT = typename Geom_traits::FT; - using Sphere_d = typename Geom_traits::Sphere_3; // SL: why? - using Array = std::array; // SL: why? - using Cartesian_const_iterator_d = typename Geom_traits::Cartesian_const_iterator_3; // SL: these could be considered as built-in data and if the typedefs are not present, the tree have none using Node_data_element = typename boost::graph_traits::face_descriptor; using Node_data = std::vector; + using Geom_traits = typename Kernel_traits::type; + struct Construct_bbox_d { Bbox_d operator()(const Array& min, const Array& max) const { @@ -59,64 +64,57 @@ struct Orthtree_traits_face_graph Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - // SL: why in each traits? because it's dimension dependant? - enum Adjacency { - LEFT, - RIGHT, - DOWN, - UP, - BACK, - FRONT - }; + auto root_node_bbox_object() const { + return [&]() -> std::pair { - std::pair root_node_bbox() const - { - Array min={0.0,0}, max={0.0,0}; - if (faces(m_pm).begin()!=faces(m_pm).end()) - { - const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); - min={p.x(), p.y(), p.z()}; - max=min; - for (auto v : vertices(m_pm)) + Array min={0.0,0}, max={0.0,0}; + if (faces(m_pm).begin()!=faces(m_pm).end()) { - const Point_d& p_v = get(m_vpm, v); - for (int i=0; i<3; ++i) + const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); + min={p.x(), p.y(), p.z()}; + max=min; + for (auto v : vertices(m_pm)) { - if (p_v[i]max[i]) max[i]=p_v[i]; + const Point_d& p_v = get(m_vpm, v); + for (int i=0; i<3; ++i) + { + if (p_v[i]max[i]) max[i]=p_v[i]; + } } } - } - return {min, max}; + return {min, max}; + }; } // SL: not clear to me what it should do from the doc - Node_data root_node_contents() - { - return {faces(m_pm).begin(), faces(m_pm).end()}; + auto root_node_contents_object() { + return [&]() -> Node_data { + return {faces(m_pm).begin(), faces(m_pm).end()}; + }; } - template - void distribute_node_contents(NodeIndex n, Tree &tree, const Point_d ¢er) - { - Node_data& ndata = tree.data(n); - for (int i=0; i<8; ++i) - { - NodeIndex child = tree.child(n, i); - Node_data& child_data = tree.data(child); - Bbox_d bbox = tree.bbox(child); - for (Node_data_element f : ndata) + auto distribute_node_contents_object(){ + return [&](typename Tree::Node_index n, Tree &tree, const Point_d ¢er) -> void { + Node_data& ndata = tree.data(n); + for (int i=0; i<8; ++i) { - typename boost::graph_traits::halfedge_descriptor - h = halfedge(f, m_pm); - typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), - get(m_vpm, target(h, m_pm)), - get(m_vpm, target(next(h, m_pm), m_pm))); - if(do_intersect(t, bbox)) - child_data.push_back(f); + typename Tree::Node_index child = tree.child(n, i); + Node_data& child_data = tree.data(child); + Bbox_d bbox = tree.bbox(child); + for (Node_data_element f : ndata) + { + typename boost::graph_traits::halfedge_descriptor + h = halfedge(f, m_pm); + typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), + get(m_vpm, target(h, m_pm)), + get(m_vpm, target(next(h, m_pm), m_pm))); + if(do_intersect(t, bbox)) + child_data.push_back(f); + } } - } + }; } //SL: I find convenient to put it here From b7a85710777240b618dc3021018ab4b650e94e5a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 16:50:31 +0200 Subject: [PATCH 200/520] Update empty quadtree to use functors --- .../Orthtree/quadtree_build_manually.cpp | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 0dbb9feef4c5..3f9e6c690d39 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -10,7 +10,8 @@ using Kernel = CGAL::Simple_cartesian; namespace CGAL { -struct empty_type{}; +struct empty_type { +}; template struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { @@ -23,29 +24,30 @@ struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { Orthtree_traits_empty_2(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; - decltype(auto) construct_point_d_from_array_object() const { + auto construct_point_d_from_array_object() const { return [](const typename Self::Array& array) -> typename Self::Point_d { return {array[0], array[1]}; }; } using Construct_point_d_from_array = std::invoke_result_t; - decltype(auto) construct_bbox_d_object() const { + auto construct_bbox_d_object() const { return [](const typename Self::Array& min, const typename Self::Array& max) -> typename Self::Bbox_d { return {min[0], min[1], max[0], max[1]}; }; } using Construct_bbox_d = std::invoke_result_t; - std::pair root_node_bbox() const { - return {{m_bbox.xmax(), m_bbox.ymax()}, - {m_bbox.xmax(), m_bbox.ymax()}}; + auto root_node_bbox_object() const { + return [&]() -> std::pair { + return {{m_bbox.xmax(), m_bbox.ymax()}, + {m_bbox.xmax(), m_bbox.ymax()}}; + }; } - Node_data root_node_contents() const { return {}; } + auto root_node_contents_object() const { return [&]() -> Node_data { return {}; }; } - template // todo: this shouldn't be necessary, but I think there's a dependency loop somehow - void distribute_node_contents(Node_index n, Tree& tree, const typename Self::Point_d& center) {} - - empty_type get_element(const Node_data_element& index) const { return {}; } + auto distribute_node_contents_object() { + return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) -> void {}; + } private: From 40b2c0dff0a5289b6c9be54682320f4ba7995602 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 17:16:54 +0200 Subject: [PATCH 201/520] Convert nearest neighbor methods to free functions --- .../CollectionPartitioningOrthtreeTraits.h | 50 +++++ .../doc/Orthtree/Concepts/OrthtreeTraits.h | 25 +-- .../Orthtree/octree_find_nearest_neighbor.cpp | 4 +- Orthtree/include/CGAL/Orthtree.h | 181 +--------------- .../include/CGAL/Orthtree/Nearest_neighbors.h | 200 ++++++++++++++++++ .../include/CGAL/Orthtree_traits_face_graph.h | 50 ++--- .../include/CGAL/Orthtree_traits_point_2.h | 2 +- .../include/CGAL/Orthtree_traits_point_3.h | 2 +- .../include/CGAL/Orthtree_traits_point_d.h | 2 +- .../Orthtree/test_octree_nearest_neighbor.cpp | 5 +- 10 files changed, 293 insertions(+), 228 deletions(-) create mode 100644 Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h create mode 100644 Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h new file mode 100644 index 000000000000..e601256d1ad6 --- /dev/null +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -0,0 +1,50 @@ +/*! + \ingroup PkgOrthtreeConcepts + \cgalConcept + + In addition to the requirements described in the OrthtreeTraits concept, + the concept `CollectionPartitioningOrthtreeTraits` defines the requirements for the + traits class of a `CGAL::Orthtree` which supports nearest-neighbor searching. + + Nearest neighbor searches expect a tree with nodes which contain list types. + The leaf nodes of the tree represent an exclusive partition of the elements contained in the tree. + This means that no element should be contained by more than one node. + + \cgalRefines{OrthtreeTraits} + + todo: update list of models + \cgalHasModel `CGAL::Orthtree_traits_2` + \cgalHasModel `CGAL::Orthtree_traits_3` + \cgalHasModel `CGAL::Orthtree_traits_d` +*/ +class CollectionPartitioningOrthtreeTraits { +public: + + + /// \name Types + /// @{ + + /*! + * \brief An element of the `Node_data` list-like type. + * + * Must be constructible from the type produced by dereferencing a `Node_data` iterator. + * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. + */ + typedef unspecified_type Node_data_element; + + /*! + * \brief Functor with an operator to produce a geometric object from a `Node_data_element`. + * + * The return type of the functor must be a valid argument to `CGAL::squared_distance`. + */ + typedef unspecified_type Get_geometric_object_for_element; + + /// @} + + /// \name Operations + /// @{ + + Get_geometric_object_for_element get_geometric_object_for_element_object() const; + + /// @} +}; \ No newline at end of file diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 2bf07fd4e57e..bc76e6a69458 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -5,6 +5,7 @@ The concept `OrthtreeTraits` defines the requirements for the template parameter of the `CGAL::Orthtree` class. + todo: update list of models \cgalHasModel `CGAL::Orthtree_traits_2` \cgalHasModel `CGAL::Orthtree_traits_3` \cgalHasModel `CGAL::Orthtree_traits_d` @@ -31,32 +32,10 @@ class OrthtreeTraits /*! - * \brief List-like or iterable type contained by each node - * - * Should provide begin() and end() iterators which span all the items contained by a node. - * Must also be default-constructible, because node data is allocated ahead of time. - * Many split predicates also expect a `Node_data::size()` method. - * For example, this could be a `boost::range` of point indices, or an `std::vector` containing primitives. - * - * todo: For an empty tree, this should contain something like `std::array`. - * This way, nearest_neighbors still compiles, and simply returns nothing because all nodes are empty. - * Eventually, nearest_neighbors will be removed and/or moved, and then this won't have to behave like a list. - * Once that's done, `boost::none_t` will work for an empty tree. + * \brief The data type contained by each node. */ typedef unspecified_type Node_data; - /*! - * \brief An element of the `Node_data` list-like type. - * - * Must be constructible from the type produced by dereferencing a `Node_data` iterator. - * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. - * - * todo: This is only used as part of the return type for `nearest_neighbors()`. - * Because `nearest_neighbors()` may be ill defined for empty node types, - * this can be omitted in the final version of Orthtree_traits. - */ - typedef unspecified_type Node_data_element; - typedef unspecified_type Adjacency; ///< Specify the adjacency directions /*! diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index 5fafb06a8140..1200d88c134f 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -46,7 +47,8 @@ int main(int argc, char** argv) { {-0.460261, -0.253533, 0.320513} }; for (const Point& p: points_to_find) - octree.nearest_neighbors( + CGAL::Orthtrees::nearest_neighbors( + octree, p, 1, // k=1 to find the single closest point boost::make_function_output_iterator([&](const Point_set::Index& nearest) { std::cout << "the nearest point to (" << p << ") is (" << points.point(nearest) << ")" << std::endl; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index a606bd93702b..d65aadcbfb66 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -83,12 +83,10 @@ class Orthtree { typedef typename Traits::Adjacency Adjacency; ///< Adjacency type. typedef typename Traits::Node_data Node_data; - typedef typename Traits::Node_data_element Node_data_element; + // todo: Node_data_element will only exist for certain Traits types, so I don't know if it can be re-exported /// \cond SKIP_IN_MANUAL typedef typename Traits::Array Array; ///< Array type. - typedef typename Traits::Construct_point_d_from_array Construct_point_d_from_array; - typedef typename Traits::Construct_bbox_d Construct_bbox_d; /// \endcond /// @} @@ -234,7 +232,7 @@ class Orthtree { bbox_max[i] = bbox_centroid[i] + max_length; } - Construct_point_d_from_array construct_point_d_from_array + auto construct_point_d_from_array = m_traits.construct_point_d_from_array_object(); // save orthtree attributes @@ -418,6 +416,13 @@ class Orthtree { /// \name Accessors /// @{ + /*! + * \brief Provides direct read-only access to the tree Traits. + * + * @return a const reference to the Traits instantiation. + */ + const Traits& traits() const { return m_traits; } + /*! \brief provides read-only access to the root node, and by extension the rest of the tree. @@ -495,7 +500,7 @@ class Orthtree { } // Create the bbox - Construct_bbox_d construct_bbox + auto construct_bbox = m_traits.construct_bbox_d_object(); return construct_bbox(min_corner, max_corner); } @@ -572,41 +577,6 @@ class Orthtree { return node_for_point; } - /*! - \brief finds the `k` nearest neighbors of `query`. - - Nearest neighbors are outputted in order of increasing distance to - `query`. - - \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. - \param query query point. - \param k number of neighbors. - \param output output iterator. - */ - template - OutputIterator nearest_neighbors(const Point& query, - std::size_t k, - OutputIterator output) const { - Sphere query_sphere(query, (std::numeric_limits::max)()); - return nearest_k_neighbors_in_radius(query_sphere, k, output); - } - - /*! - \brief finds the points in `sphere`. - - Nearest neighbors are outputted in order of increasing distance to - the center of `sphere`. - - \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. - \param query query sphere. - \param output output iterator. - */ - template - OutputIterator nearest_neighbors(const Sphere& query, OutputIterator output) const { - Sphere query_sphere = query; - return nearest_k_neighbors_in_radius(query_sphere, (std::numeric_limits::max)(), output); - } - /*! \brief finds the leaf nodes that intersect with any primitive. @@ -852,7 +822,7 @@ class Orthtree { } // Convert that location into a point - Construct_point_d_from_array construct_point_d_from_array + auto construct_point_d_from_array = m_traits.construct_point_d_from_array_object(); return construct_point_d_from_array(bary); } @@ -996,100 +966,6 @@ class Orthtree { return CGAL::do_intersect(node_cube, sphere); } - // TODO: There has to be a better way than using structs like these! - struct Node_element_with_distance { - Node_data_element point; - FT distance; - }; - - struct Node_index_with_distance { - Node_index index; - FT distance; - - Node_index_with_distance(const Node_index& index, const FT& distance) : - index(index), distance(distance) {} - - }; - - void nearest_k_neighbors_recursive(Sphere& search_bounds, Node_index node, - std::vector& results, FT epsilon = 0) const { - - // Check whether the node has children - if (is_leaf(node)) { - - // Base case: the node has no children - - // Loop through each of the points contained by the node - // Note: there might be none, and that should be fine! - for (auto p: data(node)) { - - // Pair that point with its distance from the search point - Node_element_with_distance current_point_with_distance = - {p, squared_distance(m_traits.get_element_object()(p), search_bounds.center())}; - - // Check if the new point is within the bounds - if (current_point_with_distance.distance < search_bounds.squared_radius()) { - - // Check if the results list is full - if (results.size() == results.capacity()) { - - // Delete a point if we need to make room - results.pop_back(); - } - - // Add the new point - results.push_back(current_point_with_distance); - - // Sort the list - std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { - return left.distance < right.distance; - }); - - // Check if the results list is full - if (results.size() == results.capacity()) { - - // Set the search radius - search_bounds = Sphere(search_bounds.center(), results.back().distance + epsilon); - } - } - } - } else { - - // Recursive case: the node has children - - // Create a list to map children to their distances - std::vector children_with_distances; - children_with_distances.reserve(Degree::value); - - // Fill the list with child nodes - for (int i = 0; i < Degree::value; ++i) { - auto child_node = child(node, i); - - // Add a child to the list, with its distance - children_with_distances.emplace_back( - child_node, - CGAL::squared_distance(search_bounds.center(), barycenter(child_node)) - ); - } - - // Sort the children by their distance from the search point - std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto& left, auto& right) { - return left.distance < right.distance; - }); - - // Loop over the children - for (auto child_with_distance: children_with_distances) { - - // Check whether the bounding box of the child intersects with the search bounds - if (do_intersect(child_with_distance.index, search_bounds)) { - - // Recursively invoke this function - nearest_k_neighbors_recursive(search_bounds, child_with_distance.index, results); - } - } - } - } - template Node_output_iterator intersected_nodes_recursive(const Query& query, Node_index node, Node_output_iterator output) const { @@ -1112,41 +988,6 @@ class Orthtree { return output; } - /*! - \brief finds the `k` points within a specific radius that are - nearest to the center of `query_sphere`. - - This function guarantees that there are no closer points than the ones returned, - but it does not guarantee that it will return at least `k` points. - For a query where the search radius encloses `k` or fewer points, all enclosed points will be returned. - If the search radius is too small, no points may be returned. - This function is useful when the user already knows how sparse the points are, - or if they do not care about points that are too far away. - Setting a small radius may have performance benefits. - - \tparam OutputIterator must be a model of `OutputIterator` that accepts points - \param query_sphere the region to search within - \param k the number of points to find - \param output the output iterator to add the found points to (in order of increasing distance) - */ - template - OutputIterator nearest_k_neighbors_in_radius(Sphere& query_sphere, std::size_t k, OutputIterator output) const { - - // Create an empty list of points - std::vector points_list; - if (k != (std::numeric_limits::max)()) - points_list.reserve(k); - - // Invoking the recursive function adds those points to the vector (passed by reference) - nearest_k_neighbors_recursive(query_sphere, root(), points_list); - - // Add all the points found to the output - for (auto& item: points_list) - *output++ = item.point; - - return output; - } - public: /// \cond SKIP_IN_MANUAL diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h new file mode 100644 index 000000000000..b8d67771cfbc --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -0,0 +1,200 @@ +// todo: license + +#ifndef ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H +#define ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H + +#include + +#include + +namespace CGAL { + +namespace internal { + +template +void nearest_k_neighbors_recursive(const Tree& orthtree, + typename Tree::Sphere& search_bounds, + typename Tree::Node_index node, + std::vector& results, + typename Tree::FT epsilon = 0) { + + // Check whether the node has children + if (orthtree.is_leaf(node)) { + + // Base case: the node has no children + + // Loop through each of the points contained by the node + // Note: there might be none, and that should be fine! + for (auto& p: orthtree.data(node)) { + + // Pair that point with its distance from the search point + Result current_point_with_distance = + {p, squared_distance(orthtree.traits().get_geometric_object_for_element_object()(p), search_bounds.center())}; + + // Check if the new point is within the bounds + if (current_point_with_distance.distance < search_bounds.squared_radius()) { + + // Check if the results list is full + if (results.size() == results.capacity()) { + + // Delete a point if we need to make room + results.pop_back(); + } + + // Add the new point + results.push_back(current_point_with_distance); + + // Sort the list + std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { + return left.distance < right.distance; + }); + + // Check if the results list is full + if (results.size() == results.capacity()) { + + // Set the search radius + search_bounds = typename Tree::Sphere(search_bounds.center(), results.back().distance + epsilon); + } + } + } + } else { + + struct Node_index_with_distance { + typename Tree::Node_index index; + typename Tree::FT distance; + + Node_index_with_distance(const typename Tree::Node_index& index, const typename Tree::FT& distance) : + index(index), distance(distance) {} + }; + + // Recursive case: the node has children + + // Create a list to map children to their distances + std::vector children_with_distances; + children_with_distances.reserve(Tree::Degree::value); + + // Fill the list with child nodes + for (int i = 0; i < Tree::Degree::value; ++i) { + auto child_node = orthtree.child(node, i); + + // Add a child to the list, with its distance + children_with_distances.emplace_back( + child_node, + CGAL::squared_distance(search_bounds.center(), orthtree.barycenter(child_node)) + ); + } + + // Sort the children by their distance from the search point + std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto& left, auto& right) { + return left.distance < right.distance; + }); + + // Loop over the children + for (auto child_with_distance: children_with_distances) { + + // Check whether the bounding box of the child intersects with the search bounds + if (CGAL::do_intersect(orthtree.bbox(child_with_distance.index), search_bounds)) { + + // Recursively invoke this function + CGAL::internal::nearest_k_neighbors_recursive(orthtree, search_bounds, child_with_distance.index, results); + } + } + } +} + +} + +namespace Orthtrees { + +/*! + \brief finds the `k` points within a specific radius that are + nearest to the center of `query_sphere`. + + This function guarantees that there are no closer points than the ones returned, + but it does not guarantee that it will return at least `k` points. + For a query where the search radius encloses `k` or fewer points, all enclosed points will be returned. + If the search radius is too small, no points may be returned. + This function is useful when the user already knows how sparse the points are, + or if they do not care about points that are too far away. + Setting a small radius may have performance benefits. + + \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits + \tparam OutputIterator must be a model of `OutputIterator` that accepts points + \param orthtree the tree to search within + \param query_sphere the region to search within + \param k the number of points to find + \param output the output iterator to add the found points to (in order of increasing distance) + */ +template +OutputIterator nearest_k_neighbors_in_radius( + const Tree& orthtree, + typename Tree::Sphere& query_sphere, + std::size_t k, + OutputIterator output +) { + + // todo: this type is over-constrained, this must be made more generic + struct Node_element_with_distance { + typename Tree::Traits::Node_data_element point; + typename Tree::FT distance; + }; + + // Create an empty list of points + std::vector points_list; + if (k != (std::numeric_limits::max)()) + points_list.reserve(k); + + // Invoking the recursive function adds those points to the vector (passed by reference) + CGAL::internal::nearest_k_neighbors_recursive(orthtree, query_sphere, orthtree.root(), points_list); + + // Add all the points found to the output + for (auto& item: points_list) + *output++ = item.point; + + return output; +} + + +/*! + \brief finds the `k` nearest neighbors of `query`. + + Nearest neighbors are outputted in order of increasing distance to + `query`. + + \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits + \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. + \param orthtree the tree to search within + \param query query point. + \param k number of neighbors. + \param output output iterator. + */ +template +OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Point& query, + std::size_t k, + OutputIterator output) { + typename Tree::Sphere query_sphere(query, (std::numeric_limits::max)()); + return nearest_k_neighbors_in_radius(orthtree, query_sphere, k, output); +} + +/*! + \brief finds the points in `sphere`. + + Nearest neighbors are outputted in order of increasing distance to + the center of `sphere`. + + \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits + \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. + \param orthtree the tree to search within + \param query query sphere. + \param output output iterator. + */ +template +OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Sphere& query, OutputIterator output) { + typename Tree::Sphere query_sphere = query; + return nearest_k_neighbors_in_radius(orthtree, query_sphere, (std::numeric_limits::max)(), output); +} + +} +} + +#endif //ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index bbdd2ac7c151..2b19a2c26081 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -20,8 +20,7 @@ #include #include -namespace CGAL -{ +namespace CGAL { template struct Orthtree_traits_face_graph @@ -42,8 +41,7 @@ struct Orthtree_traits_face_graph using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; // SL: these could be considered as built-in data and if the typedefs are not present, the tree have none - using Node_data_element = typename boost::graph_traits::face_descriptor; - using Node_data = std::vector; + using Node_data = std::vector::face_descriptor>; using Geom_traits = typename Kernel_traits::type; @@ -62,24 +60,22 @@ struct Orthtree_traits_face_graph }; Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } + Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } auto root_node_bbox_object() const { return [&]() -> std::pair { - Array min={0.0,0}, max={0.0,0}; - if (faces(m_pm).begin()!=faces(m_pm).end()) - { + Array min = {0.0, 0}, max = {0.0, 0}; + if (faces(m_pm).begin() != faces(m_pm).end()) { const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); - min={p.x(), p.y(), p.z()}; - max=min; - for (auto v : vertices(m_pm)) - { + min = {p.x(), p.y(), p.z()}; + max = min; + for (auto v: vertices(m_pm)) { const Point_d& p_v = get(m_vpm, v); - for (int i=0; i<3; ++i) - { - if (p_v[i]max[i]) max[i]=p_v[i]; + for (int i = 0; i < 3; ++i) { + if (p_v[i] < min[i]) min[i] = p_v[i]; + if (p_v[i] > max[i]) max[i] = p_v[i]; } } } @@ -95,22 +91,20 @@ struct Orthtree_traits_face_graph }; } - auto distribute_node_contents_object(){ - return [&](typename Tree::Node_index n, Tree &tree, const Point_d ¢er) -> void { + auto distribute_node_contents_object() { + return [&](typename Tree::Node_index n, Tree& tree, const Point_d& center) -> void { Node_data& ndata = tree.data(n); - for (int i=0; i<8; ++i) - { + for (int i = 0; i < 8; ++i) { typename Tree::Node_index child = tree.child(n, i); Node_data& child_data = tree.data(child); Bbox_d bbox = tree.bbox(child); - for (Node_data_element f : ndata) - { + for (auto f: ndata) { typename boost::graph_traits::halfedge_descriptor h = halfedge(f, m_pm); typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), get(m_vpm, target(h, m_pm)), get(m_vpm, target(next(h, m_pm), m_pm))); - if(do_intersect(t, bbox)) + if (do_intersect(t, bbox)) child_data.push_back(f); } } @@ -125,22 +119,20 @@ struct Orthtree_traits_face_graph public: Split_predicate_node_min_extent(FT me) - : m_min_extent(me) - {} + : m_min_extent(me) {} /*! \brief returns `true` if `ni` should be split, `false` otherwise. */ - template - bool operator()(Node_index ni, const Tree &tree) const - { + template + bool operator()(Node_index ni, const Tree& tree) const { if (tree.data(ni).empty()) return false; Bbox_d bb = tree.bbox(ni); //TODO: we should get better version to get guarantees // TODO: as long as the bbox is cubic you can use depth and initial size to conclude. - for (int i=0; i<3; ++i) - if ( (bb.max(i) - bb.min(i)) < 2*m_min_extent ) + for (int i = 0; i < 3; ++i) + if ((bb.max(i) - bb.min(i)) < 2 * m_min_extent) return false; return true; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index 9820b49e1ed5..c54a09892c14 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -154,7 +154,7 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { }; } - auto get_element_object() const { + auto get_geometric_object_for_element_object() const { return [&](const Node_data_element& index) -> typename Self::Point_d { return get(m_point_map, index); }; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index 294acfb6950c..a82759892e4b 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -153,7 +153,7 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { }; } - auto get_element_object() const { + auto get_geometric_object_for_element_object() const { return [&](const Node_data_element& index) -> typename Self::Point_d { return get(m_point_map, index); }; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 52ef0e7413be..40c6bac0b3a3 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -189,7 +189,7 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base typename Self::Point_d { return get(m_point_map, index); }; diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index df58254cb80f..fa4b310ab702 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -73,7 +74,7 @@ void naive_vs_octree(std::size_t dataset_size) { auto octree_start_time = high_resolution_clock::now(); { std::vector k_neighbors; - octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); + CGAL::Orthtrees::nearest_neighbors(octree, random_point, 1, std::back_inserter(k_neighbors)); octree_nearest = get(points.point_map(), *k_neighbors.begin()); } duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; @@ -123,7 +124,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { Octree octree({points, points.point_map()}); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); - octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); + CGAL::Orthtrees::nearest_neighbors(octree, random_point, K, std::back_inserter(octree_nearest_neighbors)); duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; std::cout << "Octree --> " From d0fa6ed68bca70bca8c37f06ca10d72b8a7284c5 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 17:19:29 +0200 Subject: [PATCH 202/520] Add missing license header --- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index b8d67771cfbc..c8c12ea9bb23 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -1,4 +1,13 @@ -// todo: license +// Copyright (c) 2023 INRIA +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Jackson Campolattaro #ifndef ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H #define ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H From dc3e1950680d82dd8d9184d40e2824b8b9b481ab Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Fri, 1 Sep 2023 17:40:25 +0200 Subject: [PATCH 203/520] test for clipart files --- .../test/Polygon_repair/test_clipart.cpp | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Polygon_repair/test/Polygon_repair/test_clipart.cpp diff --git a/Polygon_repair/test/Polygon_repair/test_clipart.cpp b/Polygon_repair/test/Polygon_repair/test_clipart.cpp new file mode 100644 index 000000000000..98dff9742fe0 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/test_clipart.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main(int argc, char* argv[]) { + + std::string folder = "/Users/ken/Downloads/repaired"; + + for (const auto& file: std::filesystem::directory_iterator(folder)) { + if (file.path().filename().extension() != ".svg") continue; + if (file.path().filename().stem() != "182377") continue; + std::cout << "Testing " << file.path().filename() << "... "; + + // Read test file and create multipolygon with outer boundaries + pugi::xml_document doc; + if (doc.load_file(file.path().string().c_str())) { + Multipolygon_with_holes_2 mp; + std::list holes; + for (auto child: doc.child("svg").children()) { + if (strcmp(child.name(), "polygon") == 0) { + Polygon_2 p; + std::string points(child.attribute("points").value()); + std::string color(child.attribute("fill").value()); + + std::istringstream polygonss(points); + std::string point; + while (polygonss >> point) { + std::istringstream pointss(point); + std::string coordinate; + double x, y; + getline(pointss, coordinate, ','); + x = std::stod(coordinate); + getline(pointss, coordinate); + y = std::stod(coordinate); +// std::cout << "(" << x << ", " << y << ")" << std::endl; + p.push_back(Point_2(x, y)); + } + + if (color == "black") { + mp.add_polygon(p); + } else { + holes.push_back(p); + } + } + } + + // Put holes in correct polygon + for (auto const& hole: holes) { + std::set matches; + for (auto const& vertex: hole.vertices()) { + for (std::size_t pn = 0; pn < mp.number_of_polygons(); ++pn) { + if (mp.polygons()[pn].outer_boundary().bounded_side(vertex) == CGAL::ON_BOUNDED_SIDE) { + matches.insert(pn); + } + } + } if (matches.size() == 1) { + std::cout << "Found match" << std::endl; + mp.polygons()[*matches.begin()].add_hole(hole); + } else { + std::cout << "Error: couldn't find polygon for hole" << std::endl; + } + } + + // Check validity + std::cout << CGAL::Polygon_repair::is_valid(mp) << std::endl; + } + } + + return 0; +} From 156675076651b77c57d99b525771f7f1ad498524 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 17:45:09 +0200 Subject: [PATCH 204/520] Switch license to GPL --- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index c8c12ea9bb23..190a49fd70f0 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -5,7 +5,7 @@ // // $URL$ // $Id$ -// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Jackson Campolattaro From e9fc58bd4946afe48d3ae9655cca3036885de3cb Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sat, 2 Sep 2023 13:59:09 +0200 Subject: [PATCH 205/520] get rid of hashes for exact kernels --- .../CGAL/Polygon_repair/Polygon_repair.h | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 42bfa04fae2e..6c20923fb40b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -15,7 +15,6 @@ #include -#include #include #include #include @@ -285,9 +284,19 @@ class Polygon_repair { using Face_base = CGAL::Constrained_triangulation_face_base_2; using Face_base_with_repair_info = CGAL::Triangulation_face_base_with_repair_info_2; using Triangulation_data_structure = CGAL::Triangulation_data_structure_2; - using Tag = typename std::conditional::value, CGAL::Exact_predicates_tag, CGAL::Exact_intersections_tag>::type; + using Tag = typename std::conditional::value, + CGAL::Exact_predicates_tag, + CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; using Triangulation = Triangulation_with_odd_even_constraints_2; + using Edge_map = typename std::conditional::value, + std::unordered_set, + boost::hash>>, + std::set>>::type; + using Vertex_map = typename std::conditional::value, + std::unordered_map, + std::map>::type; + using Validation_tag = CGAL::No_constraint_intersection_tag; using Validation_triangulation = CGAL::Constrained_triangulation_2; @@ -342,12 +351,12 @@ class Polygon_repair { } // Insert vertices - std::unordered_map vertices; + Vertex_map vertices; std::vector> edges_to_insert; edges_to_insert.reserve(unique_edges.size()); for (auto const& edge: unique_edges) { typename Triangulation::Vertex_handle first_vertex, second_vertex; - typename std::unordered_map::const_iterator found = vertices.find(edge.first); + typename Vertex_map::const_iterator found = vertices.find(edge.first); if (found == vertices.end()) { first_vertex = t.insert(edge.first, search_start); vertices[edge.first] = first_vertex; @@ -392,12 +401,12 @@ class Polygon_repair { } // Insert vertices - std::unordered_map vertices; + Vertex_map vertices; std::vector> edges_to_insert; edges_to_insert.reserve(unique_edges.size()); for (auto const& edge: unique_edges) { typename Triangulation::Vertex_handle first_vertex, second_vertex; - typename std::unordered_map::const_iterator found = vertices.find(edge.first); + typename Vertex_map::const_iterator found = vertices.find(edge.first); if (found == vertices.end()) { first_vertex = t.insert(edge.first, search_start); vertices[edge.first] = first_vertex; @@ -444,12 +453,12 @@ class Polygon_repair { } // Insert vertices - std::unordered_map vertices; + Vertex_map vertices; std::vector> edges_to_insert; edges_to_insert.reserve(unique_edges.size()); for (auto const& edge: unique_edges) { typename Triangulation::Vertex_handle first_vertex, second_vertex; - typename std::unordered_map::const_iterator found = vertices.find(edge.first); + typename Vertex_map::const_iterator found = vertices.find(edge.first); if (found == vertices.end()) { first_vertex = t.insert(edge.first, search_start); vertices[edge.first] = first_vertex; @@ -666,8 +675,7 @@ class Polygon_repair { protected: Triangulation t; - std::unordered_set, - boost::hash>> unique_edges; + Edge_map unique_edges; Multipolygon_with_holes_2 mp; int number_of_polygons, number_of_holes; typename Triangulation::Face_handle search_start; From 3386efda797e809d41aa66cc6968a3c867b79404 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sat, 2 Sep 2023 14:15:09 +0200 Subject: [PATCH 206/520] test showing exact kernel --- .../test/Polygon_repair/exact_test.cpp | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Polygon_repair/test/Polygon_repair/exact_test.cpp diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp new file mode 100644 index 000000000000..e0263867fa11 --- /dev/null +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_exact_constructions_kernel; +using Point_2 = Kernel::Point_2; +using Polygon_2 = CGAL::Polygon_2; +using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; +using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; +using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; + +int main(int argc, char* argv[]) { + + std::string in = "POLYGON((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5))"; + std::istringstream iss(in); + Multipolygon_with_holes_2 rmp; + + Polygon_with_holes_2 p; + CGAL::IO::read_polygon_WKT(iss, p); + CGAL::draw(p); + Polygon_repair pr; + for (auto const edge: p.outer_boundary().edges()) { + pr.triangulation().odd_even_insert_constraint(edge.source(), edge.target()); + } int spikes = 20; + for (auto const& hole: p.holes()) { + for (auto const edge: hole.edges()) { + if (spikes-- <= 0) break; + pr.triangulation().odd_even_insert_constraint(edge.source(), edge.target()); + } + } + pr.label_triangulation_odd_even(); + pr.reconstruct_multipolygon(); + rmp = CGAL::Polygon_repair::repair_odd_even(p); + std::ostringstream oss; + CGAL::IO::write_multi_polygon_WKT(oss, rmp); + std::string out = oss.str(); + std::cout << "\tin: " << in << std::endl; + std::cout << "\tout: " << out; + CGAL::draw(rmp); + + return 0; +} From a5cea61a3309c894bf1e763ae495b91a97cd2458 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sat, 2 Sep 2023 15:03:36 +0200 Subject: [PATCH 207/520] exact_test needs qt too --- Polygon_repair/test/Polygon_repair/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Polygon_repair/test/Polygon_repair/CMakeLists.txt b/Polygon_repair/test/Polygon_repair/CMakeLists.txt index 858e342b24ab..cc6b07baefe6 100644 --- a/Polygon_repair/test/Polygon_repair/CMakeLists.txt +++ b/Polygon_repair/test/Polygon_repair/CMakeLists.txt @@ -17,4 +17,5 @@ endforeach() if(CGAL_Qt5_FOUND) target_link_libraries(draw_test_polygons PUBLIC CGAL::CGAL_Basic_viewer) + target_link_libraries(exact_test PUBLIC CGAL::CGAL_Basic_viewer) endif() \ No newline at end of file From 99a2d5612f5a595cf1ea788cb8582ba74e804220 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 2 Sep 2023 15:09:16 +0200 Subject: [PATCH 208/520] Replace bbox with Iso_rectangle/_cuboid/_box --- .../examples/Orthtree/octree_surface_mesh.cpp | 2 +- .../include/CGAL/Orthtree_traits_2_base.h | 2 +- .../include/CGAL/Orthtree_traits_3_base.h | 3 +- .../include/CGAL/Orthtree_traits_d_base.h | 19 +---- .../include/CGAL/Orthtree_traits_face_graph.h | 2 +- Orthtree/test/Orthtree/test_octree_bbox.cpp | 72 ++++++++++--------- 6 files changed, 43 insertions(+), 57 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index c2acd05791e6..2e8fdd510330 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -20,7 +20,7 @@ void dump_as_polylines(const Octree& ot) { if (!ot.is_leaf(node)) continue; - CGAL::Bbox_3 bb = ot.bbox(node); + auto bb = ot.bbox(node); out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() << " " << bb.xmax() << " " << bb.ymin() << " " << bb.zmin() << "\n"; out << "2 " << bb.xmin() << " " << bb.ymin() << " " << bb.zmin() diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h index 84cfc33b59ad..a4a51d7a502a 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_2_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_2_base.h @@ -42,9 +42,9 @@ struct Orthtree_traits_2_base { /// @{ using Dimension = Dimension_tag<2>; - using Bbox_d = Bbox_2; using FT = typename K::FT; using Point_d = typename K::Point_2; + using Bbox_d = typename K::Iso_rectangle_2; using Sphere_d = typename K::Circle_2; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; using Array = std::array; // todo: This should have a more descriptive name diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h index 781fcd99e32f..d6ecb38aac45 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -41,10 +41,11 @@ struct Orthtree_traits_3_base { /// \name Types /// @{ + using GeomTraits = K; using Dimension = Dimension_tag<3>; - using Bbox_d = Bbox_3; using FT = typename K::FT; using Point_d = typename K::Point_3; + using Bbox_d = typename K::Iso_cuboid_3; using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; using Array = std::array; // todo: This should have a more descriptive name diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h index 82b9268d9009..671cca822031 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_d_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_d_base.h @@ -42,28 +42,11 @@ struct Orthtree_traits_d_base { using Dimension = DimensionTag; using FT = typename K::FT; using Point_d = typename K::Point_d; + using Bbox_d = typename K::Iso_box_d; using Sphere_d = typename K::Sphere_d; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; using Array = std::array; -#ifdef DOXYGEN_RUNNING - typedef unspecified_type Bbox_d; ///< Bounding box type. -#else - - class Bbox_d { - Point_d m_min, m_max; - public: - - Bbox_d(const Point_d& pmin, const Point_d& pmax) - : m_min(pmin), m_max(pmax) {} - - const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_min; } - - const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION() { return m_max; } - }; - -#endif - /*! Adjacency type. diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 2b19a2c26081..d52a1e540245 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -132,7 +132,7 @@ struct Orthtree_traits_face_graph //TODO: we should get better version to get guarantees // TODO: as long as the bbox is cubic you can use depth and initial size to conclude. for (int i = 0; i < 3; ++i) - if ((bb.max(i) - bb.min(i)) < 2 * m_min_extent) + if ((bb.max()[i] - bb.min()[i]) < 2 * m_min_extent) return false; return true; } diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index c36e0498474f..227aac05d6bb 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -23,8 +23,10 @@ void test_1_node() { Octree octree({points, points.point_map()}); octree.refine(10, 1); + Octree::Bbox expected_bbox{-1, -1, -1, -1, -1, -1}; + // Compare the top (only) node - assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1, -1, -1, -1, -1, -1)); + assert(octree.bbox(octree.root()) == Octree::Bbox(-1, -1, -1, -1, -1, -1)); } void test_9_nodes() { @@ -39,17 +41,17 @@ void test_9_nodes() { octree.refine(10, 1); // Compare the top node - assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.root()) == Octree::Bbox(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1)); // Compare the child nodes - assert(octree.bbox(octree.node(0)) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0)); - assert(octree.bbox(octree.node(1)) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0)); - assert(octree.bbox(octree.node(2)) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0)); - assert(octree.bbox(octree.node(3)) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0)); - assert(octree.bbox(octree.node(4)) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1)); - assert(octree.bbox(octree.node(5)) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1)); - assert(octree.bbox(octree.node(6)) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1)); - assert(octree.bbox(octree.node(7)) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1)); + assert(octree.bbox(octree.node(0)) == Octree::Bbox(-1.1, -1.1, -1.1, 0, 0, 0)); + assert(octree.bbox(octree.node(1)) == Octree::Bbox(0, -1.1, -1.1, 1.1, 0, 0)); + assert(octree.bbox(octree.node(2)) == Octree::Bbox(-1.1, 0, -1.1, 0, 1.1, 0)); + assert(octree.bbox(octree.node(3)) == Octree::Bbox(0, 0, -1.1, 1.1, 1.1, 0)); + assert(octree.bbox(octree.node(4)) == Octree::Bbox(-1.1, -1.1, 0, 0, 0, 1.1)); + assert(octree.bbox(octree.node(5)) == Octree::Bbox(0, -1.1, 0, 1.1, 0, 1.1)); + assert(octree.bbox(octree.node(6)) == Octree::Bbox(-1.1, 0, 0, 0, 1.1, 1.1)); + assert(octree.bbox(octree.node(7)) == Octree::Bbox(0, 0, 0, 1.1, 1.1, 1.1)); } void test_25_nodes() { @@ -66,53 +68,53 @@ void test_25_nodes() { octree.refine(10, 1); // Compare the top node - assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.root()) == Octree::Bbox(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5)); // Compare the child nodes - assert(octree.bbox(octree.node(0)) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0)); - assert(octree.bbox(octree.node(1)) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0)); - assert(octree.bbox(octree.node(2)) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0)); - assert(octree.bbox(octree.node(3)) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0)); - assert(octree.bbox(octree.node(4)) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5)); - assert(octree.bbox(octree.node(5)) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5)); - assert(octree.bbox(octree.node(6)) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5)); - assert(octree.bbox(octree.node(7)) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5)); + assert(octree.bbox(octree.node(0)) == Octree::Bbox(-1.5, -1.5, -1.5, 0, 0, 0)); + assert(octree.bbox(octree.node(1)) == Octree::Bbox(0, -1.5, -1.5, 1.5, 0, 0)); + assert(octree.bbox(octree.node(2)) == Octree::Bbox(-1.5, 0, -1.5, 0, 1.5, 0)); + assert(octree.bbox(octree.node(3)) == Octree::Bbox(0, 0, -1.5, 1.5, 1.5, 0)); + assert(octree.bbox(octree.node(4)) == Octree::Bbox(-1.5, -1.5, 0, 0, 0, 1.5)); + assert(octree.bbox(octree.node(5)) == Octree::Bbox(0, -1.5, 0, 1.5, 0, 1.5)); + assert(octree.bbox(octree.node(6)) == Octree::Bbox(-1.5, 0, 0, 0, 1.5, 1.5)); + assert(octree.bbox(octree.node(7)) == Octree::Bbox(0, 0, 0, 1.5, 1.5, 1.5)); // Compare children of the first child assert(octree.bbox(octree.node(0, 0)) == - CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); + Octree::Bbox(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75)); assert(octree.bbox(octree.node(0, 1)) == - CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); + Octree::Bbox(-0.75, -1.5, -1.5, 0, -0.75, -0.75)); assert(octree.bbox(octree.node(0, 2)) == - CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); + Octree::Bbox(-1.5, -0.75, -1.5, -0.75, 0, -0.75)); assert(octree.bbox(octree.node(0, 3)) == - CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75)); + Octree::Bbox(-0.75, -0.75, -1.5, 0, 0, -0.75)); assert(octree.bbox(octree.node(0, 4)) == - CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); + Octree::Bbox(-1.5, -1.5, -0.75, -0.75, -0.75, 0)); assert(octree.bbox(octree.node(0, 5)) == - CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0)); + Octree::Bbox(-0.75, -1.5, -0.75, 0, -0.75, 0)); assert(octree.bbox(octree.node(0, 6)) == - CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0)); + Octree::Bbox(-1.5, -0.75, -0.75, -0.75, 0, 0)); assert(octree.bbox(octree.node(0, 7)) == - CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0)); + Octree::Bbox(-0.75, -0.75, -0.75, 0, 0, 0)); // Compare children of the last child assert(octree.bbox(octree.node(7, 0)) == - CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75)); + Octree::Bbox(0, 0, 0, 0.75, 0.75, 0.75)); assert(octree.bbox(octree.node(7, 1)) == - CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75)); + Octree::Bbox(0.75, 0, 0, 1.5, 0.75, 0.75)); assert(octree.bbox(octree.node(7, 2)) == - CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75)); + Octree::Bbox(0, 0.75, 0, 0.75, 1.5, 0.75)); assert(octree.bbox(octree.node(7, 3)) == - CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75)); + Octree::Bbox(0.75, 0.75, 0, 1.5, 1.5, 0.75)); assert(octree.bbox(octree.node(7, 4)) == - CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5)); + Octree::Bbox(0, 0, 0.75, 0.75, 0.75, 1.5)); assert(octree.bbox(octree.node(7, 5)) == - CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5)); + Octree::Bbox(0.75, 0, 0.75, 1.5, 0.75, 1.5)); assert(octree.bbox(octree.node(7, 6)) == - CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5)); + Octree::Bbox(0, 0.75, 0.75, 0.75, 1.5, 1.5)); assert(octree.bbox(octree.node(7, 7)) == - CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); + Octree::Bbox(0.75, 0.75, 0.75, 1.5, 1.5, 1.5)); } int main(void) { From 51aae3a6a8194d5cad2d67181ee888b63376aa70 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 2 Sep 2023 15:19:07 +0200 Subject: [PATCH 209/520] Bbox construction functor is no longer necessary; Bbox_d type must be constructible from a pair of points --- .../CollectionPartitioningOrthtreeTraits.h | 5 +++++ .../doc/Orthtree/Concepts/OrthtreeTraits.h | 13 +---------- Orthtree/include/CGAL/Orthtree.h | 5 ++--- .../include/CGAL/Orthtree_traits_face_graph.h | 9 -------- .../include/CGAL/Orthtree_traits_point_2.h | 22 ------------------- .../include/CGAL/Orthtree_traits_point_3.h | 21 ------------------ .../include/CGAL/Orthtree_traits_point_d.h | 21 ------------------ 7 files changed, 8 insertions(+), 88 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index e601256d1ad6..0d6f1f0222a0 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -24,6 +24,11 @@ class CollectionPartitioningOrthtreeTraits { /// \name Types /// @{ + /*! + * Sphere type used for the shrinking-sphere approach for neighbor queries + */ + typedef unspecified_type Sphere_d; + /*! * \brief An element of the `Node_data` list-like type. * diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index bc76e6a69458..09837bfbbd04 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -18,10 +18,9 @@ class OrthtreeTraits /// @{ typedef unspecified_type Dimension; ///< Dimension type (see `CGAL::Dimension_tag`). - typedef unspecified_type Bbox_d; ///< Bounding box type. typedef unspecified_type FT; ///< The number type of the %Cartesian coordinates of types `Point_d` typedef unspecified_type Point_d; ///< Point type. - typedef unspecified_type Sphere_d; ///< The sphere type for neighbor queries. + typedef unspecified_type Bbox_d; ///< Bounding box type. Must be constructible from a pair of Point_d types. /*! A random access iterator type to enumerate the @@ -43,11 +42,6 @@ class OrthtreeTraits */ typedef unspecified_type Construct_point_d_from_array; - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; - /// @} /// \name Operations @@ -58,11 +52,6 @@ class OrthtreeTraits */ Construct_point_d_from_array construct_point_d_from_array_object() const; - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const; - /*! * \brief Produces a bounding box which encloses the contents of the tree * diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d65aadcbfb66..ee5c9fcd9224 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -500,9 +500,8 @@ class Orthtree { } // Create the bbox - auto construct_bbox - = m_traits.construct_bbox_d_object(); - return construct_bbox(min_corner, max_corner); + return {m_traits.construct_point_d_from_array_object()(min_corner), + m_traits.construct_point_d_from_array_object()(max_corner)}; } /// @} diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index d52a1e540245..d1eb27cd9e89 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -45,13 +45,6 @@ struct Orthtree_traits_face_graph using Geom_traits = typename Kernel_traits::type; - struct Construct_bbox_d { - Bbox_d operator()(const Array& min, - const Array& max) const { - return Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); - } - }; - // SL: why? struct Construct_point_d_from_array { Point_d operator()(const Array& array) const { @@ -61,8 +54,6 @@ struct Orthtree_traits_face_graph Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } - Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - auto root_node_bbox_object() const { return [&]() -> std::pair { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index c54a09892c14..df9acd3f9ec5 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -72,23 +72,6 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { }; #endif - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - struct Construct_bbox_d - { - typename Self::Bbox_d operator() (const typename Self::Array& min, - const typename Self::Array& max) const - { - return typename Self::Bbox_d (min[0], min[1], max[0], max[1]); - } - }; -#endif - /// @} Orthtree_traits_point_2( @@ -104,11 +87,6 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { */ Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - auto root_node_bbox_object() const { return [&]() -> std::pair { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index a82759892e4b..03f62a312f4d 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -70,22 +70,6 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { } }; -#endif - -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points). - */ - typedef unspecified_type Construct_bbox_d; -#else - - struct Construct_bbox_d { - typename Self::Bbox_d operator()(const typename Self::Array& min, - const typename Self::Array& max) const { - return typename Self::Bbox_d(min[0], min[1], min[2], max[0], max[1], max[2]); - } - }; - #endif /// @} @@ -103,11 +87,6 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { */ Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } - /*! - Function used to construct an object of type `Construct_bbox_d`. - */ - Construct_bbox_d construct_bbox_d_object() const { return Construct_bbox_d(); } - auto root_node_bbox_object() const { return [&]() -> std::pair { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 40c6bac0b3a3..c21cfe71b1ae 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -106,22 +106,6 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base std::pair { From fbf3e08a5f26e96ebb670832cf998b1ee23b89a9 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sat, 2 Sep 2023 15:47:37 +0200 Subject: [PATCH 210/520] test_clipart not needed --- .../test/Polygon_repair/test_clipart.cpp | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 Polygon_repair/test/Polygon_repair/test_clipart.cpp diff --git a/Polygon_repair/test/Polygon_repair/test_clipart.cpp b/Polygon_repair/test/Polygon_repair/test_clipart.cpp deleted file mode 100644 index 98dff9742fe0..000000000000 --- a/Polygon_repair/test/Polygon_repair/test_clipart.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -#include -#include - -using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; -using Point_2 = Kernel::Point_2; -using Polygon_2 = CGAL::Polygon_2; -using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; -using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; - -int main(int argc, char* argv[]) { - - std::string folder = "/Users/ken/Downloads/repaired"; - - for (const auto& file: std::filesystem::directory_iterator(folder)) { - if (file.path().filename().extension() != ".svg") continue; - if (file.path().filename().stem() != "182377") continue; - std::cout << "Testing " << file.path().filename() << "... "; - - // Read test file and create multipolygon with outer boundaries - pugi::xml_document doc; - if (doc.load_file(file.path().string().c_str())) { - Multipolygon_with_holes_2 mp; - std::list holes; - for (auto child: doc.child("svg").children()) { - if (strcmp(child.name(), "polygon") == 0) { - Polygon_2 p; - std::string points(child.attribute("points").value()); - std::string color(child.attribute("fill").value()); - - std::istringstream polygonss(points); - std::string point; - while (polygonss >> point) { - std::istringstream pointss(point); - std::string coordinate; - double x, y; - getline(pointss, coordinate, ','); - x = std::stod(coordinate); - getline(pointss, coordinate); - y = std::stod(coordinate); -// std::cout << "(" << x << ", " << y << ")" << std::endl; - p.push_back(Point_2(x, y)); - } - - if (color == "black") { - mp.add_polygon(p); - } else { - holes.push_back(p); - } - } - } - - // Put holes in correct polygon - for (auto const& hole: holes) { - std::set matches; - for (auto const& vertex: hole.vertices()) { - for (std::size_t pn = 0; pn < mp.number_of_polygons(); ++pn) { - if (mp.polygons()[pn].outer_boundary().bounded_side(vertex) == CGAL::ON_BOUNDED_SIDE) { - matches.insert(pn); - } - } - } if (matches.size() == 1) { - std::cout << "Found match" << std::endl; - mp.polygons()[*matches.begin()].add_hole(hole); - } else { - std::cout << "Error: couldn't find polygon for hole" << std::endl; - } - } - - // Check validity - std::cout << CGAL::Polygon_repair::is_valid(mp) << std::endl; - } - } - - return 0; -} From 091511b9f7be019ddad21798ba346910643055c1 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sat, 2 Sep 2023 15:56:01 +0200 Subject: [PATCH 211/520] test clipart files directly instead --- .../test/Polygon_repair/clipart.cpp | 57 +++++-------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/clipart.cpp b/Polygon_repair/test/Polygon_repair/clipart.cpp index 68b9f86e001d..cc184ee781ef 100644 --- a/Polygon_repair/test/Polygon_repair/clipart.cpp +++ b/Polygon_repair/test/Polygon_repair/clipart.cpp @@ -22,24 +22,20 @@ void print_timer(clock_t start_time) { int main(int argc, char* argv[]) { -// std::string folder_in = "/Volumes/Toshiba/out_fix"; -// std::string folder_out = "/Volumes/Toshiba/repaired"; - std::string folder_in = "/Users/ken/Downloads/test"; - std::string folder_out = "/Users/ken/Downloads/test"; + std::string folder_in = "/Volumes/T7 Shield/out_fix"; + std::string folder_out = "/Volumes/T7 Shield/repaired"; double desired_width = 500.0; - int current = 0, how_many = 100000; clock_t start_time; for (const auto& file: std::filesystem::directory_iterator(folder_in)) { if (file.path().filename().extension() != ".obj") continue; std::cout << "Reading " << file.path().filename() << "..."; -// if (std::filesystem::exists(folder_out + "/" + std::string(file.path().stem()) + ".svg")) { -// std::cout << " skipped: already processed" << std::endl; -// continue; -// } + if (std::filesystem::exists(folder_out + "/" + std::string(file.path().stem()) + ".svg")) { + std::cout << " skipped: already processed" << std::endl; + continue; + } - start_time = clock(); Polygon_repair pr; std::vector vertices; std::vector> edges; @@ -61,11 +57,7 @@ int main(int argc, char* argv[]) { edges.push_back(std::make_pair(vertices[a-1], vertices[b-1])); } } ifs.close(); - std::cout << "Read and parsed file in "; - print_timer(start_time); - std::cout << std::endl; - start_time = clock(); std::unordered_set, boost::hash>> edges_to_insert; @@ -75,46 +67,26 @@ int main(int argc, char* argv[]) { std::make_pair(edge.first, edge.second) : std::make_pair(edge.second, edge.first); auto inserted = edges_to_insert.insert(pair); if (!inserted.second) edges_to_insert.erase(inserted.first); - } std::cout << "Generated unique edges in "; - print_timer(start_time); - std::cout << std::endl; + } - start_time = clock(); Polygon_repair::Triangulation::Face_handle search_start; for (auto const& edge: edges_to_insert) { Polygon_repair::Triangulation::Vertex_handle va = pr.triangulation().insert(edge.first, search_start); Polygon_repair::Triangulation::Vertex_handle vb = pr.triangulation().insert(edge.second, va->face()); // vb is likely close to va pr.triangulation().odd_even_insert_constraint(va, vb); search_start = vb->face(); - } std::cout << "Inserted constraints in "; - print_timer(start_time); - std::cout << std::endl; - - // std::cout << pr.triangulation().number_of_faces() << " faces in the triangulation" << std::endl; + } if (pr.triangulation().number_of_faces() > 0) { - start_time = clock(); pr.label_triangulation_odd_even(); - std::cout << "Labelled in "; - print_timer(start_time); - std::cout << std::endl; - - start_time = clock(); pr.reconstruct_multipolygon(); - std::cout << "Reconstructed multipolygon in "; - print_timer(start_time); - std::cout << std::endl; } Multipolygon_with_holes_2 mp = pr.multipolygon(); - // std::cout << mp << std::endl; - // std::cout << mp.number_of_polygons() << " polygons" << std::endl; - if (mp.number_of_polygons() > 0) { CGAL::Bbox_2 bbox = mp.polygons().front().bbox(); for (auto const& polygon: mp.polygons()) { bbox += polygon.outer_boundary().bbox(); - } // std::cout << bbox.xmin() << " " << bbox.xmax() << " " << bbox.ymin() << " " << bbox.ymax() << std::endl; - Kernel::Vector_2 translate(-bbox.xmin(), -bbox.ymin()); + } Kernel::Vector_2 translate(-bbox.xmin(), -bbox.ymin()); double scale = desired_width/(bbox.xmax()-bbox.xmin()); @@ -127,9 +99,6 @@ int main(int argc, char* argv[]) { for (auto const& vertex: polygon.outer_boundary()) { ofs << scale*(vertex.x()+translate.x()) << "," << scale*(vertex.y()+translate.y()) << " "; } ofs << "\" fill=\"black\"/>" << std::endl; - } - - for (auto const& polygon: mp.polygons()) { for (auto const& hole: polygon.holes()) { // std::cout << hole << std::endl; ofs << "\t"; ofs.close(); - std::cout << " ok" << std::endl; - } ++current; - if (current >= how_many) break; + if (CGAL::Polygon_repair::is_valid(mp)) { + std::cout << " ok" << std::endl; + } + } + } return 0; From 23c4e1098b275f5ab4fc4ebf0e61d754bd851f95 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Sat, 2 Sep 2023 16:22:39 +0200 Subject: [PATCH 212/520] remove default kernel --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 6c20923fb40b..addb665bc26f 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -276,8 +276,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo * The class `Polygon_repair` builds on a constrained * triangulation to remove the parts of constraints that overlap an even number of times */ -template > +template > class Polygon_repair { public: using Vertex_base = CGAL::Triangulation_vertex_base_2; From fd34fb53029f6f4c8dfd47019dd6a7ae6ff28256 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 2 Sep 2023 16:49:32 +0200 Subject: [PATCH 213/520] `root_node_contents` now produces a Bbox directly Some tests fail due to non-cubic bounding boxes --- .../Orthtree/quadtree_build_manually.cpp | 13 +--- Orthtree/include/CGAL/Orthtree.h | 25 ++----- .../include/CGAL/Orthtree_traits_face_graph.h | 5 +- .../include/CGAL/Orthtree_traits_point_2.h | 5 +- .../include/CGAL/Orthtree_traits_point_3.h | 5 +- .../include/CGAL/Orthtree_traits_point_d.h | 5 +- Orthtree/test/Orthtree/test_octree_bbox.cpp | 72 +++++++++---------- Orthtree/test/Orthtree/test_octree_locate.cpp | 34 ++++----- Orthtree/test/Orthtree/test_octree_refine.cpp | 4 +- .../test/Orthtree/test_octree_traverse.cpp | 15 ++-- 10 files changed, 80 insertions(+), 103 deletions(-) diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 3f9e6c690d39..8d989ceae865 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -20,7 +20,6 @@ struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { using Tree = Orthtree; using Node_data = std::array; - using Node_data_element = empty_type; Orthtree_traits_empty_2(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; @@ -29,18 +28,8 @@ struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { } using Construct_point_d_from_array = std::invoke_result_t; - auto construct_bbox_d_object() const { - return [](const typename Self::Array& min, const typename Self::Array& max) -> typename Self::Bbox_d { - return {min[0], min[1], max[0], max[1]}; - }; - } - using Construct_bbox_d = std::invoke_result_t; - auto root_node_bbox_object() const { - return [&]() -> std::pair { - return {{m_bbox.xmax(), m_bbox.ymax()}, - {m_bbox.xmax(), m_bbox.ymax()}}; - }; + return [&]() -> typename Self::Bbox_d { return m_bbox; }; } auto root_node_contents_object() const { return [&]() -> Node_data { return {}; }; } diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ee5c9fcd9224..6d40fb7feeb7 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -205,7 +205,7 @@ class Orthtree { \param enlarge_ratio ratio to which the bounding box should be enlarged. \param traits the traits object. */ - explicit Orthtree(Traits traits, const FT enlarge_ratio = 1.2) : + explicit Orthtree(Traits traits) : m_traits(traits), m_node_points(m_node_properties.add_property("points")), m_node_depths(m_node_properties.add_property("depths", 0)), @@ -215,29 +215,12 @@ class Orthtree { m_node_properties.emplace(); - // init bbox with first values found - auto [bbox_min, bbox_max] = m_traits.root_node_bbox_object()(); - - // Dilate the bounding box - Array bbox_centroid; - FT max_length = FT(0); - for (std::size_t i = 0; i < Dimension::value; ++i) { - bbox_centroid[i] = (bbox_min[i] + bbox_max[i]) / FT(2); - max_length = (std::max)(max_length, bbox_max[i] - bbox_min[i]); - } - max_length *= enlarge_ratio / FT(2); - for (std::size_t i = 0; i < Dimension::value; ++i) { - bbox_min[i] = bbox_centroid[i] - max_length; - bbox_max[i] = bbox_centroid[i] + max_length; - } - - auto construct_point_d_from_array - = m_traits.construct_point_d_from_array_object(); + auto bbox = m_traits.root_node_bbox_object()(); // save orthtree attributes - m_bbox_min = construct_point_d_from_array(bbox_min); - m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]); + m_bbox_min = bbox.min(); + m_side_per_depth.push_back(bbox.max()[0] - bbox.min()[0]); data(root()) = m_traits.root_node_contents_object()(); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index d1eb27cd9e89..01b709efc351 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -55,7 +55,7 @@ struct Orthtree_traits_face_graph Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } auto root_node_bbox_object() const { - return [&]() -> std::pair { + return [&]() -> Bbox_d { Array min = {0.0, 0}, max = {0.0, 0}; if (faces(m_pm).begin() != faces(m_pm).end()) { @@ -71,7 +71,8 @@ struct Orthtree_traits_face_graph } } - return {min, max}; + return {construct_point_d_from_array_object()(min), + construct_point_d_from_array_object()(max)}; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index df9acd3f9ec5..ff52558d1e83 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -88,7 +88,7 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } auto root_node_bbox_object() const { - return [&]() -> std::pair { + return [&]() -> typename Self::Bbox_d { typename Self::Array bbox_min; typename Self::Array bbox_max; @@ -115,7 +115,8 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { } } - return {bbox_min, bbox_max}; + return {construct_point_d_from_array_object()(bbox_min), + construct_point_d_from_array_object()(bbox_max)}; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index 03f62a312f4d..9bd3a12cd04c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -88,7 +88,7 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } auto root_node_bbox_object() const { - return [&]() -> std::pair { + return [&]() -> typename Self::Bbox_d { typename Self::Array bbox_min; typename Self::Array bbox_max; @@ -115,7 +115,8 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { } } - return {bbox_min, bbox_max}; + return {construct_point_d_from_array_object()(bbox_min), + construct_point_d_from_array_object()(bbox_max)}; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index c21cfe71b1ae..d054164fe259 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -124,7 +124,7 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base std::pair { + return [&]() -> typename Self::Bbox_d { typename Self::Array bbox_min; typename Self::Array bbox_max; @@ -151,7 +151,8 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base(); @@ -105,28 +106,28 @@ bool test_preorder_25_nodes() { assert(*iter == octree.root()); iter++; assert(*iter == octree.node(0)); + for (int i = 0; i < 8; ++i) { + iter++; + assert(*iter == octree.node(0, i)); + } iter++; assert(*iter == octree.node(1)); iter++; assert((*iter == octree.node(2))); iter++; assert(*iter == octree.node(3)); + iter++; + assert((*iter == octree.node(4))); for (int i = 0; i < 8; ++i) { iter++; - assert(*iter == octree.node(3, i)); + assert(*iter == octree.node(4, i)); } iter++; - assert((*iter == octree.node(4))); - iter++; assert((*iter == octree.node(5))); iter++; assert((*iter == octree.node(6))); iter++; assert((*iter == octree.node(7))); - for (int i = 0; i < 8; ++i) { - iter++; - assert(*iter == octree.node(7, i)); - } return true; } From 039b693b6157c0d29e5dda537544541b215cb8dd Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 2 Sep 2023 17:37:34 +0200 Subject: [PATCH 214/520] Add support for trees with non-cubic bounding boxes High-order orthtrees break, because Epick_d::Point is somehow defined as an array --- Orthtree/examples/Orthtree/orthtree_build.cpp | 1 + Orthtree/include/CGAL/Orthtree.h | 20 ++++++++++--------- .../include/CGAL/Orthtree_traits_point_d.h | 2 +- Orthtree/test/Orthtree/test_octree_refine.cpp | 3 +-- .../test/Orthtree/test_octree_traverse.cpp | 14 ++++++------- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 7d129cbde6fc..2dca94d50755 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 6d40fb7feeb7..c7dc72f33e82 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -169,7 +169,8 @@ class Orthtree { Point m_bbox_min; /* input bounding box min value */ - std::vector m_side_per_depth; /* side length per node's depth */ + using Bbox_dimensions = decltype(std::declval().max() - std::declval().min()); + std::vector m_side_per_depth; /* side length per node's depth */ Cartesian_ranges cartesian_range; /* a helper to easily iterator on coordinates of points */ @@ -220,7 +221,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = bbox.min(); - m_side_per_depth.push_back(bbox.max()[0] - bbox.min()[0]); + m_side_per_depth.push_back(bbox.max() - bbox.min()); data(root()) = m_traits.root_node_contents_object()(); } @@ -471,20 +472,19 @@ class Orthtree { Bbox bbox(Node_index n) const { // Determine the side length of this node - FT size = m_side_per_depth[depth(n)]; + Bbox_dimensions size = m_side_per_depth[depth(n)]; // Determine the location this node should be split Array min_corner; Array max_corner; for (int i = 0; i < Dimension::value; i++) { - min_corner[i] = m_bbox_min[i] + (global_coordinates(n)[i] * size); - max_corner[i] = min_corner[i] + size; + min_corner[i] = m_bbox_min[i] + (global_coordinates(n)[i] * size[i]); } // Create the bbox return {m_traits.construct_point_d_from_array_object()(min_corner), - m_traits.construct_point_d_from_array_object()(max_corner)}; + m_traits.construct_point_d_from_array_object()(min_corner) + size}; } /// @} @@ -494,6 +494,7 @@ class Orthtree { template std::pair>, bool> + get_or_add_node_property(const std::string& name, const T default_value = T()) { return m_node_properties.get_or_add_property(name, default_value); } @@ -509,7 +510,8 @@ class Orthtree { } template - std::optional>> + std::optional>> + get_node_property_if_exists(const std::string& name) { return m_node_properties.get_property_if_exists(name); } @@ -793,13 +795,13 @@ class Orthtree { Point barycenter(Node_index n) const { // Determine the side length of this node - FT size = m_side_per_depth[depth(n)]; + Bbox_dimensions size = m_side_per_depth[depth(n)]; // Determine the location this node should be split Array bary; std::size_t i = 0; for (const FT& f: cartesian_range(m_bbox_min)) { - bary[i] = FT(global_coordinates(n)[i]) * size + size / FT(2) + f; + bary[i] = FT(global_coordinates(n)[i]) * size[i] + size[i] / FT(2) + f; ++i; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index d054164fe259..48d3a905690c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -102,7 +102,7 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base Date: Mon, 4 Sep 2023 11:58:44 +0200 Subject: [PATCH 215/520] update dependencies --- Polygon_repair/package_info/Polygon_repair/dependencies | 2 -- 1 file changed, 2 deletions(-) diff --git a/Polygon_repair/package_info/Polygon_repair/dependencies b/Polygon_repair/package_info/Polygon_repair/dependencies index 3678863c6925..107d9f6d0d96 100644 --- a/Polygon_repair/package_info/Polygon_repair/dependencies +++ b/Polygon_repair/package_info/Polygon_repair/dependencies @@ -6,13 +6,11 @@ Distance_2 Distance_3 Filtered_kernel Hash_map -Homogeneous_kernel Installation Intersections_2 Intersections_3 Interval_support Kernel_23 -Kernel_d Modular_arithmetic Number_types Polygon From 248866871ea4338f02a1b931b5729c43e1293ada Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 5 Sep 2023 15:46:54 +0200 Subject: [PATCH 216/520] adaptation of Efficient_RANSAC to changed Orthtree interface --- .../Efficient_RANSAC/Efficient_RANSAC.h | 57 ++++++------------- .../Efficient_RANSAC_traits.h | 2 + .../Shape_detection/Efficient_RANSAC/Octree.h | 34 ++++++++--- 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h index 88089e89e01a..f8f0fe2959f9 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h @@ -496,11 +496,11 @@ class Efficient_RANSAC { } // Use bounding box diagonal as reference for default values - Bbox_3 bbox = m_global_octree->boundingBox(); + auto bbox = m_global_octree->boundingBox(); FT bbox_diagonal = (FT) CGAL::sqrt( - (bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) - + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) - + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); + (bbox.max()[0] - bbox.min()[0]) * (bbox.max()[0] - bbox.min()[0]) + + (bbox.max()[1] - bbox.min()[1]) * (bbox.max()[1] - bbox.min()[1]) + + (bbox.max()[2] - bbox.min()[2]) * (bbox.max()[2] - bbox.min()[2])); // Epsilon or cluster_epsilon have been set by the user? // If not, derive from bounding box diagonal @@ -1086,7 +1086,7 @@ class Efficient_RANSAC { Cell cell = stack.top(); stack.pop(); - FT width = octree->width() / (1 << (cell.depth())); + FT width = octree->width() / (1 << (octree->depth(cell))); FT diag = CGAL::sqrt(FT(3) * width * width) + epsilon; @@ -1097,10 +1097,10 @@ class Efficient_RANSAC { // differ between full or partial overlap? // if full overlap further traversal of this branch is not necessary - if (cell.is_leaf()) { + if (octree->is_leaf(cell)) { std::vector indices; - indices.reserve(cell.size()); - for (std::size_t i = 0; i < cell.size(); i++) { + indices.reserve(octree->points(cell).size()); + for (std::size_t i = 0; i < octree->points(cell).size(); i++) { if (shapeIndex[octree->index(cell, i)] == -1) { indices.push_back(octree->index(cell, i)); } @@ -1111,10 +1111,10 @@ class Efficient_RANSAC { indices); } else { - if (!cell.is_leaf()) { + if (!octree->is_leaf(cell)) { for (std::size_t i = 0; i < 8; i++) { - if (!cell[i].empty()) - stack.push(cell[i]); + if (octree->points(octree->child(cell, i)).size() != 0) + stack.push(octree->child(cell, i)); } } } @@ -1129,30 +1129,11 @@ class Efficient_RANSAC { const typename Octree::Node node_containing_point(const Octree *octree, const Point &p, std::size_t level) { // Find the node containing the point - typename Octree::Node cur = octree->root(); - while (!cur.is_null() && cur.depth() < level) { + typename Octree::Node n = octree->locate(p); + while (octree->depth(n) > level) + n = octree->parent(n); - // If cur is a leaf node, its child is null - if (cur.is_leaf()) - return typename Octree::Node(); - - // If that child is empty, return null - if (cur.empty()) - return typename Octree::Node(); - - // Determine the coordinate of the child - Point center = octree->barycenter(cur); - std::bitset<3> coordinate; - coordinate[0] = center.x() <= p.x(); - coordinate[1] = center.y() <= p.y(); - coordinate[2] = center.z() <= p.z(); - - // Otherwise, return the correct child of cur - cur = cur[coordinate.to_ulong()]; - - } - - return cur; + return n; } template @@ -1167,13 +1148,9 @@ class Efficient_RANSAC { const Cell cur = node_containing_point(octree, p, level); - // Stop if the node we need doesn't exist - if (cur.is_null()) - return false; - // Count point indices that map to -1 in the shape index std::size_t enough = 0; - for (auto j : cur) { + for (const auto j : octree->points(cur)) { if (shapeIndex[j] == -1) enough++; if (enough >= requiredSamples) @@ -1186,7 +1163,7 @@ class Efficient_RANSAC { do { std::size_t p = CGAL::get_default_random(). - uniform_int(0, cur.size() - 1); + uniform_int(0, octree->points(cur).size() - 1); std::size_t j = octree->index(cur, p); if (shapeIndex[j] == -1) diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h index da8c37139901..214a5f05e0d6 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h @@ -39,6 +39,8 @@ namespace CGAL { class InputPointMap, class InputNormalMap> struct Efficient_RANSAC_traits { + /// + typedef typename Gt GeomTraits; /// typedef typename Gt::FT FT; /// diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h index 8e3e261e9908..12de48cc60f4 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h @@ -56,8 +56,9 @@ class RANSAC_octree { typedef std::vector Input_range; typedef Random_index_access_property_map Indexed_point_map; - typedef CGAL::Octree::type, - Input_range, Indexed_point_map> Octree; + typedef Orthtree_traits_point_3::type, Input_range, Indexed_point_map> OTraits; + + typedef CGAL::Orthtree Octree; Traits m_traits; Input_range m_input_range; @@ -70,7 +71,8 @@ class RANSAC_octree { public: - typedef typename Octree::Node Node; + typedef typename Octree::Node_index Node; + typedef typename OTraits::Node_data Node_data; RANSAC_octree(const Traits &traits, Input_iterator begin, @@ -81,18 +83,26 @@ class RANSAC_octree { m_input_range(boost::counting_iterator(0), boost::counting_iterator(end - begin)), m_index_map(begin, point_map), - m_octree(m_input_range, m_index_map, 1.0), + m_octree(OTraits(m_input_range, m_index_map)), m_bBox (bbox_3(make_transform_iterator_from_property_map(begin, point_map), make_transform_iterator_from_property_map(end, point_map))), m_offset(offset) {} std::size_t index (Node node, std::size_t i) const { - return m_offset + *(node.begin() + i); + return m_offset + *(m_octree.data(node).begin() + i); + } + + std::size_t depth(const Node& node) const { + return m_octree.depth(node); + } + + bool is_leaf(const Node& node) const { + return m_octree.is_leaf(node); } std::size_t size() const { - return m_octree.root().size(); + return m_input_range.size(); } std::size_t maxLevel() const { @@ -127,17 +137,27 @@ class RANSAC_octree { return m_width; } + Node child(const Node& node, std::size_t i) const { + return m_octree.child(node, i); + } + + Node parent(const Node& node) const { + return m_octree.parent(node); + } + Node locate(const typename Traits::Point_3 &p) const { return m_octree.locate(p); } Node root() const { return m_octree.root(); } + Node_data points(const Node& n) const { return m_octree.data(n); } + typename Traits::Point_3 barycenter(const Node &node) const { return m_octree.barycenter(node); } - Bbox_3 boundingBox() const { + typename Traits::GeomTraits::Iso_cuboid_3 boundingBox() const { return m_octree.bbox(m_octree.root()); } }; From bf5bbcc0f1a559c8a7f0d2801ac30eabd02cf6f7 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 6 Sep 2023 14:23:47 +0200 Subject: [PATCH 217/520] Add `construct_point_d_object` to traits classes Not yet documented by the OrthtreeTraits concept. Will replace construct_point_d_from_array. --- Orthtree/examples/Orthtree/octree_grade.cpp | 2 ++ Orthtree/examples/Orthtree/orthtree_build.cpp | 3 ++ .../Orthtree/quadtree_build_manually.cpp | 1 + Orthtree/include/CGAL/Orthtree.h | 32 +++++++++++-------- .../include/CGAL/Orthtree_traits_2_base.h | 11 +++++++ .../include/CGAL/Orthtree_traits_3_base.h | 12 +++++++ .../include/CGAL/Orthtree_traits_d_base.h | 13 ++++++++ 7 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_grade.cpp b/Orthtree/examples/Orthtree/octree_grade.cpp index f10dcfd1f40f..fd74634d88bb 100644 --- a/Orthtree/examples/Orthtree/octree_grade.cpp +++ b/Orthtree/examples/Orthtree/octree_grade.cpp @@ -14,6 +14,8 @@ int main() { // Here, our point set is a vector Point_vector points; + using IPoint = CGAL::Simple_cartesian::Point_3; + // Add a few points to the vector, most of which are in one region points.emplace_back(1, 1, 1); points.emplace_back(2, 1, -11); diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 2dca94d50755..1a8a0d759c07 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -31,5 +31,8 @@ int main() Orthtree orthtree(points_dd); orthtree.refine(10, 5); + std::cout << orthtree.bbox(orthtree.root()).min()[0] << std::endl; + std::cout << orthtree << std::endl; + return EXIT_SUCCESS; } diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 8d989ceae865..9cfa40d7419f 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -58,6 +58,7 @@ int main() { quadtree.split(quadtree.node(3)); quadtree.split(quadtree.node(3, 0)); + std::cout << quadtree.bbox(quadtree.root()) << std::endl; std::cout << quadtree << std::endl; return EXIT_SUCCESS; } diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index c7dc72f33e82..998255cc2c01 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -169,7 +169,7 @@ class Orthtree { Point m_bbox_min; /* input bounding box min value */ - using Bbox_dimensions = decltype(std::declval().max() - std::declval().min()); + using Bbox_dimensions = std::array; std::vector m_side_per_depth; /* side length per node's depth */ Cartesian_ranges cartesian_range; /* a helper to easily iterator on coordinates of points */ @@ -219,9 +219,14 @@ class Orthtree { // init bbox with first values found auto bbox = m_traits.root_node_bbox_object()(); + // Determine dimensions of the root bbox + Bbox_dimensions size; + for (int i = 0; i < Dimension::value; ++i) + size[i] = bbox.max()[i] - bbox.min()[i]; + // save orthtree attributes m_bbox_min = bbox.min(); - m_side_per_depth.push_back(bbox.max() - bbox.min()); + m_side_per_depth.push_back(size); data(root()) = m_traits.root_node_contents_object()(); } @@ -471,20 +476,15 @@ class Orthtree { */ Bbox bbox(Node_index n) const { - // Determine the side length of this node + using Cartesian_coordinate = std::array; + Cartesian_coordinate min_corner, max_corner; Bbox_dimensions size = m_side_per_depth[depth(n)]; - - // Determine the location this node should be split - Array min_corner; - Array max_corner; for (int i = 0; i < Dimension::value; i++) { - min_corner[i] = m_bbox_min[i] + (global_coordinates(n)[i] * size[i]); + max_corner[i] = min_corner[i] + size[i]; } - - // Create the bbox - return {m_traits.construct_point_d_from_array_object()(min_corner), - m_traits.construct_point_d_from_array_object()(min_corner) + size}; + return {std::apply(m_traits.construct_point_d_object(), min_corner), + std::apply(m_traits.construct_point_d_object(), max_corner)}; } /// @} @@ -770,8 +770,12 @@ class Orthtree { // Check if we've reached a new max depth if (depth(n) + 1 == m_side_per_depth.size()) { - // Update the side length map - m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); + // Update the side length map with the dimensions of the children + Bbox_dimensions size = m_side_per_depth.back(); + Bbox_dimensions child_size; + for (int i = 0; i < Dimension::value; ++i) + child_size[i] = size[i] / FT(2); + m_side_per_depth.push_back(child_size); } // Find the point around which the node is split diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h index a4a51d7a502a..620089c00987 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_2_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_2_base.h @@ -88,6 +88,17 @@ struct Orthtree_traits_2_base { /// @} + /// \name Operations + /// @{ + + auto construct_point_d_object() const { + return [](const FT& x, const FT& y) -> Point_d { + return {x, y}; + }; + } + + /// @} + }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h index d6ecb38aac45..1cf7c8327a3c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -106,6 +106,18 @@ struct Orthtree_traits_3_base { /// @} + + /// \name Operations + /// @{ + + auto construct_point_d_object() const { + return [](const FT& x, const FT& y, const FT& z) -> Point_d { + return {x, y, z}; + }; + } + + /// @} + }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h index 671cca822031..279d37c03a5c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_d_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_d_base.h @@ -61,6 +61,19 @@ struct Orthtree_traits_d_base { /// @} + + /// \name Operations + /// @{ + + auto construct_point_d_object() const { + return [](auto... Args) -> Point_d { + std::initializer_list args_list{Args...}; + return Point_d{args_list.size(), args_list.begin(), args_list.end()}; + }; + } + + /// @} + }; } From 31ca1f1485c82c050e46903c11574f88b71dd374 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:30:31 +0200 Subject: [PATCH 218/520] package details --- Polygon_repair/doc/Polygon_repair/PackageDescription.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index 0f63f0844b7d..b9526d2d1320 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -24,9 +24,8 @@ \cgalPkgShortInfoBegin \cgalPkgSince{6.0} \cgalPkgDependsOn{\ref PkgPolygon2, \ref PkgTriangulation2} -\cgalPkgBib{cgal:x-x} +\cgalPkgBib{cgal:a-pr} \cgalPkgLicense{\ref licensesGPL "GPL"} -\cgalPkgDemo{DEMO 1,demo1.zip} \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd From 015e2baf965856218be613f942845412dcd091d6 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:31:04 +0200 Subject: [PATCH 219/520] repair is odd-even --- Polygon_repair/doc/Polygon_repair/PackageDescription.txt | 2 +- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index b9526d2d1320..0274c721cfa9 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -36,6 +36,6 @@ - `CGAL::Polygon_repair` \cgalCRPSection{Functions} -- `CGAL::Polygon_repair::repair()` +- `CGAL::Polygon_repair::repair_odd_even()` */ diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index addb665bc26f..aa74a09864f9 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -31,7 +31,7 @@ template class Polygon_repair; /// \ingroup PkgPolygonRepairFunctions -/// Repair a polygon without holes +/// Repair a polygon without holes using the odd-even heuristic template Multipolygon_with_holes_2 repair_odd_even(const Polygon_2& p) { CGAL::Polygon_repair::Polygon_repair pr; @@ -43,7 +43,7 @@ Multipolygon_with_holes_2 repair_odd_even(const Polygo } /// \ingroup PkgPolygonRepairFunctions -/// Repair a polygon with holes +/// Repair a polygon with holes using the odd-even heuristic template Multipolygon_with_holes_2 repair_odd_even(const Polygon_with_holes_2& p) { CGAL::Polygon_repair::Polygon_repair pr; @@ -55,7 +55,7 @@ Multipolygon_with_holes_2 repair_odd_even(const Polygo } /// \ingroup PkgPolygonRepairFunctions -/// Repair a multipolygon with holes +/// Repair a multipolygon with holes using the odd-even heuristic template Multipolygon_with_holes_2 repair_odd_even(const Multipolygon_with_holes_2& mp) { CGAL::Polygon_repair::Polygon_repair pr; From 12d3505942c756e90d5a447b2fbfa6e33ecbadc9 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:31:24 +0200 Subject: [PATCH 220/520] remove undocumented classes --- Polygon_repair/doc/Polygon_repair/PackageDescription.txt | 3 --- .../include/CGAL/Polygon_repair/Polygon_repair.h | 5 ----- .../Triangulation_with_odd_even_constraints_2.h | 7 ------- 3 files changed, 15 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index 0274c721cfa9..ce5b791ff5c3 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -32,9 +32,6 @@ \cgalClassifedRefPages -\cgalCRPSection{Classes} -- `CGAL::Polygon_repair` - \cgalCRPSection{Functions} - `CGAL::Polygon_repair::repair_odd_even()` diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index aa74a09864f9..91829fd271d5 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -271,11 +271,6 @@ bool is_valid(const Multipolygon_with_holes_2& multipo return true; } -/*! \ingroup PkgPolygonRepairRef - * - * The class `Polygon_repair` builds on a constrained - * triangulation to remove the parts of constraints that overlap an even number of times - */ template > class Polygon_repair { public: diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h index 9a693be3d94d..3d5dd2a77e47 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h @@ -17,13 +17,6 @@ namespace CGAL { -/*! \ingroup PkgPolygonRepairRef - * - * The class `Triangulation_with_odd_even_constraints_2` builds on a constrained - * triangulation to remove the parts of constraints that overlap an even number of times - * - * \tparam Triangulation_ must have support for constraints - */ template class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { public: From e06422aab5eaa723a4f14b6207f5b7becc5599d6 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:31:31 +0200 Subject: [PATCH 221/520] logo draft From 35d56ae8603cc3073acd6478f6be52f1c71fe265 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:31:45 +0200 Subject: [PATCH 222/520] todo for exact kernels --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 91829fd271d5..66ff45ba462b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -283,6 +283,7 @@ class Polygon_repair { CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; using Triangulation = Triangulation_with_odd_even_constraints_2; + // TODO: Edge_map and Vertex_map use std::set and set::map with exact kernels since Point_2 can't be hashed otherwise using Edge_map = typename std::conditional::value, std::unordered_set, boost::hash>>, From 6a203906c60d91cf2c95e93a93b3426fa4dba56b Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:32:22 +0200 Subject: [PATCH 223/520] svg logo for the future From d954161b79e95d006090faefad78559d7e347536 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:32:40 +0200 Subject: [PATCH 224/520] capitalisation --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 6547a97d482f..85a71fdc0767 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -123,7 +123,7 @@ faces. Then, all other faces are labeled. For the odd-even heuristic, the label applied alternates between polygon interior and hole every time that an edge is passed. -\subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the multipolygon +\subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the Multipolygon The algorithm reconstructs the multipolygon boundary by boundary, obtaining counter-clockwise cycles for outer boundaries and clockwise cycles for inner @@ -147,7 +147,7 @@ with holes has zero holes and extract these if needed. \section SectionPolygonRepair_Examples Examples -\subsection SubsectionPolygonRepair_Repair Repairing a (multi)polygon +\subsection SubsectionPolygonRepair_Repair Repairing a (Multi)polygon It's possible to repair a polygon, polygon with holes or multipolygon with holes using the odd-even rule by calling the `repair_odd_even` function as shown in the From c1e1e72e7fb7e7713e61f79ec62adefc720551c2 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 6 Sep 2023 22:33:36 +0200 Subject: [PATCH 225/520] narrower figure From 32c6d61f2763ef2ef74a45f29f2380eaf11c91f8 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 7 Sep 2023 11:44:14 +0200 Subject: [PATCH 226/520] Eliminate Array and Construct_point_d_from_array --- .../CollectionPartitioningOrthtreeTraits.h | 3 +++ .../doc/Orthtree/Concepts/OrthtreeTraits.h | 11 ++++---- .../Orthtree/quadtree_build_manually.cpp | 5 ---- Orthtree/include/CGAL/Orthtree.h | 9 ++----- .../include/CGAL/Orthtree_traits_2_base.h | 1 - .../include/CGAL/Orthtree_traits_3_base.h | 1 - .../include/CGAL/Orthtree_traits_d_base.h | 1 - .../include/CGAL/Orthtree_traits_face_graph.h | 17 +++--------- .../include/CGAL/Orthtree_traits_point_2.h | 27 +++---------------- .../include/CGAL/Orthtree_traits_point_3.h | 27 +++---------------- .../include/CGAL/Orthtree_traits_point_d.h | 27 +++---------------- 11 files changed, 22 insertions(+), 107 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 0d6f1f0222a0..e56e8578668e 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -49,6 +49,9 @@ class CollectionPartitioningOrthtreeTraits { /// \name Operations /// @{ + /*! + Function used to construct an object of type `Get_geometric_object_for_element`. + */ Get_geometric_object_for_element get_geometric_object_for_element_object() const; /// @} diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 09837bfbbd04..fc9e369659f3 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -27,7 +27,6 @@ class OrthtreeTraits %Cartesian coordinates of a point. */ typedef unspecified_type Cartesian_const_iterator_d; - typedef std::array Array; ///< Array used for easy point constructions. /*! @@ -38,9 +37,9 @@ class OrthtreeTraits typedef unspecified_type Adjacency; ///< Specify the adjacency directions /*! - Functor with an operator to construct a `Point_d` from an `Array` object. + Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. FT arguments. */ - typedef unspecified_type Construct_point_d_from_array; + typedef unspecified_type Construct_point_d; /// @} @@ -48,9 +47,9 @@ class OrthtreeTraits /// @{ /*! - Function used to construct an object of type `Construct_point_d_from_array`. + Function used to construct an object of type `Construct_point_d`. */ - Construct_point_d_from_array construct_point_d_from_array_object() const; + Construct_point_d construct_point_d() const; /*! * \brief Produces a bounding box which encloses the contents of the tree @@ -61,7 +60,7 @@ class OrthtreeTraits * * @return std::pair, where min and max represent cartesian corners which define a bounding box */ - std::pair root_node_bbox() const; + Bbox_d root_node_bbox() const; /*! * \brief Initializes the contained elements for the root node. diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 9cfa40d7419f..55be9e46ed6e 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -23,11 +23,6 @@ struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { Orthtree_traits_empty_2(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; - auto construct_point_d_from_array_object() const { - return [](const typename Self::Array& array) -> typename Self::Point_d { return {array[0], array[1]}; }; - } - using Construct_point_d_from_array = std::invoke_result_t; - auto root_node_bbox_object() const { return [&]() -> typename Self::Bbox_d { return m_bbox; }; } diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 998255cc2c01..9172e5d3e233 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -85,9 +85,6 @@ class Orthtree { typedef typename Traits::Node_data Node_data; // todo: Node_data_element will only exist for certain Traits types, so I don't know if it can be re-exported - /// \cond SKIP_IN_MANUAL - typedef typename Traits::Array Array; ///< Array type. - /// \endcond /// @} /// \name Public Types @@ -802,7 +799,7 @@ class Orthtree { Bbox_dimensions size = m_side_per_depth[depth(n)]; // Determine the location this node should be split - Array bary; + Bbox_dimensions bary; std::size_t i = 0; for (const FT& f: cartesian_range(m_bbox_min)) { bary[i] = FT(global_coordinates(n)[i]) * size[i] + size[i] / FT(2) + f; @@ -810,9 +807,7 @@ class Orthtree { } // Convert that location into a point - auto construct_point_d_from_array - = m_traits.construct_point_d_from_array_object(); - return construct_point_d_from_array(bary); + return std::apply(m_traits.construct_point_d_object(), bary); } static bool is_topology_equal(Node_index lhsNode, const Self& lhsTree, Node_index rhsNode, const Self& rhsTree) { diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h index 620089c00987..eb8393f48e63 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_2_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_2_base.h @@ -47,7 +47,6 @@ struct Orthtree_traits_2_base { using Bbox_d = typename K::Iso_rectangle_2; using Sphere_d = typename K::Circle_2; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; - using Array = std::array; // todo: This should have a more descriptive name /*! * \brief Two directions along each axis in Cartesian space, relative to a node. diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h index 1cf7c8327a3c..4ad143f1be42 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -48,7 +48,6 @@ struct Orthtree_traits_3_base { using Bbox_d = typename K::Iso_cuboid_3; using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; - using Array = std::array; // todo: This should have a more descriptive name /*! * \brief Two directions along each axis in Cartesian space, relative to a node. diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h index 279d37c03a5c..7851c3970870 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_d_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_d_base.h @@ -45,7 +45,6 @@ struct Orthtree_traits_d_base { using Bbox_d = typename K::Iso_box_d; using Sphere_d = typename K::Sphere_d; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; - using Array = std::array; /*! Adjacency type. diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 01b709efc351..084baed7b4d2 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -36,8 +36,6 @@ struct Orthtree_traits_face_graph using Dimension = typename Self::Dimension; using Bbox_d = typename Self::Bbox_d; using FT = typename Self::FT; - using Sphere_d = typename Self::Sphere_d; // SL: why? - using Array = typename Self::Array; // SL: why? using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; // SL: these could be considered as built-in data and if the typedefs are not present, the tree have none @@ -45,19 +43,10 @@ struct Orthtree_traits_face_graph using Geom_traits = typename Kernel_traits::type; - // SL: why? - struct Construct_point_d_from_array { - Point_d operator()(const Array& array) const { - return Point_d(array[0], array[1], array[2]); - } - }; - - Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } - auto root_node_bbox_object() const { return [&]() -> Bbox_d { - Array min = {0.0, 0}, max = {0.0, 0}; + std::array min = {0.0, 0}, max = {0.0, 0}; if (faces(m_pm).begin() != faces(m_pm).end()) { const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); min = {p.x(), p.y(), p.z()}; @@ -71,8 +60,8 @@ struct Orthtree_traits_face_graph } } - return {construct_point_d_from_array_object()(min), - construct_point_d_from_array_object()(max)}; + return {std::apply(Self::construct_point_d_object(), min), + std::apply(Self::construct_point_d_object(), max)}; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h index ff52558d1e83..165eca646b81 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_2.h @@ -57,21 +57,6 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - struct Construct_point_d_from_array - { - typename Self::Point_d operator() (const typename Self::Array& array) const - { - return typename Self::Point_d (array[0], array[1]); - } - }; -#endif - /// @} Orthtree_traits_point_2( @@ -82,16 +67,10 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { /// \name Operations /// @{ - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } - auto root_node_bbox_object() const { return [&]() -> typename Self::Bbox_d { - typename Self::Array bbox_min; - typename Self::Array bbox_max; + std::array bbox_min, bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found @@ -115,8 +94,8 @@ struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { } } - return {construct_point_d_from_array_object()(bbox_min), - construct_point_d_from_array_object()(bbox_max)}; + return {std::apply(Self::construct_point_d_object(), bbox_min), + std::apply(Self::construct_point_d_object(), bbox_max)}; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h index 9bd3a12cd04c..e3f52f67be8e 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_3.h @@ -57,21 +57,6 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - - struct Construct_point_d_from_array { - typename Self::Point_d operator()(const typename Self::Array& array) const { - return typename Self::Point_d(array[0], array[1], array[2]); - } - }; - -#endif - /// @} Orthtree_traits_point_3( @@ -82,16 +67,10 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { /// \name Operations /// @{ - /*! - Function used to construct an object of type `Construct_point_d_from_array`. - */ - Construct_point_d_from_array construct_point_d_from_array_object() const { return Construct_point_d_from_array(); } - auto root_node_bbox_object() const { return [&]() -> typename Self::Bbox_d { - typename Self::Array bbox_min; - typename Self::Array bbox_max; + std::array bbox_min, bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found @@ -115,8 +94,8 @@ struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { } } - return {construct_point_d_from_array_object()(bbox_min), - construct_point_d_from_array_object()(bbox_max)}; + return {std::apply(Self::construct_point_d_object(), bbox_min), + std::apply(Self::construct_point_d_object(), bbox_max)}; }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point_d.h index 48d3a905690c..807b0a2ab7ea 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point_d.h @@ -93,21 +93,6 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base; using Node_data_element = typename std::iterator_traits::value_type; -#ifdef DOXYGEN_RUNNING - /*! - Functor with an operator to construct a `Point_d` from an `Array` object. - */ - typedef unspecified_type Construct_point_d_from_array; -#else - - struct Construct_point_d_from_array { - typename Self::Point_d operator()(const typename Self::Array& array) const { - return typename Self::Point_d(array.size(), array.begin(), array.end()); - } - }; - -#endif - /// @} Orthtree_traits_point_d( @@ -118,16 +103,10 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base typename Self::Bbox_d { - typename Self::Array bbox_min; - typename Self::Array bbox_max; + std::array bbox_min, bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found @@ -151,8 +130,8 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base Date: Thu, 7 Sep 2023 13:25:35 +0200 Subject: [PATCH 227/520] Combine 2d, 3d, and d-d points traits into one template --- Orthtree/examples/Orthtree/orthtree_build.cpp | 2 +- Orthtree/include/CGAL/Octree.h | 2 +- .../include/CGAL/Orthtree_traits_2_base.h | 2 - .../include/CGAL/Orthtree_traits_3_base.h | 2 - ...aits_point_d.h => Orthtree_traits_point.h} | 58 +++++--- .../include/CGAL/Orthtree_traits_point_2.h | 133 ------------------ .../include/CGAL/Orthtree_traits_point_3.h | 132 ----------------- Orthtree/include/CGAL/Quadtree.h | 2 +- 8 files changed, 45 insertions(+), 288 deletions(-) rename Orthtree/include/CGAL/{Orthtree_traits_point_d.h => Orthtree_traits_point.h} (76%) delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_point_2.h delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_point_3.h diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 1a8a0d759c07..a721b2c6723c 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include // Type Declarations diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index b0b247f00a4d..9911668d67b5 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -15,7 +15,7 @@ #include #include -#include +#include namespace CGAL { diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h index eb8393f48e63..f04e1ac6611f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_2_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_2_base.h @@ -19,8 +19,6 @@ #include #include -#include - namespace CGAL { /*! diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h index 4ad143f1be42..008e4c8b9d49 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -19,8 +19,6 @@ #include #include -#include - namespace CGAL { /*! diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_d.h b/Orthtree/include/CGAL/Orthtree_traits_point.h similarity index 76% rename from Orthtree/include/CGAL/Orthtree_traits_point_d.h rename to Orthtree/include/CGAL/Orthtree_traits_point.h index 807b0a2ab7ea..b94acca60b9f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point_d.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -9,21 +9,22 @@ // // Author(s) : Jackson Campolattaro -#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_D_H -#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_D_H +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_H #include #include +#include +#include #include -#include -#include +#include +#include #include namespace CGAL { -// todo: should this go in its own header & namespace? template void reassign_points( Tree& tree, PointMap& point_map, @@ -58,36 +59,35 @@ void reassign_points( reassign_points(tree, point_map, n, center, {split_point, points.end()}, coord_right, dimension + 1); } - /*! \ingroup PkgOrthtreeTraits - The class `Orthtree_traits_point_d` can be used as a template parameter of + The class `Orthtree_traits_point` can be used as a template parameter of the `Orthtree` class. \tparam GeomTraits model of `Kernel`. - \tparam DimensionTag specialization of `CGAL::Dimension_tag`. - \tparam PointSet must be a model of range whose value type is the key type of `Point_map` + \tparam PointSet must be a model of range whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` \cgalModels `OrthtreeTraits` - \sa `CGAL::Orthtree` + \sa `CGAL::Octree` \sa `CGAL::Orthtree_traits_2` \sa `CGAL::Orthtree_traits_3` + \sa `CGAL::Orthtree_traits_d` */ template < typename GeomTraits, - typename DimensionTag, typename PointSet, - typename PointMap = Identity_property_map + typename PointMap, + typename OrthtreeTraitsDimensionBase > -struct Orthtree_traits_point_d : public Orthtree_traits_d_base { +struct Orthtree_traits_point : public OrthtreeTraitsDimensionBase { public: /// \name Types /// @{ - using Self = Orthtree_traits_point_d; + using Self = Orthtree_traits_point; using Tree = Orthtree; using Node_data = boost::iterator_range; @@ -95,7 +95,7 @@ struct Orthtree_traits_point_d : public Orthtree_traits_d_base +> +using Orthtree_traits_point_2 = + Orthtree_traits_point>; + +template < + typename GeomTraits, + typename PointSet, + typename PointMap = Identity_property_map +> +using Orthtree_traits_point_3 = + Orthtree_traits_point>; + +template < + typename GeomTraits, + typename DimensionTag, + typename PointSet, + typename PointMap = Identity_property_map +> +using Orthtree_traits_point_d = + Orthtree_traits_point>; + } -#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_D_H + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_2.h b/Orthtree/include/CGAL/Orthtree_traits_point_2.h deleted file mode 100644 index 165eca646b81..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_point_2.h +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro - -#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_2_H -#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_2_H - -#include - -#include -#include -#include -#include - -#include -#include - -namespace CGAL { - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_point_2` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - \tparam PointSet must be a model of range whose value type is the key type of `PointMap` - \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` - - \cgalModels `OrthtreeTraits` - \sa `CGAL::Octree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_d` -*/ -template < - typename GeomTraits, - typename PointSet, - typename PointMap = Identity_property_map -> -struct Orthtree_traits_point_2 : public Orthtree_traits_2_base { -public: - - /// \name Types - /// @{ - - using Self = Orthtree_traits_point_2; - using Tree = Orthtree; - - // todo: looking for better names - using Node_data = boost::iterator_range; - using Node_data_element = typename std::iterator_traits::value_type; - - /// @} - - Orthtree_traits_point_2( - PointSet& point_set, - PointMap point_map = PointMap() - ) : m_point_set(point_set), m_point_map(point_map) {} - - /// \name Operations - /// @{ - - auto root_node_bbox_object() const { - return [&]() -> typename Self::Bbox_d { - - std::array bbox_min, bbox_max; - Orthtrees::internal::Cartesian_ranges cartesian_range; - - // init bbox with first values found - { - const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = x; - bbox_max[i] = x; - ++i; - } - } - // Expand bbox to contain all points - for (const auto& p: m_point_set) { - const typename Self::Point_d& point = get(m_point_map, p); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++i; - } - } - - return {std::apply(Self::construct_point_d_object(), bbox_min), - std::apply(Self::construct_point_d_object(), bbox_max)}; - }; - } - - auto root_node_contents_object() const { - return [&]() -> typename Self::Node_data { - return {m_point_set.begin(), m_point_set.end()}; - }; - } - - auto distribute_node_contents_object() const { - return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) { - CGAL_precondition(!tree.is_leaf(n)); - reassign_points(tree, m_point_map, n, center, tree.data(n)); - }; - } - - auto get_geometric_object_for_element_object() const { - return [&](const Node_data_element& index) -> typename Self::Point_d { - return get(m_point_map, index); - }; - } - - /// @} - -private: - - PointSet& m_point_set; - PointMap m_point_map; - -}; - -} - - -#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_2_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_point_3.h b/Orthtree/include/CGAL/Orthtree_traits_point_3.h deleted file mode 100644 index e3f52f67be8e..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_point_3.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro - -#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H -#define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H - -#include - -#include -#include -#include -#include - -#include -#include - -namespace CGAL { - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_point_3` can be used as a template parameter of - the `Orthtree` class. - - \tparam GeomTraits model of `Kernel`. - \tparam PointSet must be a model of range whose value type is the key type of `PointMap` - \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` - - \cgalModels `OrthtreeTraits` - \sa `CGAL::Octree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_d` -*/ -template < - typename GeomTraits, - typename PointSet, - typename PointMap = Identity_property_map -> -struct Orthtree_traits_point_3 : public Orthtree_traits_3_base { -public: - - /// \name Types - /// @{ - - using Self = Orthtree_traits_point_3; - using Tree = Orthtree; - - // todo: looking for better names - using Node_data = boost::iterator_range; - using Node_data_element = typename std::iterator_traits::value_type; - - /// @} - - Orthtree_traits_point_3( - PointSet& point_set, - PointMap point_map = PointMap() - ) : m_point_set(point_set), m_point_map(point_map) {} - - /// \name Operations - /// @{ - - auto root_node_bbox_object() const { - return [&]() -> typename Self::Bbox_d { - - std::array bbox_min, bbox_max; - Orthtrees::internal::Cartesian_ranges cartesian_range; - - // init bbox with first values found - { - const typename Self::Point_d& point = get(m_point_map, *(m_point_set.begin())); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = x; - bbox_max[i] = x; - ++i; - } - } - // Expand bbox to contain all points - for (const auto& p: m_point_set) { - const typename Self::Point_d& point = get(m_point_map, p); - std::size_t i = 0; - for (const typename Self::FT& x: cartesian_range(point)) { - bbox_min[i] = (std::min)(x, bbox_min[i]); - bbox_max[i] = (std::max)(x, bbox_max[i]); - ++i; - } - } - - return {std::apply(Self::construct_point_d_object(), bbox_min), - std::apply(Self::construct_point_d_object(), bbox_max)}; - }; - } - - auto root_node_contents_object() const { - return [&]() -> typename Self::Node_data { - return {m_point_set.begin(), m_point_set.end()}; - }; - } - - auto distribute_node_contents_object() const { - return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) { - CGAL_precondition(!tree.is_leaf(n)); - reassign_points(tree, m_point_map, n, center, tree.data(n)); - }; - } - - auto get_geometric_object_for_element_object() const { - return [&](const Node_data_element& index) -> typename Self::Point_d { - return get(m_point_map, index); - }; - } - - /// @} - -private: - - PointSet& m_point_set; - PointMap m_point_map; - -}; - -} - -#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 3c8649d95c93..52184807e3b7 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -15,7 +15,7 @@ #include #include -#include +#include namespace CGAL { From 6c9bc49d9f2f3fe3997f01a1e1cec45a45068975 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Thu, 7 Sep 2023 14:01:36 +0200 Subject: [PATCH 228/520] triangulation should be passed by reference --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 66ff45ba462b..aa49e1857b05 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -479,7 +479,7 @@ class Polygon_repair { // Label a region of adjacent triangles without passing through constraints // adjacent triangles that involve passing through constraints are added to to_check template - static void label_region(T tt, typename T::Face_handle face, int label, + static void label_region(T& tt, typename T::Face_handle face, int label, std::list& to_check, std::list& to_check_added_by) { // std::cout << "Labelling region with " << label << std::endl; From ea59877c195469877892f6d7e3b824f5217125f5 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 7 Sep 2023 17:19:04 +0200 Subject: [PATCH 229/520] Automatically determine the correct typedefs for an ambient dimension using template specializations & SFINAE --- Orthtree/examples/Orthtree/orthtree_build.cpp | 2 +- .../Orthtree/quadtree_build_manually.cpp | 12 +- Orthtree/include/CGAL/Octree.h | 2 +- .../CGAL/Orthtree_traits_base_for_dimension.h | 196 ++++++++++++++++++ .../include/CGAL/Orthtree_traits_face_graph.h | 16 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 40 +--- Orthtree/include/CGAL/Quadtree.h | 2 +- 7 files changed, 225 insertions(+), 45 deletions(-) create mode 100644 Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index a721b2c6723c..3af483eb8fc9 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -12,7 +12,7 @@ typedef CGAL::Epick_d Kernel; typedef Kernel::Point_d Point_d; typedef std::vector Point_vector; -typedef CGAL::Orthtree_traits_point_d Traits; +typedef CGAL::Orthtree_traits_point Traits; typedef CGAL::Orthtree Orthtree; int main() diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 55be9e46ed6e..f0fe27d1f205 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using Kernel = CGAL::Simple_cartesian; @@ -13,15 +13,15 @@ namespace CGAL { struct empty_type { }; -template -struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { +template +struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension { - using Self = Orthtree_traits_empty_2; + using Self = Orthtree_traits_empty; using Tree = Orthtree; using Node_data = std::array; - Orthtree_traits_empty_2(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; + Orthtree_traits_empty(typename Self::Bbox_d bbox) : m_bbox(bbox) {}; auto root_node_bbox_object() const { return [&]() -> typename Self::Bbox_d { return m_bbox; }; @@ -40,7 +40,7 @@ struct Orthtree_traits_empty_2 : public Orthtree_traits_2_base { }; } -using EmptyQuadtree = CGAL::Orthtree>; +using EmptyQuadtree = CGAL::Orthtree>>; int main() { diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 9911668d67b5..324d655e1d91 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -43,7 +43,7 @@ template < #ifdef DOXYGEN_RUNNING class Octree; #else -using Octree = Orthtree>; +using Octree = Orthtree>>; #endif } // namespace CGAL diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h new file mode 100644 index 000000000000..fb7d4f6b2f95 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -0,0 +1,196 @@ +// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro + +#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H +#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H + +#include + +#include +#include +#include +#include + +namespace CGAL { + +template +struct Orthtree_traits_base_for_dimension; + +template +struct Orthtree_traits_base_for_dimension { + /// \name Types + /// @{ + using Dimension = DimensionTag; + using FT = typename K::FT; + using Point_d = typename K::Point_d; + using Bbox_d = typename K::Iso_box_d; + using Sphere_d = typename K::Sphere_d; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; + /*! + Adjacency type. + + \note This type is used to identify adjacency directions with + easily understandable keywords (left, right, up, etc.) and is thus + mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In + higher dimensions, such keywords do not exist and this type is + simply an integer. Conversions from this integer to bitsets still + work but do not provide any easier API for adjacency selection. + */ + using Adjacency = int; + /// @} + + /// \name Operations + /// @{ + auto construct_point_d_object() const { + return [](auto... Args) -> Point_d { + std::initializer_list args_list{Args...}; + return Point_d{args_list.size(), args_list.begin(), args_list.end()}; + }; + } + /// @} +}; + +template +struct Orthtree_traits_base_for_dimension> { + /// \name Types + /// @{ + using Dimension = Dimension_tag<2>; + using FT = typename K::FT; + using Point_d = typename K::Point_2; + using Bbox_d = typename K::Iso_rectangle_2; + using Sphere_d = typename K::Circle_2; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; + /*! + * \brief Two directions along each axis in Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 2-bit integers. + * + * The first bit indicates the axis (0 = x, 1 = y), + * the second bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram may be a useful reference: + * + * 3 * + * | + * | y+ + * | * + * 0 *------+------* 1 | + * | | + * | +-----* x+ + * | + * * 2 + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 00 | 0 | LEFT | + * | `+x` | 01 | 1 | RIGHT | + * | `-y` | 10 | 2 | DOWN | + * | `+y` | 11 | 3 | UP | + */ + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP + }; + /// @} + + /// \name Operations + /// @{ + auto construct_point_d_object() const { + return [](const FT& x, const FT& y) -> Point_d { + return {x, y}; + }; + } + /// @} +}; + +template +struct Orthtree_traits_base_for_dimension> { + /// \name Types + /// @{ + using GeomTraits = K; + using Dimension = Dimension_tag<3>; + using FT = typename K::FT; + using Point_d = typename K::Point_3; + using Bbox_d = typename K::Iso_cuboid_3; + using Sphere_d = typename K::Sphere_3; + using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; + /*! + * \brief Two directions along each axis in Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 3-bit integers, + * though the numbers 6 and 7 are not used because there are only 6 different directions. + * + * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), + * the third bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram may be a useful reference: + * + * 3 * + * | * 5 + * | / y+ + * |/ * z+ + * 0 *------+------* 1 | * + * /| |/ + * / | +-----* x+ + * 4 * | + * * 2 + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 000 | 0 | LEFT | + * | `+x` | 001 | 1 | RIGHT | + * | `-y` | 010 | 2 | DOWN | + * | `+y` | 011 | 3 | UP | + * | `-z` | 100 | 4 | BACK | + * | `+z` | 101 | 5 | FRONT | + */ + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP, + BACK, + FRONT + }; + /// \cond SKIP_IN_MANUAL + enum Child { + LEFT_BOTTOM_BACK, + RIGHT_BOTTOM_BACK, + LEFT_TOP_BACK, + RIGHT_TOP_BACK, + LEFT_BOTTOM_FRONT, + RIGHT_BOTTOM_FRONT, + LEFT_TOP_FRONT, + RIGHT_TOP_FRONT + }; + /// \endcond + /// @} + + /// \name Operations + /// @{ + auto construct_point_d_object() const { + return [](const FT& x, const FT& y, const FT& z) -> Point_d { + return {x, y, z}; + }; + } + /// @} +}; + + +} + +#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 084baed7b4d2..144564ebf79a 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -15,7 +15,7 @@ #include -#include +#include #include #include @@ -23,8 +23,15 @@ namespace CGAL { template -struct Orthtree_traits_face_graph - : public Orthtree_traits_3_base::value_type>::type> { +struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< + typename Kernel_traits::value_type>::type, + Dimension_tag<3> + // todo: it should be possible to determine the ambient dimension automatically, but this isn't working +// Ambient_dimension< +// typename boost::property_traits::value_type, +// typename Kernel_traits::value_type>::type +// > +> { Orthtree_traits_face_graph(const PolygonMesh& pm, VPM vpm) : m_pm(pm), m_vpm(vpm) {} @@ -110,8 +117,9 @@ struct Orthtree_traits_face_graph if (tree.data(ni).empty()) return false; Bbox_d bb = tree.bbox(ni); - //TODO: we should get better version to get guarantees + // TODO: we should get better version to get guarantees // TODO: as long as the bbox is cubic you can use depth and initial size to conclude. + // todo (jackson): bbox is _not_ guaranteed to be cubic now, this may break in some very niche cases for (int i = 0; i < 3; ++i) if ((bb.max()[i] - bb.min()[i]) < 2 * m_min_extent) return false; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index b94acca60b9f..dd4803e07bf0 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -19,9 +19,7 @@ #include #include -#include -#include -#include +#include namespace CGAL { @@ -78,16 +76,19 @@ void reassign_points( template < typename GeomTraits, typename PointSet, - typename PointMap, - typename OrthtreeTraitsDimensionBase + typename PointMap = Identity_property_map::value_type>, + typename DimensionTag = Ambient_dimension< + typename std::iterator_traits::value_type, + GeomTraits + > > -struct Orthtree_traits_point : public OrthtreeTraitsDimensionBase { +struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension { public: /// \name Types /// @{ - using Self = Orthtree_traits_point; + using Self = Orthtree_traits_point; using Tree = Orthtree; using Node_data = boost::iterator_range; @@ -163,31 +164,6 @@ struct Orthtree_traits_point : public OrthtreeTraitsDimensionBase { }; -template < - typename GeomTraits, - typename PointSet, - typename PointMap = Identity_property_map -> -using Orthtree_traits_point_2 = - Orthtree_traits_point>; - -template < - typename GeomTraits, - typename PointSet, - typename PointMap = Identity_property_map -> -using Orthtree_traits_point_3 = - Orthtree_traits_point>; - -template < - typename GeomTraits, - typename DimensionTag, - typename PointSet, - typename PointMap = Identity_property_map -> -using Orthtree_traits_point_d = - Orthtree_traits_point>; - } diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 52184807e3b7..5ed4dc1baab8 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -41,7 +41,7 @@ template >; +using Quadtree = Orthtree>>; #endif } // namespace CGAL From 3e20800b7fa03ef9bebe9d954c2c8a75d0396a32 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 8 Sep 2023 12:13:27 +0200 Subject: [PATCH 230/520] Update documentation for Traits concepts --- .../CollectionPartitioningOrthtreeTraits.h | 9 +- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 101 ++++++++++-------- .../Orthtree/quadtree_build_manually.cpp | 4 +- Orthtree/include/CGAL/Orthtree.h | 4 +- .../include/CGAL/Orthtree_traits_face_graph.h | 6 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 4 +- 6 files changed, 69 insertions(+), 59 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index e56e8578668e..19b21aa71a96 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -12,10 +12,7 @@ \cgalRefines{OrthtreeTraits} - todo: update list of models - \cgalHasModel `CGAL::Orthtree_traits_2` - \cgalHasModel `CGAL::Orthtree_traits_3` - \cgalHasModel `CGAL::Orthtree_traits_d` + \cgalHasModel `CGAL::Orthtree_traits_point` */ class CollectionPartitioningOrthtreeTraits { public: @@ -50,8 +47,8 @@ class CollectionPartitioningOrthtreeTraits { /// @{ /*! - Function used to construct an object of type `Get_geometric_object_for_element`. - */ + * Function used to construct an object of type `Get_geometric_object_for_element`. + */ Get_geometric_object_for_element get_geometric_object_for_element_object() const; /// @} diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index fc9e369659f3..4360ede24882 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -5,10 +5,8 @@ The concept `OrthtreeTraits` defines the requirements for the template parameter of the `CGAL::Orthtree` class. - todo: update list of models - \cgalHasModel `CGAL::Orthtree_traits_2` - \cgalHasModel `CGAL::Orthtree_traits_3` - \cgalHasModel `CGAL::Orthtree_traits_d` + \cgalHasModel `CGAL::Orthtree_traits_point` + \cgalHasModel `CGAL::Orthtree_traits_face_graph` */ class OrthtreeTraits { @@ -25,6 +23,8 @@ class OrthtreeTraits /*! A random access iterator type to enumerate the %Cartesian coordinates of a point. + + todo: This isn't used, should it be? */ typedef unspecified_type Cartesian_const_iterator_d; @@ -34,11 +34,54 @@ class OrthtreeTraits */ typedef unspecified_type Node_data; + /*! + * \brief Number-type which can take on values indicating adjacency directions. + * + * Must be able to take on values ranging from 0 to the number of faces of the (hyper)cube type, equivalent to 2 * D. + */ typedef unspecified_type Adjacency; ///< Specify the adjacency directions /*! - Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. FT arguments. - */ + * \brief Functor with an operator to create the bounding box of the root node. + * + * The bounding box must enclose all elements contained by the tree. + * It may be tight-fitting. The orthtree constructor produces a bounding cube surrounding this region. + * For traits which assign no data to each node, this can be defined to return a fixed region. + */ + typedef unspecified_type Construct_root_node_bbox; + + /*! + * \brief Functor which initializes the contained elements for the root node. + * + * Each node of a tree has an associated `Node_data` value. + * For most nodes, this is set by `Distribute_node_contents`, but that is not possible for the root node. + * Instead, this functor initializes the `Node_data` of the root node. + * It should take no arguments, and return an instance of `Node_data`. + * + * Typically, the `Node_data` of the root node contains all the elements in the tree. + * For a tree in which each node contains an `std::span` this function would return the span containing all items. + * + */ + typedef unspecified_type Construct_root_node_contents; + + /*! + * \brief Functor which distributes a node's contents to its children. + * + * The functor takes a node index, a tree reference, and a Point_d which is the center of the node. + * It can use tree.children(node_index) to access the children of the node, and tree.data(node_index) + * to access the contents of the node and each of its children. + * It should distribute the contents of the node to each of its children. + * For a tree in which each node contains a span, this may mean rearranging the contents of the original node + * and producing spans containing a subset of its contents for each of its children. + */ + typedef unspecified_type Distribute_node_contents; + + /*! + * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. FT arguments. + * + * For trees which use a different kernel for the Bbox type, + * the return type of this functor must match the kernel used by the Bbox and not that of the contents. + */ typedef unspecified_type Construct_point_d; /// @} @@ -47,52 +90,24 @@ class OrthtreeTraits /// @{ /*! - Function used to construct an object of type `Construct_point_d`. - */ - Construct_point_d construct_point_d() const; + * Function used to construct an object of type `Construct_root_node_bbox`. + */ + Construct_root_node_bbox construct_root_node_bbox_object() const; /*! - * \brief Produces a bounding box which encloses the contents of the tree - * - * The bounding box must enclose all elements contained by the tree. - * It may be tight-fitting, the orthtree constructor produces a bounding cube surrounding this region. - * For traits which assign no data to each node, this can be defined to return a fixed region. - * - * @return std::pair, where min and max represent cartesian corners which define a bounding box + * Function used to construct an object of type `Construct_root_node_contents`. */ - Bbox_d root_node_bbox() const; + Construct_root_node_contents construct_root_node_contents_object() const; /*! - * \brief Initializes the contained elements for the root node. - * - * Typically produces a `Node_data` which contains all the elements in the tree. - * e.g. For a tree where each node contains a set of points, - * root_node_contents() will produce the list of all points. - * - * @return The `Node_data` instance to be contained by the root node + * Function used to construct an object of type `Distribute_node_contents`. */ - Node_data root_node_contents() const; + Distribute_node_contents distribute_node_contents_object() const; /*! - * \brief Distributes the `Node_data` contents of a node to its immediate children. - * - * Invoked after a node is split. - * Adds the contents of the node n to each of its children. - * May rearrange or modify n's `Node_data`, but generally expected not to reset n. - * After distributing n's contents, n should still have an list of elements it encloses. - * Each of n's children should have an accurate list of the subset of elements within n they enclose. - * - * For an empty tree, this can be a null-op. - * - * @tparam Node_index The index type used by an orthtree implementation - * @tparam Tree An Orthree implementation - * - * @param n The index of the node who's contents must be distributed. - * @param tree The Orthtree which n belongs to - * @param center The coordinate center of the node n, which its contents should be split around. + * Function used to construct an object of type `Construct_point_d`. */ - template - void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center); + Construct_point_d construct_point_d_object() const; /// @} }; diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index f0fe27d1f205..73808cc603b1 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -23,11 +23,11 @@ struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension typename Self::Bbox_d { return m_bbox; }; } - auto root_node_contents_object() const { return [&]() -> Node_data { return {}; }; } + auto construct_root_node_contents_object() const { return [&]() -> Node_data { return {}; }; } auto distribute_node_contents_object() { return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) -> void {}; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9172e5d3e233..6bfabf30885d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -214,7 +214,7 @@ class Orthtree { m_node_properties.emplace(); // init bbox with first values found - auto bbox = m_traits.root_node_bbox_object()(); + auto bbox = m_traits.construct_root_node_bbox_object()(); // Determine dimensions of the root bbox Bbox_dimensions size; @@ -224,7 +224,7 @@ class Orthtree { // save orthtree attributes m_bbox_min = bbox.min(); m_side_per_depth.push_back(size); - data(root()) = m_traits.root_node_contents_object()(); + data(root()) = m_traits.construct_root_node_contents_object()(); } /// @} diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 144564ebf79a..547f02846a85 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -50,7 +50,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< using Geom_traits = typename Kernel_traits::type; - auto root_node_bbox_object() const { + auto construct_root_node_bbox_object() const { return [&]() -> Bbox_d { std::array min = {0.0, 0}, max = {0.0, 0}; @@ -72,8 +72,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< }; } - // SL: not clear to me what it should do from the doc - auto root_node_contents_object() { + auto construct_root_node_contents_object() { return [&]() -> Node_data { return {faces(m_pm).begin(), faces(m_pm).end()}; }; @@ -99,7 +98,6 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< }; } - //SL: I find convenient to put it here class Split_predicate_node_min_extent { FT m_min_extent; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index dd4803e07bf0..001bd5fc1ab2 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -104,7 +104,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension typename Self::Bbox_d { std::array bbox_min, bbox_max; @@ -136,7 +136,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension typename Self::Node_data { return {m_point_set.begin(), m_point_set.end()}; }; From ed32969908454434385f0e6132b3a132771c7942 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 8 Sep 2023 12:42:20 +0200 Subject: [PATCH 231/520] Prefer `using` over `typedef` universally, for consistency --- Orthtree/benchmark/Orthtree/construction.cpp | 18 ++++---- .../benchmark/Orthtree/nearest_neighbor.cpp | 20 ++++----- Orthtree/benchmark/Orthtree/util.h | 4 +- .../Orthtree/octree_build_from_point_set.cpp | 11 +++-- .../octree_build_from_point_vector.cpp | 9 ++-- .../octree_build_with_custom_split.cpp | 13 +++--- .../Orthtree/octree_find_nearest_neighbor.cpp | 11 +++-- Orthtree/examples/Orthtree/octree_grade.cpp | 8 ++-- .../Orthtree/octree_traversal_custom.cpp | 11 +++-- .../Orthtree/octree_traversal_manual.cpp | 11 +++-- .../Orthtree/octree_traversal_preorder.cpp | 13 +++--- Orthtree/examples/Orthtree/orthtree_build.cpp | 13 +++--- .../quadtree_build_from_point_vector.cpp | 9 ++-- Orthtree/include/CGAL/Orthtree.h | 42 +++++++++---------- .../include/CGAL/Orthtree/Cartesian_ranges.h | 4 +- .../CGAL/Orthtree/Traversal_iterator.h | 4 +- Orthtree/test/Orthtree/test_node_adjacent.cpp | 11 +++-- Orthtree/test/Orthtree/test_node_index.cpp | 9 ++-- Orthtree/test/Orthtree/test_octree_bbox.cpp | 11 +++-- .../test_octree_copy_move_constructors.cpp | 10 ++--- .../test_octree_custom_properties.cpp | 10 ++--- .../test/Orthtree/test_octree_equality.cpp | 9 ++-- Orthtree/test/Orthtree/test_octree_grade.cpp | 12 +++--- .../Orthtree/test_octree_intersecting.cpp | 8 ++-- .../test/Orthtree/test_octree_kernels.cpp | 6 +-- Orthtree/test/Orthtree/test_octree_locate.cpp | 11 +++-- .../Orthtree/test_octree_nearest_neighbor.cpp | 18 ++++---- Orthtree/test/Orthtree/test_octree_refine.cpp | 8 ++-- .../test/Orthtree/test_octree_traverse.cpp | 12 +++--- 29 files changed, 158 insertions(+), 178 deletions(-) diff --git a/Orthtree/benchmark/Orthtree/construction.cpp b/Orthtree/benchmark/Orthtree/construction.cpp index 36b8800f28c4..5d55f39c1ee8 100644 --- a/Orthtree/benchmark/Orthtree/construction.cpp +++ b/Orthtree/benchmark/Orthtree/construction.cpp @@ -12,16 +12,14 @@ #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; - -typedef CGAL::Search_traits_3 Kd_tree_traits; -typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; -typedef Kd_tree_search::Tree Kdtree; +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; +using Kd_tree_traits = CGAL::Search_traits_3; +using Kd_tree_search = CGAL::Orthogonal_k_neighbor_search; +using Kdtree = Kd_tree_search::Tree; using std::chrono::high_resolution_clock; using std::chrono::duration_cast; diff --git a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp index 69ca01c89580..1ab0f51f7c53 100644 --- a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp +++ b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp @@ -12,21 +12,19 @@ #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; - -typedef CGAL::Search_traits_3 Kd_tree_traits; -typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; -typedef Kd_tree_search::Tree Kdtree; - using std::chrono::high_resolution_clock; using std::chrono::duration_cast; using std::chrono::microseconds; +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; +using Kd_tree_traits = CGAL::Search_traits_3; +using Kd_tree_search = CGAL::Orthogonal_k_neighbor_search; +using Kdtree = Kd_tree_search::Tree; + int main(int argc, char **argv) { int num_runs = 100; diff --git a/Orthtree/benchmark/Orthtree/util.h b/Orthtree/benchmark/Orthtree/util.h index de65aa89e812..f619317fa5f5 100644 --- a/Orthtree/benchmark/Orthtree/util.h +++ b/Orthtree/benchmark/Orthtree/util.h @@ -10,8 +10,8 @@ template CGAL::Point_set_3 generate(size_t num_points = 1) { - typedef typename Kernel::Point_3 Point; - typedef CGAL::Point_set_3 Point_set; + using Point = typename Kernel::Point_3; + using Point_set = CGAL::Point_set_3; // Create an empty point set Point_set points; diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index e7f930fd9766..c8382b8d5ff0 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -7,12 +7,11 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; int main(int argc, char **argv) { diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp index e12a895cfc8b..16f489109e8c 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp @@ -4,11 +4,10 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef std::vector Point_vector; // todo: this was changed to std::list at some point; why? - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_vector = std::vector; // todo: this was changed to std::list at some point; why? +using Octree = CGAL::Octree; int main() { diff --git a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp index 1a84c914d394..0d25907cbeeb 100644 --- a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp +++ b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp @@ -7,13 +7,12 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; // Split Predicate // The predicate is a functor which returns a boolean value, whether a node needs to be split or not diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index 1200d88c134f..0d457c5e24d0 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -10,12 +10,11 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; int main(int argc, char** argv) { diff --git a/Orthtree/examples/Orthtree/octree_grade.cpp b/Orthtree/examples/Orthtree/octree_grade.cpp index fd74634d88bb..ae41079ea87e 100644 --- a/Orthtree/examples/Orthtree/octree_grade.cpp +++ b/Orthtree/examples/Orthtree/octree_grade.cpp @@ -4,10 +4,10 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef std::vector Point_vector; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_vector = std::vector; +using Octree = CGAL::Octree; int main() { diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index 5e784559e7c3..bbe4a08b76e5 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -8,12 +8,11 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; template struct First_branch_traversal { diff --git a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp index 94bdec8879bc..974d52f6077d 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp @@ -7,12 +7,11 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; int main(int argc, char** argv) { diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index fbda2f8db8ff..5ae7cc0a657e 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -7,13 +7,12 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef Point_set::Point_map Point_map; - -typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Point_map = Point_set::Point_map; +using Octree = CGAL::Octree; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; int main(int argc, char **argv) { diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 3af483eb8fc9..114e709ad12b 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -7,13 +7,12 @@ #include // Type Declarations -typedef CGAL::Dimension_tag<4> Dimension; -typedef CGAL::Epick_d Kernel; -typedef Kernel::Point_d Point_d; -typedef std::vector Point_vector; - -typedef CGAL::Orthtree_traits_point Traits; -typedef CGAL::Orthtree Orthtree; +using Dimension = CGAL::Dimension_tag<4>; +using Kernel = CGAL::Epick_d; +using Point_d = Kernel::Point_d; +using Point_vector = std::vector; +using Traits = CGAL::Orthtree_traits_point; +using Orthtree = CGAL::Orthtree; int main() { diff --git a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp index db47c50b853d..45a9b2a80f50 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp @@ -5,11 +5,10 @@ #include // Type Declarations -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_2 Point_2; -typedef std::vector Point_vector; - -typedef CGAL::Quadtree Quadtree; +using Kernel = CGAL::Simple_cartesian; +using Point_2 = Kernel::Point_2; +using Point_vector = std::vector; +using Quadtree = CGAL::Quadtree; int main() { diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 6bfabf30885d..55e68971b33d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -70,19 +70,19 @@ class Orthtree { /// \name Template Types /// @{ - typedef Traits_ Traits; ///< Geometry traits + using Traits = Traits_; ///< Geometry traits /// @} /// \name Traits Types /// @{ - typedef typename Traits::Dimension Dimension; ///< Dimension of the tree - typedef typename Traits::FT FT; ///< Number type. - typedef typename Traits::Point_d Point; ///< Point type. - typedef typename Traits::Bbox_d Bbox; ///< Bounding box type. - typedef typename Traits::Sphere_d Sphere; ///< Sphere type. - typedef typename Traits::Adjacency Adjacency; ///< Adjacency type. - - typedef typename Traits::Node_data Node_data; + using Dimension = typename Traits::Dimension; ///< Dimension of the tree + using FT = typename Traits::FT; ///< Number type. + using Point = typename Traits::Point_d; ///< Point type. + using Bbox = typename Traits::Bbox_d; ///< Bounding box type. + using Sphere = typename Traits::Sphere_d; ///< Sphere type. + using Adjacency = typename Traits::Adjacency; ///< Adjacency type. + + using Node_data = typename Traits::Node_data; // todo: Node_data_element will only exist for certain Traits types, so I don't know if it can be re-exported /// @} @@ -93,25 +93,25 @@ class Orthtree { /*! * \brief Self typedef for convenience. */ - typedef Orthtree Self; + using Self = Orthtree; /*! * \brief Degree of the tree (number of children of non-leaf nodes). */ - typedef Dimension_tag<(2 << (Dimension::value - 1))> Degree; + using Degree = Dimension_tag<(2 << (Dimension::value - 1))>; /*! * \brief Index of a given node in the tree; the root always has index 0. */ - typedef std::size_t Node_index; + using Node_index = std::size_t; /*! * \brief Optional index of a node in the tree. */ - typedef std::optional Maybe_node_index; + using Maybe_node_index = std::optional; // todo: maybe this could be private? - typedef Properties::Property_container Node_property_container; + using Node_property_container = Properties::Property_container; /*! \brief Set of bits representing this node's relationship to its parent. @@ -121,7 +121,7 @@ class Orthtree { `z` is greater, and so on for higher dimensions if needed. Used to represent a node's relationship to the center of its parent. */ - typedef std::bitset Local_coordinates; + using Local_coordinates = std::bitset; /*! \brief Coordinates representing this node's relationship @@ -130,25 +130,25 @@ class Orthtree { Each value `(x, y, z, ...)` of global coordinates is calculated by doubling the parent's global coordinates and adding the local coordinates. */ - typedef std::array Global_coordinates; + using Global_coordinates = std::array; /*! * \brief A predicate that determines whether a node must be split when refining a tree. */ - typedef std::function Split_predicate; + using Split_predicate = std::function; /*! * \brief A model of `ConstRange` whose value type is `Node_index`. */ #ifdef DOXYGEN_RUNNING - typedef unspecified_type Node_range; - typedef unspecified_type Node_index_range; + using Node_range = unspecified_type; + using Node_index_range = unspecified_type; #else - typedef boost::iterator_range> Node_index_range; + using Node_index_range = boost::iterator_range>; #endif /// \cond SKIP_IN_MANUAL - typedef Orthtrees::internal::Cartesian_ranges Cartesian_ranges; + using Cartesian_ranges = Orthtrees::internal::Cartesian_ranges; /// \endcond /// @} diff --git a/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h b/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h index 38b4ef3f5aa0..80cdc3cd3e5e 100644 --- a/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h +++ b/Orthtree/include/CGAL/Orthtree/Cartesian_ranges.h @@ -29,8 +29,8 @@ namespace internal template struct Cartesian_ranges { - typedef typename Traits::Point_d Point; - typedef typename Traits::Cartesian_const_iterator_d Cartesian_const_iterator; + using Point = typename Traits::Point_d; + using Cartesian_const_iterator = typename Traits::Cartesian_const_iterator_d; using Range_single = CGAL::Iterator_range; diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index c79965f3afc2..75c7abb990a7 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -50,9 +50,9 @@ class Index_traversal_iterator : public boost::iterator_facade< * * \todo */ - typedef std::function(const Tree&, std::size_t)> Traversal_function; + using Traversal_function = std::function(const Tree&, std::size_t)>; - typedef typename Tree::Node_index Node_index; + using Node_index = typename Tree::Node_index; /// @} diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 48bfb8d98f28..2f79a63008b2 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -6,12 +6,11 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree - Octree; -typedef Octree::Traits Traits; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Traits = Octree::Traits; int main(void) { diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index 2390c2c11476..8e045174de15 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -6,11 +6,10 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree - Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index bdd468509efb..59410aa98631 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -6,12 +6,11 @@ #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree - Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; void test_1_node() { diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index ee7c8f3be524..9b8ae6ffbc84 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -9,11 +9,11 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 8398175dbfd0..f7dc21198bbb 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -8,11 +8,11 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_equality.cpp b/Orthtree/test/Orthtree/test_octree_equality.cpp index e73a5cf3cbd3..fc71eb647ec0 100644 --- a/Orthtree/test/Orthtree/test_octree_equality.cpp +++ b/Orthtree/test/Orthtree/test_octree_equality.cpp @@ -6,11 +6,10 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree -Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; void test_identical_trees() { diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index ef39e2f89b9d..972b4a7168ad 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -10,12 +10,12 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Leaves_traversal Leaves_traversal; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Leaves_traversal = CGAL::Orthtrees::Leaves_traversal; std::size_t count_jumps(Octree& octree) { diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 5f1fbc3ee75b..6b246bb89476 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -8,10 +8,10 @@ #include #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef std::vector Point_vector; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_vector = std::vector; +using Octree = CGAL::Octree; int main(void) { diff --git a/Orthtree/test/Orthtree/test_octree_kernels.cpp b/Orthtree/test/Orthtree/test_octree_kernels.cpp index 20a2d616aa7d..3b2353738b66 100644 --- a/Orthtree/test/Orthtree/test_octree_kernels.cpp +++ b/Orthtree/test/Orthtree/test_octree_kernels.cpp @@ -8,9 +8,9 @@ template void test() { - typedef typename Kernel::Point_3 Point; - typedef CGAL::Point_set_3 Point_set; - typedef CGAL::Octree Octree; + using Point = typename Kernel::Point_3; + using Point_set = CGAL::Point_set_3; + using Octree = CGAL::Octree; Point_set points; CGAL::Random_points_in_cube_3 generator; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 94b502c3e639..708727341490 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -8,12 +8,11 @@ #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree - Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; void test_1_point() { diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index fa4b310ab702..18ae90b40058 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -16,16 +16,14 @@ using namespace std::chrono; -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef Kernel::FT FT; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree - Octree; - -typedef CGAL::Search_traits_3 Kd_tree_traits; -typedef CGAL::Orthogonal_k_neighbor_search Kd_tree_search; -typedef Kd_tree_search::Tree Kd_tree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using FT = Kernel::FT; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Kd_tree_traits = CGAL::Search_traits_3; +using Kd_tree_search = CGAL::Orthogonal_k_neighbor_search; +using Kd_tree = Kd_tree_search::Tree; void naive_vs_octree(std::size_t dataset_size) { diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index f251fde83f9e..64ef05648a81 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -8,10 +8,10 @@ #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; class Split_nth_child_of_root { std::size_t m_n; diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index e07afdbd555e..13d7527bd993 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -8,12 +8,12 @@ #include -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point; -typedef CGAL::Point_set_3 Point_set; -typedef CGAL::Octree Octree; -typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal; -typedef CGAL::Orthtrees::Level_traversal Level_traversal; +using Kernel = CGAL::Simple_cartesian; +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +using Octree = CGAL::Octree; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; +using Level_traversal = CGAL::Orthtrees::Level_traversal; bool test_preorder_1_node() { From c4e6ad77f732c3c9d51bca173400339821311aba Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 13 Sep 2023 09:19:33 +0200 Subject: [PATCH 232/520] Update documentation for Orthtree member functions. --- Orthtree/include/CGAL/Orthtree.h | 149 +++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 16 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 55e68971b33d..dcc497326e13 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -259,7 +259,7 @@ class Orthtree { // Non-necessary but just to be clear on the rule of 5: - // assignment operators deleted (PointRange is a ref) + // assignment operators deleted Orthtree& operator=(const Orthtree& other) = delete; Orthtree& operator=(Orthtree&& other) = delete; @@ -489,26 +489,36 @@ class Orthtree { /// \name Custom Properties /// @{ + /*! + * \brief pass-through to `get_or_add_property` for node properties. + */ template std::pair>, bool> - get_or_add_node_property(const std::string& name, const T default_value = T()) { return m_node_properties.get_or_add_property(name, default_value); } + /*! + * \brief pass-through to `add_property` for node properties. + */ template Node_property_container::Array & add_node_property(const std::string& name, const T default_value = T()) { return m_node_properties.add_property(name, default_value); } + /*! + * \brief pass-through to `get_property` for node properties. + */ template Node_property_container::Array & get_node_property(const std::string& name) { return m_node_properties.get_property(name); } + /*! + * \brief pass-through to `get_property_if_exists` for node properties. + */ template std::optional>> - get_node_property_if_exists(const std::string& name) { return m_node_properties.get_property_if_exists(name); } @@ -521,14 +531,14 @@ class Orthtree { /// @{ /*! - \brief finds the leaf node which contains the point `p`. + \brief finds the leaf node which contains a particular point in space. Traverses the orthtree and finds the deepest cell that has a domain enclosing the point passed. The point passed must be within the region enclosed by the orthtree (bbox of the root node). \param point query point. - \return the node which contains the point. + \return the index of the node which contains the point. */ const Node_index locate(const Point& point) const { @@ -564,7 +574,7 @@ class Orthtree { \note this function requires the function `bool CGAL::do_intersect(QueryType, Traits::Bbox_d)` to be defined. - This function finds all the intersecting nodes and returns them as const pointers. + This function finds all the intersecting nodes and writes their indices to the ouput iterator. \tparam Query the primitive class (e.g. sphere, ray) \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types @@ -584,8 +594,8 @@ class Orthtree { /*! \brief compares the topology of the orthtree with that of `rhs`. - Trees may be considered equivalent even if they contain different points. - Equivalent trees must have the same bounding box and the same node structure. + Trees may be considered equivalent even if they have different contents. + Equivalent trees must have the same root bounding box and the same node structure. */ bool operator==(const Self& rhs) const { @@ -613,33 +623,65 @@ class Orthtree { /// \name Node Access /// @{ + /*! + * \brief Determines whether the node specified by index `n` is a leaf node. + * + * @param n index of the node to check. + * @return true of the node is a leaf, false otherwise. + */ bool is_leaf(Node_index n) const { return !m_node_children[n].has_value(); } + /*! + * \brief Determines whether the node specified by index `n` is a root node. + * + * @param n index of the node to check. + * @return true of the node is a root, false otherwise. + */ bool is_root(Node_index n) const { return n == 0; } + /*! + * \brief Determines the depth of the node specified. + * + * The root node has depth 0, its children have depth 1, and so on. + * + * @param n index of the node to check. + * @return the depth of the node within its tree. + */ std::size_t depth(Node_index n) const { -// std::cerr << n -// << " " << m_node_depths.size() -// << std::endl; return m_node_depths[n]; } + /*! + * \brief Retrieves a reference to the Node_data associated with the node specified by `n`. + * + * @param n index of the node to retrieve data for. + * @return the data associated with the node. + */ Node_data& data(Node_index n) { return m_node_points[n]; } + /*! + * \brief const version of `data()` + */ const Node_data& data(Node_index n) const { return m_node_points[n]; } + /*! + * \brief Retrieves the global coordinates of the node. + */ Global_coordinates global_coordinates(Node_index n) const { return m_node_coordinates[n]; } + /*! + * \brief Retrieves the local coordinates of the node. + */ Local_coordinates local_coordinates(Node_index n) const { Local_coordinates result; for (std::size_t i = 0; i < Dimension::value; ++i) @@ -656,30 +698,56 @@ class Orthtree { return *m_node_parents[node]; } + /*! + \brief returns this node's `i`th child. + \pre `!is_leaf()` + */ Node_index child(Node_index node, std::size_t i) const { CGAL_precondition (!is_leaf(node)); return *m_node_children[node] + i; } - Node_index descendant(Node_index node, std::size_t i) { return child(node, i); } - + /*! + * \brief Retrieves an arbitrary descendant of the node specified by `node`. + * + * Convenience function to avoid the need to call `orthtree.child(orthtree.child(node, 0), 1)`. + * + * Each index in `indices` specifies which child to enter as descending the tree from `node` down. + * Indices are evaluated in the order they appear as parameters, so + * `descendant(root, 0, 1)` returns the second child of the first child of the root. + * + * @param node the node to descend + * @param indices the descent to perform + * @return the index of the specified descendant node. + */ template - Node_index descendant(Node_index node, std::size_t i, Indices... remaining_indices) { - return descendant(child(node, i), remaining_indices...); + Node_index descendant(Node_index node, Indices... indices) { + return recursive_descendant(node, indices...); } + /*! + * \brief Convenience function for retrieving arbitrary nodes, equivalent to `tree.descendant(tree.root(), indices...)`. + */ template Node_index node(Indices... indices) { return descendant(root(), indices...); } + /*! + * \brief Finds the next sibling in the parent of the node specified by the index `n`. + * + * Traverses the tree in increasing order of local index (e.g. 000, 001, 010, etc.) + * + * @param n the node to find the sibling of. + * @return the next sibling of `n` if `n` is not the last node in its parent, otherwise nothing. + */ const Maybe_node_index next_sibling(Node_index n) const { // Root node has no siblings if (is_root(n)) return {}; // Find out which child this is - std::size_t local_coords = local_coordinates(n).to_ulong(); // todo: add local_coordinates(n) helper + std::size_t local_coords = local_coordinates(n).to_ulong(); // The last child has no more siblings if (int(local_coords) == Degree::value - 1) @@ -689,6 +757,12 @@ class Orthtree { return child(parent(n), local_coords + 1); } + /*! + * \brief Finds the next sibling of the parent of the node specified by `n` if it exists. + * + * @param n the node to find the sibling up of. + * @return The next sibling of the parent of `n` if `n` is not the root and its parent has a sibling, otherwise nothing. + */ const Maybe_node_index next_sibling_up(Node_index n) const { // the root node has no next sibling up @@ -705,6 +779,14 @@ class Orthtree { return {}; } + /*! + * \brief Finds the leaf node reached when descending the tree and always choosing child 0. + * + * This is the starting point of a depth-first traversal. + * + * @param n the node to find the deepest first child of. + * @return the index of the deepest first child. + */ Node_index deepest_first_child(Node_index n) const { auto first = n; @@ -714,6 +796,15 @@ class Orthtree { return first; } + /*! + * \brief Finds node reached when descending the tree to a depth `d` and always choosing child 0. + * + * Similar to `deepest_first_child`, but does not go to a fixed depth. + * + * @param n the node to find the `d`th first child of. + * @param d the depth to descend to. + * @return the index of the `d`th first child, nothing if the tree is not deep enough. + */ Maybe_node_index first_child_at_depth(Node_index n, std::size_t d) const { std::queue todo; @@ -791,8 +882,15 @@ class Orthtree { */ void unsplit(Node_index n) { // todo: the child nodes should be de-allocated! + // This may need to be done recursively } + /*! + * \brief Finds the center point of the node specified by `n`. + * + * @param n The node to find the center point for. + * @return the center point of node `n`. + */ Point barycenter(Node_index n) const { // Determine the side length of this node @@ -810,6 +908,15 @@ class Orthtree { return std::apply(m_traits.construct_point_d_object(), bary); } + /*! + * \brief Determines whether a pair of subtrees have the same topology. + * + * @param lhsNode a node in lhsTree + * @param lhsTree an Orthtree + * @param rhsNode a node in rhsTree + * @param rhsTree another Orthtree + * @return true if lhsNode and rhsNode have the same topology, false otherwise + */ static bool is_topology_equal(Node_index lhsNode, const Self& lhsTree, Node_index rhsNode, const Self& rhsTree) { // If one node is a leaf, and the other isn't, they're not the same @@ -831,6 +938,9 @@ class Orthtree { return (lhsTree.global_coordinates(lhsNode) == rhsTree.global_coordinates(rhsNode)); } + /*! + * \brief Helper function for calling `is_topology_equal` on the root nodes of two trees. + */ static bool is_topology_equal(const Self& lhs, const Self& rhs) { return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); } @@ -940,6 +1050,13 @@ class Orthtree { private: // functions : + Node_index recursive_descendant(Node_index node, std::size_t i) { return child(node, i); } + + template + Node_index recursive_descendant(Node_index node, std::size_t i, Indices... remaining_indices) { + return recursive_descendant(child(node, i), remaining_indices...); + } + bool do_intersect(Node_index n, const Sphere& sphere) const { // Create a cubic bounding box from the node From d169adb98be846cc03c9a60ac880f2bff5cbb07a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 13 Sep 2023 16:31:37 +0200 Subject: [PATCH 233/520] Update benchmarks to account for recent changes --- Orthtree/benchmark/Orthtree/construction.cpp | 2 +- Orthtree/benchmark/Orthtree/nearest_neighbor.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Orthtree/benchmark/Orthtree/construction.cpp b/Orthtree/benchmark/Orthtree/construction.cpp index 5d55f39c1ee8..b1f2a132aa5b 100644 --- a/Orthtree/benchmark/Orthtree/construction.cpp +++ b/Orthtree/benchmark/Orthtree/construction.cpp @@ -45,7 +45,7 @@ int main(int argc, char **argv) { auto octreeTime = bench( [&] { // Build the tree - Octree octree(octreePoints, octreePoints.point_map()); + Octree octree({octreePoints, octreePoints.point_map()}); octree.refine(); } ); diff --git a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp index 1ab0f51f7c53..ddba4b8ffd5c 100644 --- a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp +++ b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -95,14 +96,14 @@ int main(int argc, char **argv) { // ); // Build the octree from points (this had to be done second because it rearranges the point set) - Octree octree(points, points.point_map()); + Octree octree({points, points.point_map()}); octree.refine(); // Time how long it takes to find neighbors using the octree auto octreeTime = bench( [&] { - std::vector nearest_neighbors; - octree.nearest_neighbors(search_point, k, std::back_inserter(nearest_neighbors)); + std::vector nearest_neighbors; + CGAL::Orthtrees::nearest_neighbors(octree, search_point, k, std::back_inserter(nearest_neighbors)); } ); From 97ed41fa5a16e68092b409a02407fe0c84515ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 14 Sep 2023 09:17:17 +0200 Subject: [PATCH 234/520] do not document Property_container for now --- Property_map/include/CGAL/Property_container.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index c3659d7db16c..35887a1ee435 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -19,6 +19,8 @@ // todo: maybe this could be avoided #include +#ifndef DOXYGEN_RUNNING + namespace CGAL::Properties { template @@ -600,4 +602,6 @@ class Property_container { } +#endif // DOXYGEN_RUNNING + #endif //CGAL_PROPERTY_CONTAINTER_H From 33dce33e0b880e5775ac8b93cc17ba16398ec19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 14 Sep 2023 09:58:04 +0200 Subject: [PATCH 235/520] fix some doc issues --- .../Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h | 4 +++- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 4 ++-- Orthtree/doc/Orthtree/Doxyfile.in | 1 + Orthtree/include/CGAL/Orthtree.h | 4 +--- Orthtree/include/CGAL/Orthtree_traits_2_base.h | 2 +- Orthtree/include/CGAL/Orthtree_traits_3_base.h | 2 +- Orthtree/include/CGAL/Orthtree_traits_d_base.h | 2 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 19b21aa71a96..6974370a3d08 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -12,7 +12,9 @@ \cgalRefines{OrthtreeTraits} - \cgalHasModel `CGAL::Orthtree_traits_point` + \cgalHasModelsBegin + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModelsEnd */ class CollectionPartitioningOrthtreeTraits { public: diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index a582c6d624d6..d6b5c4343738 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -6,8 +6,8 @@ template parameter of the `CGAL::Orthtree` class. \cgalHasModelsBegin - \cgalHasModel{CGAL::Orthtree_traits_point} - \cgalHasModel{CGAL::Orthtree_traits_face_graph} + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_face_graph} \cgalHasModelsEnd */ class OrthtreeTraits diff --git a/Orthtree/doc/Orthtree/Doxyfile.in b/Orthtree/doc/Orthtree/Doxyfile.in index 980e12e8f6e5..69f43f5fda86 100644 --- a/Orthtree/doc/Orthtree/Doxyfile.in +++ b/Orthtree/doc/Orthtree/Doxyfile.in @@ -5,3 +5,4 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Quadtrees, Octrees, and Orthtrees" EXTRACT_ALL = false HIDE_UNDOC_MEMBERS = true HIDE_UNDOC_CLASSES = true +WARN_IF_UNDOCUMENTED = false \ No newline at end of file diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index dcc497326e13..fc2e5f467040 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -198,9 +198,7 @@ class Orthtree { and is rearranged by the `Orthtree`. Altering the point range after creating the orthtree might leave it in an invalid state. - \param point_range input point range. - \param point_map property map to access the input points. - \param enlarge_ratio ratio to which the bounding box should be enlarged. + \param traits the traits object. */ explicit Orthtree(Traits traits) : diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h index f04e1ac6611f..bed03a753082 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_2_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_2_base.h @@ -28,7 +28,7 @@ namespace CGAL { \tparam GeomTraits model of `Kernel`. - \cgalModels `OrthtreeTraits` + \cgalModels{OrthtreeTraits} \sa `CGAL::Quadtree` \sa `CGAL::Orthtree_traits_point_2` */ diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h index 008e4c8b9d49..0b2ab7ddfaa3 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_3_base.h @@ -28,7 +28,7 @@ namespace CGAL { \tparam GeomTraits model of `Kernel`. - \cgalModels `OrthtreeTraits` + \cgalModels{OrthtreeTraits} \sa `CGAL::Quadtree` \sa `CGAL::Orthtree_traits_point_3` */ diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h index 7851c3970870..53436e01be43 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_d_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_d_base.h @@ -28,7 +28,7 @@ namespace CGAL { \tparam GeomTraits model of `Kernel`. - \cgalModels `OrthtreeTraits` + \cgalModels{OrthtreeTraits} \sa `CGAL::Quadtree` \sa `CGAL::Orthtree_traits_point_d` */ diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 001bd5fc1ab2..00c963ad18b7 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -67,7 +67,7 @@ void reassign_points( \tparam PointSet must be a model of range whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` - \cgalModels `OrthtreeTraits` + \cgalModels{OrthtreeTraits} \sa `CGAL::Octree` \sa `CGAL::Orthtree_traits_2` \sa `CGAL::Orthtree_traits_3` From 4681f17ca8a90c100d01d17b59a90ff4abdfae09 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 10:24:24 +0200 Subject: [PATCH 236/520] Fix a small issue with the Octree definition --- Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h | 1 - .../include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index fb7d4f6b2f95..55692b2ffbab 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -119,7 +119,6 @@ template struct Orthtree_traits_base_for_dimension> { /// \name Types /// @{ - using GeomTraits = K; using Dimension = Dimension_tag<3>; using FT = typename K::FT; using Point_d = typename K::Point_3; diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h index 12de48cc60f4..0aa26c50521b 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h @@ -56,7 +56,7 @@ class RANSAC_octree { typedef std::vector Input_range; typedef Random_index_access_property_map Indexed_point_map; - typedef Orthtree_traits_point_3::type, Input_range, Indexed_point_map> OTraits; + typedef Orthtree_traits_point::type, Input_range, Indexed_point_map, Dimension_tag<3>> OTraits; typedef CGAL::Orthtree Octree; From 37d60b4ac64c9fb698466321c5cce9b969eb8d77 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 15:37:15 +0200 Subject: [PATCH 237/520] Replace pointer with std::optional This is not an ideal solution, but the Scene_points class has bigger issues. --- Point_set_3/include/CGAL/Point_set_3.h | 2 +- .../Scene_points_with_normal_item.cpp | 48 +++--- .../demo/Polyhedron/include/Point_set_3.h | 150 +++++++++--------- 3 files changed, 96 insertions(+), 104 deletions(-) diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 881ea322e751..c5e43dd08d43 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -1018,7 +1018,7 @@ class Point_set_3 std::vector prop = m_base.properties(); for (std::size_t i = 0; i < prop.size(); ++ i) oss << " * \"" << prop[i] << "\" property of type " - << CGAL::demangle(m_base.get_type(prop[i]).name()) << std::endl; + << CGAL::demangle(m_base.property_type(prop[i]).name()) << std::endl; return oss.str(); } diff --git a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp index 75bea895cfac..c8a20a888768 100644 --- a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp @@ -77,21 +77,21 @@ struct Scene_points_with_normal_item_priv } Scene_points_with_normal_item_priv(Scene_points_with_normal_item* parent) - : m_points(new Point_set) + : m_points() { init_values(parent); } Scene_points_with_normal_item_priv(const Scene_points_with_normal_item& toCopy, Scene_points_with_normal_item* parent) - : m_points(new Point_set(*toCopy.d->m_points)) + : m_points(toCopy.d->m_points) { init_values(parent); } Scene_points_with_normal_item_priv(const SMesh& input_mesh, Scene_points_with_normal_item* parent) - : m_points(new Point_set) + : m_points() { using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; @@ -110,8 +110,7 @@ struct Scene_points_with_normal_item_priv { if(m_points) { - delete m_points; - m_points = nullptr; + m_points.reset(); } delete normal_Slider; delete point_Slider; @@ -119,9 +118,9 @@ struct Scene_points_with_normal_item_priv bool isPointSliderMoving() { return is_point_slider_moving; } void initializeBuffers(CGAL::Three::Viewer_interface *viewer) const; - void compute_normals_and_vertices() const; + void compute_normals_and_vertices(); - Point_set* m_points; + std::optional m_points; std::string m_comments; QAction* actionDeleteSelection; QAction* actionResetSelection; @@ -286,7 +285,7 @@ initializeBuffers(CGAL::Three::Viewer_interface *viewer) const colors_points .shrink_to_fit(); } -void Scene_points_with_normal_item_priv::compute_normals_and_vertices() const +void Scene_points_with_normal_item_priv::compute_normals_and_vertices() { const CGAL::qglviewer::Vec offset = static_cast( CGAL::QGLViewer::QGLViewerPool().first())->offset(); @@ -331,9 +330,9 @@ void Scene_points_with_normal_item_priv::compute_normals_and_vertices() const positions_lines.resize(m_points->size() * 3); } - Fill_buffers fill_buffers (m_points, indices, positions_lines, positions_normals, + Fill_buffers fill_buffers (&m_points.value(), indices, positions_lines, positions_normals, item->has_normals(), offset, normal_length * length_factor); - Fill_buffers fill_buffers_2 (m_points, indices, positions_lines, positions_selected_normals, + Fill_buffers fill_buffers_2 (&m_points.value(), indices, positions_lines, positions_selected_normals, item->has_normals(), offset, normal_length * length_factor, m_points->first_selected() - m_points->begin()); @@ -548,7 +547,7 @@ void Scene_points_with_normal_item::selectDuplicates() // Loads point set from .PLY file bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream) { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); d->m_points->clear(); @@ -576,7 +575,7 @@ bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream) // Write point set to .PLY file bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream, bool binary) const { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); d->m_points->reset_indices(); @@ -592,7 +591,7 @@ bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream, bo // Loads point set from .OFF file bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream) { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); d->m_points->clear(); bool ok = CGAL::IO::read_OFF(stream, *(d->m_points)) && !isEmpty(); @@ -605,7 +604,7 @@ bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream) // Write point set to .OFF file bool Scene_points_with_normal_item::write_off_point_set(std::ostream& stream) const { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); d->m_points->reset_indices(); @@ -615,7 +614,7 @@ bool Scene_points_with_normal_item::write_off_point_set(std::ostream& stream) co // Loads point set from .XYZ file bool Scene_points_with_normal_item::read_xyz_point_set(std::istream& stream) { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); d->m_points->clear(); @@ -629,7 +628,7 @@ bool Scene_points_with_normal_item::read_xyz_point_set(std::istream& stream) // Write point set to .XYZ file bool Scene_points_with_normal_item::write_xyz_point_set(std::ostream& stream) const { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); d->m_points->reset_indices(); @@ -639,7 +638,7 @@ bool Scene_points_with_normal_item::write_xyz_point_set(std::ostream& stream) co QString Scene_points_with_normal_item::toolTip() const { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); return QObject::tr("

%1 (color: %4)
" "Point_set_3

" @@ -749,13 +748,13 @@ drawPoints(CGAL::Three::Viewer_interface* viewer) const // Gets wrapped point set Point_set* Scene_points_with_normal_item::point_set() { - Q_ASSERT(d->m_points != nullptr); - return d->m_points; + Q_ASSERT(d->m_points); + return &d->m_points.value(); } const Point_set* Scene_points_with_normal_item::point_set() const { - Q_ASSERT(d->m_points != nullptr); - return d->m_points; + Q_ASSERT(d->m_points); + return &d->m_points.value(); } // Gets wrapped point set @@ -771,14 +770,14 @@ const std::string& Scene_points_with_normal_item::comments() const bool Scene_points_with_normal_item::isEmpty() const { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); return d->m_points->empty(); } void Scene_points_with_normal_item::compute_bbox()const { - Q_ASSERT(d->m_points != nullptr); + Q_ASSERT(d->m_points); Kernel::Iso_cuboid_3 bbox = d->m_points->bounding_box(); setBbox(Bbox(bbox.xmin(),bbox.ymin(),bbox.zmin(), @@ -950,8 +949,7 @@ void Scene_points_with_normal_item::itemAboutToBeDestroyed(Scene_item *item) Scene_item::itemAboutToBeDestroyed(item); if(d && d->m_points && item == this) { - delete d->m_points; - d->m_points = nullptr; + d->m_points.reset(); } } diff --git a/Polyhedron/demo/Polyhedron/include/Point_set_3.h b/Polyhedron/demo/Polyhedron/include/Point_set_3.h index e72edbd90848..3872d5ce11c4 100644 --- a/Polyhedron/demo/Polyhedron/include/Point_set_3.h +++ b/Polyhedron/demo/Polyhedron/include/Point_set_3.h @@ -78,13 +78,13 @@ class Point_set_3 : public CGAL::Point_set_3 m_radius; + std::optional m_red; + std::optional m_green; + std::optional m_blue; + std::optional m_fred; + std::optional m_fgreen; + std::optional m_fblue; mutable CGAL::Iterator_range m_const_range; CGAL::Iterator_range m_range; @@ -134,38 +134,36 @@ class Point_set_3 : public CGAL::Point_set_3template add_property_map ("radius", 0.); + auto [radius_map, out] = this->template add_property_map ("radius", 0.); + m_radius = {radius_map}; return out; } - double& radius (const Index& index) { return m_radius[index]; } - const double& radius (const Index& index) const { return m_radius[index]; } + double& radius (const Index& index) { return m_radius.value()[index]; } + const double& radius (const Index& index) const { return m_radius.value()[index]; } bool check_colors() { - bool found = false; - - boost::tie (m_red, found) = this->template property_map("red"); - if (!found) + m_red = this->template property_map("red"); + if (!m_red) { - boost::tie (m_red, found) = this->template property_map("r"); - if (!found) + m_red = this->template property_map("r"); + if (!m_red) return get_float_colors(); } - boost::tie (m_green, found) = this->template property_map("green"); - if (!found) + m_green = this->template property_map("green"); + if (!m_green) { - boost::tie (m_green, found) = this->template property_map("g"); - if (!found) + m_green = this->template property_map("g"); + if (!m_green) return false; } - boost::tie (m_blue, found) = this->template property_map("blue"); - if (!found) + m_blue = this->template property_map("blue"); + if (!m_blue) { - boost::tie (m_blue, found) = this->template property_map("b"); - if (!found) + m_blue = this->template property_map("b"); + if (!m_blue) return false; } @@ -176,29 +174,26 @@ class Point_set_3 : public CGAL::Point_set_3template property_map("red"); - if (!found) - { - boost::tie (m_fred, found) = this->template property_map("r"); - if (!found) - return get_las_colors(); - } + m_fred = this->template property_map("red"); + if (!m_fred) { + m_fred = this->template property_map("r"); + if (!m_fred) + return get_las_colors(); + } - boost::tie (m_fgreen, found) = this->template property_map("green"); - if (!found) - { - boost::tie (m_fgreen, found) = this->template property_map("g"); - if (!found) - return false; - } + m_fgreen = this->template property_map("green"); + if (!m_fgreen) { + m_fgreen = this->template property_map("g"); + if (!m_fgreen) + return false; + } - boost::tie (m_fblue, found) = this->template property_map("blue"); - if (!found) - { - boost::tie (m_fblue, found) = this->template property_map("b"); - if (!found) - return false; - } + m_fblue = this->template property_map("blue"); + if (!m_fblue) { + m_fblue = this->template property_map("b"); + if (!m_fblue) + return false; + } return true; } @@ -207,25 +202,24 @@ class Point_set_3 : public CGAL::Point_set_3 Ushort_map; - Ushort_map red, green, blue; - boost::tie (red, found) = this->template property_map("R"); - if (!found) + auto red = this->template property_map("R"); + if (!red) return false; - boost::tie (green, found) = this->template property_map("G"); - if (!found) + auto green = this->template property_map("G"); + if (!green) return false; - boost::tie (blue, found) = this->template property_map("B"); - if (!found) + auto blue = this->template property_map("B"); + if (!blue) return false; unsigned int bit_short_to_char = 0; for (iterator it = begin(); it != end(); ++ it) - if (get(red, *it) > 255 - || get(green, *it) > 255 - || get(blue, *it) > 255) + if (get(*red, *it) > 255 + || get(*green, *it) > 255 + || get(*blue, *it) > 255) { bit_short_to_char = 8; break; @@ -236,25 +230,25 @@ class Point_set_3 : public CGAL::Point_set_3template add_property_map("b").first; for (iterator it = begin(); it != end(); ++ it) { - put (m_red, *it, (unsigned char)((get(red, *it) >> bit_short_to_char))); - put (m_green, *it, (unsigned char)((get(green, *it) >> bit_short_to_char))); - put (m_blue, *it, (unsigned char)((get(blue, *it) >> bit_short_to_char))); + put (*m_red, *it, (unsigned char)((get(*red, *it) >> bit_short_to_char))); + put (*m_green, *it, (unsigned char)((get(*green, *it) >> bit_short_to_char))); + put (*m_blue, *it, (unsigned char)((get(*blue, *it) >> bit_short_to_char))); } - this->remove_property_map(red); - this->remove_property_map(green); - this->remove_property_map(blue); + this->remove_property_map(*red); + this->remove_property_map(*green); + this->remove_property_map(*blue); return true; } bool has_colors() const { - return (m_blue != Byte_map() || m_fblue != Double_map()); + return (m_blue || m_fblue); } bool has_byte_colors() const { - return (m_blue != Byte_map()); + return (m_blue); } bool add_colors () @@ -271,32 +265,32 @@ class Point_set_3 : public CGAL::Point_set_3template remove_property_map(m_red); - this->template remove_property_map(m_green); - this->template remove_property_map(m_blue); + this->template remove_property_map(*m_red); + this->template remove_property_map(*m_green); + this->template remove_property_map(*m_blue); } - if (m_fblue != Double_map()) + if (m_fblue) { - this->template remove_property_map(m_fred); - this->template remove_property_map(m_fgreen); - this->template remove_property_map(m_fblue); + this->template remove_property_map(*m_fred); + this->template remove_property_map(*m_fgreen); + this->template remove_property_map(*m_fblue); } } double red (const Index& index) const - { return (m_red == Byte_map()) ? m_fred[index] : double(m_red[index]) / 255.; } + { return (m_red) ? m_fred.value()[index] : double(m_red.value()[index]) / 255.; } double green (const Index& index) const - { return (m_green == Byte_map()) ? m_fgreen[index] : double(m_green[index]) / 255.; } + { return (m_green) ? m_fgreen.value()[index] : double(m_green.value()[index]) / 255.; } double blue (const Index& index) const - { return (m_blue == Byte_map()) ? m_fblue[index] : double(m_blue[index]) / 255.; } + { return (m_blue) ? m_fblue.value()[index] : double(m_blue.value()[index]) / 255.; } void set_color (const Index& index, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0) { - m_red[index] = r; - m_green[index] = g; - m_blue[index] = b; + m_red.value()[index] = r; + m_green.value()[index] = g; + m_blue.value()[index] = b; } void set_color (const Index& index, const QColor& color) From 25790f607729ea89fe906eea52ae49cceef845e5 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 16:51:53 +0200 Subject: [PATCH 238/520] Replace nullable pmap types with std::optionals Also not an ideal solution, but making this correct-by-construction would require more invasive refactoring. --- .../Polyhedron/Scene_surface_mesh_item.cpp | 68 ++++++++----------- .../include/CGAL/Surface_mesh/Surface_mesh.h | 2 +- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 49e2da75e659..688108e772de 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -276,11 +276,11 @@ struct Scene_surface_mesh_item_priv{ mutable QOpenGLShaderProgram *program; Scene_surface_mesh_item *item; - mutable SMesh::Property_map fpatch_id_map; + mutable std::optional> fpatch_id_map; mutable int min_patch_id; - mutable SMesh::Property_map v_selection_map; - mutable SMesh::Property_map f_selection_map; - mutable SMesh::Property_map::edge_descriptor, bool> e_is_feature_map; + mutable std::optional> v_selection_map; + mutable std::optional> f_selection_map; + mutable std::optional::edge_descriptor, bool>> e_is_feature_map; mutable Color_vector colors_; double volume, area; @@ -358,7 +358,7 @@ Scene_surface_mesh_item::vertex_selection_map() if(! d->v_selection_map){ d->v_selection_map = d->smesh_->add_property_map("v:selection").first; } - return d->v_selection_map; + return d->v_selection_map.value(); } Scene_surface_mesh_item::Face_selection_map @@ -367,7 +367,7 @@ Scene_surface_mesh_item::face_selection_map() if(! d->f_selection_map){ d->f_selection_map = d->smesh_->add_property_map("f:selection").first; } - return d->f_selection_map; + return d->f_selection_map.value(); } std::vector& @@ -514,7 +514,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: idx_edge_data_.push_back(source(ed, *smesh_)); idx_edge_data_.push_back(target(ed, *smesh_)); if(has_feature_edges && - get(e_is_feature_map, ed)) + get(*e_is_feature_map, ed)) { idx_feature_edge_data_.push_back(source(ed, *smesh_)); idx_feature_edge_data_.push_back(target(ed, *smesh_)); @@ -556,7 +556,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: { //The sharp features detection produces patch ids >=1, this //is meant to insure the wanted id is in the range [min,max] - QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id]; + QColor c = item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; CGAL::IO::Color color(c.red(),c.green(),c.blue()); CPF::add_color_in_buffer(color, f_colors); } @@ -585,7 +585,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: CGAL::IO::Color *c; if(has_fpatch_id) { - QColor color = item->color_vector()[fpatch_id_map[fd] - min_patch_id]; + QColor color = item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; c = new CGAL::IO::Color(color.red(),color.green(),color.blue()); } else if(has_fcolors) @@ -724,8 +724,8 @@ void Scene_surface_mesh_item_priv::initialize_colors() const int max = 0; min_patch_id = (std::numeric_limits::max)(); for(face_descriptor fd : faces(*smesh_)){ - max = (std::max)(max, fpatch_id_map[fd]); - min_patch_id = (std::min)(min_patch_id, fpatch_id_map[fd]); + max = (std::max)(max, fpatch_id_map.value()[fd]); + min_patch_id = (std::min)(min_patch_id, fpatch_id_map.value()[fd]); } if(item->property("recompute_colors").toBool()) { @@ -911,7 +911,7 @@ void Scene_surface_mesh_item_priv::triangulate_convex_facet(face_descriptor fd, CGAL::IO::Color* color; if(has_fpatch_id) { - QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id]; + QColor c = item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; color = new CGAL::IO::Color(c.red(),c.green(),c.blue()); } else if(has_fcolors) @@ -1003,7 +1003,7 @@ Scene_surface_mesh_item_priv::triangulate_facet(face_descriptor fd, CGAL::IO::Color* color; if(has_fpatch_id) { - QColor c= item->color_vector()[fpatch_id_map[fd] - min_patch_id]; + QColor c= item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; color = new CGAL::IO::Color(c.red(),c.green(),c.blue()); } else if(has_fcolors) @@ -1447,31 +1447,23 @@ bool Scene_surface_mesh_item::intersect_face(double orig_x, } void Scene_surface_mesh_item::setItemIsMulticolor(bool b) { - if(b) - { - d->fpatch_id_map = d->smesh_->add_property_map("f:patch_id", 1).first; + if (b) { + d->fpatch_id_map = d->smesh_->add_property_map("f:patch_id", 1).first; d->has_fcolors = true; - } - else - { - if(d->smesh_->property_map("f:patch_id").second) - { - d->fpatch_id_map = d->smesh_->property_map("f:patch_id").first; - d->smesh_->remove_property_map(d->fpatch_id_map); + } else { + d->fpatch_id_map = d->smesh_->get_property_map("f:patch_id"); + if (d->fpatch_id_map) { + d->smesh_->remove_property_map(d->fpatch_id_map.value()); d->has_fcolors = false; } - if(d->smesh_->property_map("f:color").second) - { - SMesh::Property_map pmap = - d->smesh_->property_map("f:color").first; - d->smesh_->remove_property_map(pmap); + auto fcolormap = d->smesh_->get_property_map("f:color"); + if (fcolormap) { + d->smesh_->remove_property_map(*fcolormap); d->has_fcolors = false; } - if(d->smesh_->property_map("v:color").second) - { - SMesh::Property_map pmap = - d->smesh_->property_map("v:color").first; - d->smesh_->remove_property_map(pmap); + auto vcolormap = d->smesh_->get_property_map("v:color"); + if (vcolormap) { + d->smesh_->remove_property_map(*vcolormap); d->has_vcolors = false; } this->setProperty("NbPatchIds", 0); //for the joinandsplit_plugin @@ -1581,12 +1573,10 @@ Scene_surface_mesh_item::load_obj(std::istream& in) bool Scene_surface_mesh_item::save_obj(std::ostream& out) const { - SMesh::template Property_map vnormals; - bool has_normals = false; - boost::tie(vnormals, has_normals) = d->smesh_->template property_map("v:normal"); + auto vnormals = d->smesh_->template get_property_map("v:normal"); - if(has_normals) - return CGAL::IO::write_OBJ(out, *(d->smesh_), CGAL::parameters::vertex_normal_map(vnormals)); + if(vnormals) + return CGAL::IO::write_OBJ(out, *(d->smesh_), CGAL::parameters::vertex_normal_map(*vnormals)); else return CGAL::IO::write_OBJ(out, *(d->smesh_)); } @@ -2000,7 +1990,7 @@ void Scene_surface_mesh_item::resetColors() setItemIsMulticolor(false); if(d->has_feature_edges){ for(boost::graph_traits::edge_descriptor e : edges(*d->smesh_)){ - put(d->e_is_feature_map, e, false); + put(d->e_is_feature_map.value(), e, false); } d->has_feature_edges = false; } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index d1c75f4d260b..ab3cbe353f32 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -1904,8 +1904,8 @@ class Surface_mesh /// freed. template void remove_property_map(Property_map p) { - // todo: this is never used, but it should probably still work // Maybe this could be replaced with removal by name? + const_cast*>(this)->get_property_container().template remove_property(p.array()); } From e552b0d3cdcf042e46d61607a41cbde5f51d1810 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 17:08:09 +0200 Subject: [PATCH 239/520] Replace pointers to umap and vmap with optionals --- .../Scene_textured_surface_mesh_item.cpp | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp index 0985491e4458..75cb252b81ac 100644 --- a/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp @@ -16,7 +16,7 @@ typedef Edge_container Ec; struct Scene_textured_surface_mesh_item_priv { Scene_textured_surface_mesh_item_priv(Scene_textured_surface_mesh_item* parent) - :sm(new SMesh) + :sm() { item = parent; texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255); @@ -24,7 +24,7 @@ struct Scene_textured_surface_mesh_item_priv vmap = sm->add_property_map("h:v", 0.0f).first; } Scene_textured_surface_mesh_item_priv(const SMesh& p, Scene_textured_surface_mesh_item* parent) - : sm(new SMesh(p)) + : sm(p) { item = parent; @@ -32,8 +32,8 @@ struct Scene_textured_surface_mesh_item_priv umap = sm->add_property_map("h:u", 0.0f).first; vmap = sm->add_property_map("h:v", 0.0f).first; } - Scene_textured_surface_mesh_item_priv(SMesh* const p,Scene_textured_surface_mesh_item* parent) - :sm(p) + Scene_textured_surface_mesh_item_priv(SMesh* const p, Scene_textured_surface_mesh_item* parent) + :sm(*p) { item = parent; texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255); @@ -41,17 +41,12 @@ struct Scene_textured_surface_mesh_item_priv vmap = sm->add_property_map("h:v", 0.0f).first; } - ~Scene_textured_surface_mesh_item_priv() - { - delete sm; - } - - void compute_normals_and_vertices(void) const; + void compute_normals_and_vertices(void); - SMesh* sm; + std::optional sm; ::Texture texture; - SMesh::Property_map umap; - SMesh::Property_map vmap; + std::optional> umap; + std::optional> vmap; //[Px][Py][Pz][Nx][Ny][Nz][u][v] mutable std::vector faces_buffer; @@ -69,7 +64,7 @@ struct Scene_textured_surface_mesh_item_priv typedef Scene_textured_surface_mesh_item I; }; void -Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const +Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) { faces_buffer.resize(0); @@ -81,7 +76,7 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const SMesh::Property_map positions = sm->points(); SMesh::Property_map fnormals = - sm->add_property_map("f:normal").first; + sm->add_property_map("f:normal").first; CGAL::Polygon_mesh_processing::compute_face_normals(*sm,fnormals); for(face_iterator f = faces(*sm).begin(); @@ -104,8 +99,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const faces_buffer.push_back(n[1]); faces_buffer.push_back(n[2]); //uvs [2] - const float u = get(umap, *he); - const float v = get(vmap, *he); + const float u = get(*umap, *he); + const float v = get(*vmap, *he); faces_buffer.push_back(u); faces_buffer.push_back(v); } @@ -129,8 +124,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const edges_buffer.push_back(a.y() + offset.y); edges_buffer.push_back(a.z() + offset.z); //uvs [2] - float u = get(umap, halfedge(*he, *sm)); - float v = get(vmap, halfedge(*he, *sm)); + float u = get(*umap, halfedge(*he, *sm)); + float v = get(*vmap, halfedge(*he, *sm)); edges_buffer.push_back(u); edges_buffer.push_back(v); @@ -140,8 +135,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const edges_buffer.push_back(b.z() + offset.z); //uvs [2] - u = get(umap, opposite(halfedge(*he, *sm), *sm)); - v = get(vmap, opposite(halfedge(*he, *sm), *sm)); + u = get(*umap, opposite(halfedge(*he, *sm), *sm)); + v = get(*vmap, opposite(halfedge(*he, *sm), *sm)); edges_buffer.push_back(u); edges_buffer.push_back(v); @@ -288,13 +283,13 @@ void Scene_textured_surface_mesh_item::drawEdges( SMesh* -Scene_textured_surface_mesh_item::textured_face_graph() { return d->sm; } +Scene_textured_surface_mesh_item::textured_face_graph() { return &d->sm.value(); } const SMesh* -Scene_textured_surface_mesh_item::textured_face_graph() const { return d->sm; } +Scene_textured_surface_mesh_item::textured_face_graph() const { return &d->sm.value(); } bool Scene_textured_surface_mesh_item::isEmpty() const { - return (d->sm == nullptr) || d->sm->is_empty(); + return (!d->sm) || d->sm->is_empty(); } void From bae28c3b4ba3e4d1eb215d8604af87e72198e103 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 17:25:53 +0200 Subject: [PATCH 240/520] Fix a typo: Gt is not a dependent type, so typename is unnecessary --- .../Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h index 89732b4d2dcc..aa14af527c15 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h @@ -40,7 +40,7 @@ namespace CGAL { class InputNormalMap> struct Efficient_RANSAC_traits { /// - typedef typename Gt GeomTraits; + typedef Gt GeomTraits; /// typedef typename Gt::FT FT; /// From ce2c7a9ef58f667f32b1b52ce4b960fcb3a119f1 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 17:58:25 +0200 Subject: [PATCH 241/520] Use optionals for more maps --- .../Point_set_shape_detection_plugin.cpp | 32 ++++++++--------- .../demo/Polyhedron/include/Point_set_3.h | 24 +++++++------ .../Efficient_RANSAC/Efficient_RANSAC.h | 34 +++++++++---------- .../Efficient_RANSAC/Shape_base.h | 8 ++--- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp index e79f43e02d6b..e431b66ebb52 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp @@ -274,13 +274,13 @@ class Polyhedron_demo_point_set_shape_detection_plugin : } std::string& comments = item->comments(); - Point_set::Property_map shape_id; + std::optional> shape_id; if (dialog.add_property()) { - bool added = false; - boost::tie(shape_id, added) = points->template add_property_map ("shape", -1); + auto [shape_id_pmap, added] = points->template add_property_map ("shape", -1); + shape_id = shape_id_pmap; if (!added) { for (auto it = points->begin(); it != points->end(); ++ it) - shape_id[*it] = -1; + shape_id.value()[*it] = -1; } // Remove previously detected shapes from comments. @@ -382,8 +382,8 @@ class Polyhedron_demo_point_set_shape_detection_plugin : for (auto &item : regions[index].second) { point_item->point_set()->insert(points->point(item)); - if (dialog.add_property()) - shape_id[item] = index; + if (dialog.add_property() && shape_id) + shape_id.value()[item] = index; } unsigned char r, g, b; @@ -559,15 +559,13 @@ class Polyhedron_demo_point_set_shape_detection_plugin : std::string& comments = item->comments(); - Point_set::Property_map shape_id; - if (dialog.add_property()) - { - bool added = false; - boost::tie (shape_id, added) = points->template add_property_map ("shape", -1); - if (!added) - { - for (Point_set::iterator it = points->begin(); it != points->end(); ++ it) - shape_id[*it] = -1; + std::optional> shape_id; + if (dialog.add_property()) { + auto [shape_id_pmap, added] = points->template add_property_map ("shape", -1); + shape_id = shape_id_pmap; + if (!added) { + for (auto it = points->begin(); it != points->end(); ++ it) + shape_id.value()[*it] = -1; } // Remove previously detected shapes from comments @@ -696,8 +694,8 @@ class Polyhedron_demo_point_set_shape_detection_plugin : for(std::size_t i : shape->indices_of_assigned_points()) { point_item->point_set()->insert(points->point(*(points->begin()+i))); - if (dialog.add_property()) - shape_id[*(points->begin()+i)] = index; + if (dialog.add_property() && shape_id) + shape_id.value()[*(points->begin()+i)] = index; } unsigned char r, g, b; diff --git a/Polyhedron/demo/Polyhedron/include/Point_set_3.h b/Polyhedron/demo/Polyhedron/include/Point_set_3.h index 3872d5ce11c4..64f5a31be31a 100644 --- a/Polyhedron/demo/Polyhedron/include/Point_set_3.h +++ b/Polyhedron/demo/Polyhedron/include/Point_set_3.h @@ -491,18 +491,20 @@ class Point_set_3 : public CGAL::Point_set_3, - CGAL::internal_np::normal_t, - CGAL::Named_function_parameters - , - CGAL::internal_np::point_t> > > - inline parameters() const - { + , + CGAL::internal_np::normal_t, + CGAL::Named_function_parameters< + typename Base::template Property_map, + CGAL::internal_np::point_t + > + > + > + inline parameters() const { return CGAL::parameters::point_map (this->m_points). - normal_map (this->m_normals). + normal_map (this->m_normals.value()). geom_traits (Kernel()); } diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h index f8f0fe2959f9..1bc87fb7f1e8 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h @@ -253,12 +253,12 @@ class Efficient_RANSAC { /*! Retrieves the point property map. */ - const Point_map &point_map() const { return m_point_pmap; } + const Point_map &point_map() const { return *m_point_pmap; } /*! Retrieves the normal property map. */ - const Normal_map &normal() const { return m_normal_pmap; } + const Normal_map &normal() const { return *m_normal_pmap; } Input_iterator input_iterator_first() const { return m_input_iterator_first; @@ -361,13 +361,13 @@ class Efficient_RANSAC { m_direct_octrees[s] = new Direct_octree( m_traits, last + 1, last + subsetSize + 1, - m_point_pmap, + *m_point_pmap, remainingPoints - subsetSize); } else m_direct_octrees[0] = new Direct_octree( m_traits, m_input_iterator_first, m_input_iterator_first + (subsetSize), - m_point_pmap, + *m_point_pmap, 0); m_available_octree_sizes[s] = subsetSize; @@ -378,7 +378,7 @@ class Efficient_RANSAC { m_global_octree = new Indexed_octree( m_traits, m_input_iterator_first, m_input_iterator_beyond, - m_point_pmap + *m_point_pmap ); m_global_octree->refine(m_options.cluster_epsilon); @@ -574,14 +574,14 @@ class Efficient_RANSAC { static_cast(m_num_available_points)); while (m_shape_index[first_sample] != -1); - done = drawSamplesFromCellContainingPoint - (m_global_octree, - get(m_point_pmap, - *(m_input_iterator_first + first_sample)), - select_random_octree_level(), - indices, - m_shape_index, - m_required_samples); + done = drawSamplesFromCellContainingPoint( + m_global_octree, + get(*m_point_pmap, *(m_input_iterator_first + first_sample)), + select_random_octree_level(), + indices, + m_shape_index, + m_required_samples + ); if (callback && !callback(num_invalid / double(m_num_total_points))) { clear(num_invalid, candidates); @@ -605,8 +605,8 @@ class Efficient_RANSAC { p->compute(indices, m_input_iterator_first, m_traits, - m_point_pmap, - m_normal_pmap, + *m_point_pmap, + *m_normal_pmap, m_options.epsilon, m_options.normal_threshold); @@ -1202,8 +1202,8 @@ class Efficient_RANSAC { // iterators of input data bool m_valid_iterators; Input_iterator m_input_iterator_first, m_input_iterator_beyond; - Point_map m_point_pmap; - Normal_map m_normal_pmap; + std::optional m_point_pmap; + std::optional m_normal_pmap; }; diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h index 8a23794aa420..99fef90cc240 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h @@ -374,7 +374,7 @@ namespace CGAL { */ typename boost::property_traits< typename Traits::Point_map >::reference point(std::size_t i) const { - return get(this->m_point_pmap, *(this->m_first + i)); + return get(this->m_point_pmap.value(), *(this->m_first + i)); } /*! @@ -382,7 +382,7 @@ namespace CGAL { */ typename boost::property_traits< typename Traits::Normal_map >::reference normal(std::size_t i) const { - return get(this->m_normal_pmap, *(this->m_first + i)); + return get(this->m_normal_pmap.value(), *(this->m_first + i)); } /*! @@ -694,8 +694,8 @@ namespace CGAL { Input_iterator m_first; Traits m_traits; - Point_map m_point_pmap; - Normal_map m_normal_pmap; + std::optional m_point_pmap; + std::optional m_normal_pmap; /// \endcond }; } From 48b502e5bb0c7ccb830b7adfa1b86556d70b7b8e Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 18:22:38 +0200 Subject: [PATCH 242/520] Add no-op collect_garbage(visitor) method for backwards compatibility --- Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index ab3cbe353f32..9b0acad0868f 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -1272,9 +1272,11 @@ class Surface_mesh // todo: this should compress the array } -// //undocumented convenience function that allows to get old-index->new-index information -// template -// void collect_garbage(Visitor& visitor); + // undocumented convenience function that allows to get old-index->new-index information + template + void collect_garbage(Visitor& visitor) { + // todo: this should compress the array and remap indices + } /// controls the recycling or not of simplices previously marked as removed /// upon addition of new elements. From c67bec24cc42f9593e6eba184d96743c5b1c2cfc Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 16 Sep 2023 18:38:32 +0200 Subject: [PATCH 243/520] More optionals for non-nullable maps --- BGL/include/CGAL/boost/graph/property_maps.h | 24 +++++++-------- .../Plugins/PMP/Engrave_text_plugin.cpp | 30 ++++++++----------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/property_maps.h b/BGL/include/CGAL/boost/graph/property_maps.h index aab29d9b8811..e265d59dd963 100644 --- a/BGL/include/CGAL/boost/graph/property_maps.h +++ b/BGL/include/CGAL/boost/graph/property_maps.h @@ -24,10 +24,10 @@ template < class TriangleMesh, class VertexPointMap = typename boost::property_map::type > struct Triangle_from_face_descriptor_map{ typename std::remove_const_t* m_tm; - VertexPointMap m_vpm; + std::optional m_vpm; Triangle_from_face_descriptor_map() - : m_tm(nullptr) + : m_tm(nullptr), m_vpm() {} Triangle_from_face_descriptor_map(TriangleMesh const* tm) @@ -58,9 +58,9 @@ struct Triangle_from_face_descriptor_map{ std::remove_const_t& tm = *(pmap.m_tm); CGAL_precondition(halfedge(f,tm) == next(next(next(halfedge(f,tm),tm),tm),tm)); - return value_type( get(pmap.m_vpm, target(halfedge(f,tm),tm)), - get(pmap.m_vpm, target(next(halfedge(f,tm),tm),tm)), - get(pmap.m_vpm, source(halfedge(f,tm),tm)) ); + return value_type( get(*pmap.m_vpm, target(halfedge(f,tm),tm)), + get(*pmap.m_vpm, target(next(halfedge(f,tm),tm),tm)), + get(*pmap.m_vpm, source(halfedge(f,tm),tm)) ); } inline friend @@ -71,9 +71,9 @@ struct Triangle_from_face_descriptor_map{ std::remove_const_t & tm = *(pmap.m_tm); CGAL_precondition(halfedge(f.first,tm) == next(next(next(halfedge(f.first,tm),tm),tm),tm)); - return value_type( get(pmap.m_vpm, target(halfedge(f.first,tm),tm)), - get(pmap.m_vpm, target(next(halfedge(f.first,tm),tm),tm)), - get(pmap.m_vpm, source(halfedge(f.first,tm),tm)) ); + return value_type( get(*pmap.m_vpm, target(halfedge(f.first,tm),tm)), + get(*pmap.m_vpm, target(next(halfedge(f.first,tm),tm),tm)), + get(*pmap.m_vpm, source(halfedge(f.first,tm),tm)) ); } }; @@ -132,7 +132,7 @@ template ::type > struct One_point_from_face_descriptor_map{ One_point_from_face_descriptor_map() - : m_pm(nullptr) + : m_pm(nullptr), m_vpm() {} One_point_from_face_descriptor_map(PolygonMesh const * g) @@ -146,7 +146,7 @@ struct One_point_from_face_descriptor_map{ {} std::remove_const_t* m_pm; - VertexPointMap m_vpm; + std::optional m_vpm; //classical typedefs typedef typename boost::graph_traits::face_descriptor key_type; @@ -160,7 +160,7 @@ struct One_point_from_face_descriptor_map{ get(const One_point_from_face_descriptor_map& m, key_type f) { - return get(m.m_vpm, target(halfedge(f, *m.m_pm), *m.m_pm)); + return get(*m.m_vpm, target(halfedge(f, *m.m_pm), *m.m_pm)); } inline friend @@ -168,7 +168,7 @@ struct One_point_from_face_descriptor_map{ get(const One_point_from_face_descriptor_map& m, const std::pair& f) { - return get(m.m_vpm, target(halfedge(f.first, *m.m_pm), *m.m_pm)); + return get(*m.m_vpm, target(halfedge(f.first, *m.m_pm), *m.m_pm)); } }; diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp index c45bfdc619e7..2c56411ec611 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp @@ -151,8 +151,8 @@ public : pen.setWidth(0); painter->setPen(pen); painter->setBrush(brush); - SMesh::Property_map u; - SMesh::Property_map v; + std::optional> u; + std::optional> v; u = graph->add_property_map ("h:u", 0.0f).first; @@ -167,11 +167,11 @@ public : boost::graph_traits::face_descriptor f(*fi); QPointF points[3]; boost::graph_traits::halfedge_descriptor h = halfedge(f, *graph);; - points[0] = QPointF(get(u, h), -get(v, h)); + points[0] = QPointF(get(*u, h), -get(*v, h)); h = next(halfedge(f, *graph), *graph); - points[1] = QPointF(get(u, h), -get(v, h)); + points[1] = QPointF(get(*u, h), -get(*v, h)); h = next(next(halfedge(f, *graph), *graph), *graph); - points[2] = QPointF(get(u, h), -get(v, h)); + points[2] = QPointF(get(*u, h), -get(*v, h)); painter->drawPolygon(points,3); } @@ -515,7 +515,7 @@ public Q_SLOTS: sm->add_property_map("v:uv3").first; for(SMesh::Vertex_index v : sm->vertices()) { - uv_map_3[v] = Point_3(uv_map[v][0], uv_map[v] + uv_map_3.value()[v] = Point_3(uv_map[v][0], uv_map[v] [1], 0); if(uv_map[v][0] > xmax) xmax = uv_map[v][0]; @@ -582,7 +582,7 @@ public Q_SLOTS: } // build AABB-tree for face location queries - Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3); + Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3.value()); visu_item = new Scene_polylines_item; connect(visu_item, &Scene_polylines_item::aboutToBeDestroyed, this, @@ -609,7 +609,7 @@ public Q_SLOTS: Face_location loc = Surface_mesh_shortest_path::locate( Point_3(p_2.x(), p_2.y(), 0), - aabb_tree, *sm, uv_map_3); + aabb_tree, *sm, uv_map_3.value()); visu_item->polylines.back().push_back( Surface_mesh_shortest_path::point(loc.first, loc.second, *sm, sm->points())); } @@ -630,12 +630,8 @@ public Q_SLOTS: { component->insert(*bfit); } - SMesh::Property_map umap; - SMesh::Property_map vmap; - umap = sm->add_property_map - ("h:u", 0.0f).first; - vmap = sm->add_property_map - ("h:v", 0.0f).first; + auto umap = sm->add_property_map("h:u", 0.0f).first; + auto vmap = sm->add_property_map("h:v", 0.0f).first; SMesh::Halfedge_iterator it; SMesh::Property_map uv_map = sm->property_map("v:uv").first; @@ -886,7 +882,7 @@ public Q_SLOTS: TriangleMesh& tm) { - Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3); + Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3.value()); typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef std::map Map; @@ -907,7 +903,7 @@ public Q_SLOTS: const EPICK::Point_2& pt=fit->vertex(i)->point(); Face_location loc = Surface_mesh_shortest_path::locate( Point_3(pt.x(), pt.y(), 0), - aabb_tree, *sm, uv_map_3); + aabb_tree, *sm, uv_map_3.value()); it->second = add_vertex(Surface_mesh_shortest_path::point(loc.first, loc.second, *sm, sm->points()), tm); } @@ -955,7 +951,7 @@ public Q_SLOTS: EPICK::Vector_2 translation; EPICK::Aff_transformation_2 transfo; std::vector > polylines; - SMesh::Property_map uv_map_3; + std::optional> uv_map_3; SMesh* sm; float xmin, xmax, ymin, ymax; int pointsize; From 38c35d83c72fa23c00048bb5455d1038be479bd1 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 13:56:01 +0200 Subject: [PATCH 244/520] Update orthtree documentation and manual --- Orthtree/doc/Orthtree/Orthtree.txt | 108 +++++++++++------- .../Orthtree/octree_build_from_point_set.cpp | 2 +- .../octree_build_from_point_vector.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 68 +++++------ Orthtree/include/CGAL/Orthtree_traits_point.h | 4 + 5 files changed, 102 insertions(+), 82 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index a8a81d2b9c1b..1f40d39d5c52 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -22,7 +22,7 @@ structures in dimensions 4 and higher. This package provides a general data structure `Orthtree` along with aliases for `Quadtree` and `Octree`. These trees can be constructed -with custom point ranges and split predicates, and iterated on with +with custom contents and split predicates, and iterated on with various traversal methods. \cgalFigureBegin{Orthtree_fig, orthtree.png} @@ -32,10 +32,11 @@ Building an %orthtree in 3D (%octree) from a point cloud. \section Section_Orthtree_Building Building -An orthtree is created using a set of points. The points are not -copied: the provided point range is used directly and is rearranged by +A common purpose for an orthtree is to subdivide a collection of points, +and the Orthtree package provides a traits class for this purpose. +The points are not copied: the provided point range is used directly and rearranged by the orthtree. Altering the point range after creating the orthtree -might leave it in an invalid state. The constructor returns a tree +may leave the tree in an invalid state. The constructor returns a tree with a single (root) node that contains all the points. The method [refine()](@ref CGAL::Orthtree::refine) must be called to @@ -43,9 +44,11 @@ subdivide space further. This method uses a split predicate which takes a node as input and returns `true` if this node should be split, `false` otherwise: this enables users to choose on what criterion should the orthtree be refined. Predefined predicates are -provided such as [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth) or [Maximum_number_of_inliers](@ref CGAL::Orthtrees::Maximum_number_of_inliers). +provided for the point-set orthtree, +including [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth) +and [Maximum_number_of_inliers](@ref CGAL::Orthtrees::Maximum_number_of_inliers). -The simplest way to create an orthtree is using a vector of points. +The simplest way to create a point-set orthtree is from a vector of points. The constructor generally expects a separate point range and map, but the point map defaults to `Identity_property_map` if none is provided. @@ -53,31 +56,36 @@ The split predicate is a user-defined functor that determines whether a node needs to be split. Custom predicates can easily be defined if the existing ones do not match users' needs. +The following example shows the construction of an Octree from a vector of points. +`octree.refine(10, 1)` uses the default split predicate, which +enforces a max-depth and a maximum number of inliers per node ("bucket size"). +Nodes are split if their depth is less than 10, and they contain more than one inlier. + +\cgalExample{Orthtree/octree_build_from_point_vector.cpp} + \subsection Section_Orthtree_Quadtree Building a Quadtree -The `Orthtree` class may be templated with `Orthtree_traits_2` and thus -behave as a %quadtree. For convenience, the alias `Quadtree` is provided. +The `Orthtree` class may be templated with `Orthtree_traits_point<>` +with a 2d dimension tag and thus behave as a %quadtree. +For convenience, the alias `Quadtree` is provided. -The following example shows how to create a %quadtree object from a -vector of `Point_2` objects and refine it, which means constructing -the tree's space subdivision itself, using a maximum depth of 10 and a -maximum number of inliers per node (bucket size) of 5. The refinement -is stopped as soon as one of the conditions is violated: if a node has -more inliers than `bucket_size` but is already at `max_depth`, it is -not split. Similarly, a node that is at a depth smaller than -`max_depth` but already has fewer inliers than `bucket_size` is not -split. +The following example shows the construction of %quadtree from a vector of `Point_2` objects. +`quadtree.refine(10, 5)` uses the default split predicate, which +enforces a max-depth and a maximum number of inliers per node ("bucket size"). +Nodes are split if their depth is less than 10, and they contain more than 5 inliers. \cgalExample{Orthtree/quadtree_build_from_point_vector.cpp} \subsection Section_Orthtree_Point_Vector Building an Octree -The `Orthtree` class may be templated with `Orthtree_traits_point_3` and thus +`Orthtree_traits_point<>` can also be templated with a 3d dimension tag and thus behave as an %octree. For convenience, the alias `Octree` is provided. -The following example shows how to create an %octree from a vector of -`Point_3` objects: +The following example shows how to create an %octree from a vector of `Point_3` objects. +As with the %quadtree example, we use the default split predicate. +In this case the max depth is 10, and the bucket size is 1. + \cgalExample{Orthtree/octree_build_from_point_vector.cpp} @@ -87,12 +95,12 @@ Some data structures such as `Point_set_3` require a non-default point map type and object. This example illustrates how to create an octree from a `Point_set_3` loaded from a file. It also shows a more explicit way of setting the split predicate when refining the tree. -An octree is constructed from the point set and its map. -The tree is refined with a maximum depth (deepest node allowed) of 10, -and a bucket size (maximum number of points contained by a single node) of 20. -The tree is then written to the standard output. +An %octree is constructed from the point set and its corresponding map, +and then written to the standard output. The split predicate is manually constructed and passed to the refine method. +In this case, we use a maximum number of inliers with no corresponding max depth, +this means that nodes will continue to be split until none contain more than 10 points. \cgalExample{Orthtree/octree_build_from_point_set.cpp} @@ -108,10 +116,13 @@ at depth 7 can hold 14. \subsection Section_Orthtree_Orthtree_Point_Vector Building an Orthtree -The following example shows how to build an generalized orthtree in dimension 4. -A `std::vector` is manually filled with points. -The vector is used as the point set, -an `Identity_property_map` is automatically set as the orthtree's map type, so a map does not need to be provided. +An orthtree can also be used with an arbitrary number of dimensions. +The `Orthtree_traits_point` template can infer the arbitrary dimension count from the d-dimensional kernel. + +The following example shows how to build a generalized orthtree in dimension 4. +As `std::vector` is manually filled with 4-dimensional points. +The vector is used as the point set, and an `Identity_property_map` is automatically +set as the orthtree's map type, so a map does not need to be provided. \cgalExample{Orthtree/orthtree_build.cpp} @@ -122,24 +133,31 @@ octrees, but all the presented features also apply to quadtrees and higher dimension orthtrees. %Traversal is the act of navigating among the nodes of the tree. -The `Orthtree` and [Node](@ref CGAL::Orthtree::Node) classes provide a +The `Orthtree` class provides a number of different solutions for traversing the tree. \subsection Section_Orthtree_Manual_Traveral Manual Traversal Because our orthtree is a form of connected acyclic undirected graph, it is possible to navigate between any two nodes. -What that means in practice, is that given a node on the tree, it is possible to +What that means in practice is that given a node on the tree, it is possible to access any other node using the right set of operations. -The `Node` class provides functions that enable the user to access each of its children, as well as its parent (if it exists). +The `Node_index` type provides a handle on a node, and the `orthree` class provides methods +that enable the user to retrieve the indices of each of its children as well as its parent (if they exist). -The following example demonstrates ways of accessing different nodes of a tree, given a reference to one. +Given any node index `node`, the `n`th child of that node can be found with `orthtree.child(node, n)`. +For an octree, values of `n` from 0-7 provide access to the different children. +For non-root nodes, it is possible to access parent nodes using the `orthtree.parent()` accessor. -From the root node, children can be accessed using the subscript operator `CGAL::Orthtree::Node::operator[]()`. -For an octree, values from 0-7 provide access to the different children. +To access grandchildren, it isn't necessary to use nested `orthtree.child()` calls. +Instead, the `orthtree.descendant(node, a, b, ...)` convenience method is provided. +This retrieves the `b`th child of the `a`th child, to any depth. -For non-root nodes, it is possible to access parent nodes using the [parent()](@ref CGAL::Orthtree::Node::parent) accessor. +In most cases, we want to find the descendants of the root node. +For this case, there is another convenience method `orthtree.node(a, b, c, ...)`. +This retrieves the node specified by the descent `a`, `b`, `c`. +It is equivalent to `orthtree.descendant(orthtree.root(), a, b, c, ...)`. -These accessors and operators can be chained to access any node in the tree in a single line of code, as shown in the following example: +The following example demonstrates the use of several of these accessors. \cgalExample{Orthtree/octree_traversal_manual.cpp} @@ -147,7 +165,8 @@ These accessors and operators can be chained to access any node in the tree in a It is often useful to be able to iterate over the nodes of the tree in a particular order. For example, the stream operator `<<` uses a traversal to print out each node. -A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal). +A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) +and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal). To traverse a tree in preorder is to visit each parent immediately followed by its children, whereas in postorder, traversal the children are visited first. @@ -161,9 +180,11 @@ In this case, we print out the nodes of the tree without indentation instead. \subsection Section_Orthtree_Custom_Traversal Custom Traversal -Users can define their own traversal methods by creating models of the -`OrthtreeTraversal` concept. The following example shows how to define a -custom traversal that only traverses the first branch of the octree: +Users can define their own traversal methods by creating models of the `OrthtreeTraversal` concept. +The custom traversal must provide a method which returns the starting point of the traversal (`first_index()`) +and another method which returns the next node in the sequence (`next_index()`). +`next_index()` returns an empty optional when the end of the traversal is reached. +The following example shows how to define a custom traversal that only traverses the first branch of the octree: \cgalExample{Orthtree/octree_traversal_custom.cpp} @@ -200,9 +221,14 @@ Results are put in a vector, and then printed. \cgalExample{Orthtree/octree_find_nearest_neighbor.cpp} +Not all octrees are compatible with nearest neighbor functionality, +as the idea of a nearest neighbor may not make sense for some tree contents. +For the nearest neighbor functions to work, the traits class must implement the +`CollectionPartitioningOrthtreeTraits` concept. + \subsection Section_Orthtree_Grade Grading -An orthtree is graded if the difference of depth between two adjacent +An orthtree is considered "graded" if the difference of depth between two adjacent leaves is at most 1 for every pair of leaves. \cgalFigureBegin{Orthtree_quadree_graded_fig, quadtree_graded.png} diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index c8382b8d5ff0..fd6c0bd97ab1 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -32,7 +32,7 @@ int main(int argc, char **argv) { Octree octree({points, points.point_map()}); // Build the octree with a small bucket size, using a more verbose method - octree.refine(CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers(5, 10)); + octree.refine(CGAL::Orthtrees::Maximum_number_of_inliers(10)); // Print out the tree std::cout << octree << std::endl; diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp index 16f489109e8c..754fcaa3553b 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp @@ -6,7 +6,7 @@ // Type Declarations using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; -using Point_vector = std::vector; // todo: this was changed to std::list at some point; why? +using Point_vector = std::vector; using Octree = CGAL::Octree; int main() { diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index fc2e5f467040..e0b444810ad1 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -48,13 +48,12 @@ namespace CGAL { \ingroup PkgOrthtreeClasses \brief A data structure using an axis-aligned hybercubic - decomposition of dD space for efficient point access and - computations. + decomposition of dD space for efficient access and + computation. - \details It builds a hierarchy of nodes which subdivide the space - based on a collection of points. Each node represents an - axis-aligned hypercubic region of space. A node contains the range - of points that are present in the region it defines, and it may + \details It builds a hierarchy of nodes which subdivices the space. + Each node represents an axis-aligned hypercubic region of space. + The contents of nodes depend on the Traits class, non-leaf nodes also contain \f$2^{dim}\f$ other nodes which further subdivide the region. @@ -158,7 +157,7 @@ class Orthtree { Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - Node_property_container::Array & m_node_points; + Node_property_container::Array & m_node_contents; Node_property_container::Array & m_node_depths; Node_property_container::Array & m_node_coordinates; Node_property_container::Array & m_node_parents; @@ -169,7 +168,7 @@ class Orthtree { using Bbox_dimensions = std::array; std::vector m_side_per_depth; /* side length per node's depth */ - Cartesian_ranges cartesian_range; /* a helper to easily iterator on coordinates of points */ + Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ public: @@ -177,33 +176,22 @@ class Orthtree { /// @{ /*! - \brief creates an orthtree from a collection of points. + \brief creates an orthtree for a traits instance. - The constructed orthtree has a root node, with no children, that - contains the points passed. That root node has a bounding box that - encloses all of the points passed, with padding according to the - `enlarge_ratio`. - - That root node is built as a `n`-cube (square in 2D, cube in 3D, - etc.) whose edge size is the longest bounding box edge multiplied - by `enlarge_ratio`. Using an `enlarge_ratio>1.0` prevents some - points from being exactly on the border of some cells, which can - lead to over-refinement. + The constructed orthtree has a root node with no children, + containing the contents determined by `construct_root_node_contents_object` from the Traits class. + That root node has a bounding box determined by `construct_root_node_bbox_object` from the Traits class, + which typically encloses its contents. This single-node orthtree is valid and compatible with all Orthtree functionality, but any performance benefits are unlikely to be realized until `refine()` is called. - \warning The input point range is not copied. It is used directly - and is rearranged by the `Orthtree`. Altering the point range - after creating the orthtree might leave it in an invalid state. - - \param traits the traits object. */ explicit Orthtree(Traits traits) : m_traits(traits), - m_node_points(m_node_properties.add_property("points")), + m_node_contents(m_node_properties.add_property("contents")), m_node_depths(m_node_properties.add_property("depths", 0)), m_node_coordinates(m_node_properties.add_property("coordinates")), m_node_parents(m_node_properties.add_property("parents")), @@ -234,7 +222,7 @@ class Orthtree { m_traits(other.m_traits), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(other.m_node_properties), - m_node_points(m_node_properties.get_property("points")), + m_node_contents(m_node_properties.get_property("contents")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), @@ -245,7 +233,7 @@ class Orthtree { m_traits(other.m_traits), m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(std::move(other.m_node_properties)), - m_node_points(m_node_properties.get_property("points")), + m_node_contents(m_node_properties.get_property("contents")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), @@ -299,7 +287,7 @@ class Orthtree { // Check if this node needs to be processed if (split_predicate(current, *this)) { - // Split the node, redistributing its points to its children + // Split the node, redistributing its contents to its children split(current); } @@ -317,7 +305,7 @@ class Orthtree { /*! \brief Convenience overload that refines an orthtree using a - maximum depth and maximum number of points in a node as split + maximum depth and maximum number of inliers in a node as split predicate. This is equivalent to calling @@ -330,8 +318,11 @@ class Orthtree { at a depth smaller than `max_depth` but already has fewer inliers than `bucket_size`, it is not split. + \warning This convenience method is only appropriate for trees with traits classes where + `Node_data` is a list-like type with a `size()` method. + \param max_depth deepest a tree is allowed to be (nodes at this depth will not be split). - \param bucket_size maximum points a node is allowed to contain. + \param bucket_size maximum number of items a node is allowed to contain. */ void refine(size_t max_depth = 10, size_t bucket_size = 20) { refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth, bucket_size)); @@ -465,9 +456,9 @@ class Orthtree { /*! \brief constructs the bounding box of a node. - \note The object constructed is not the bounding box of the point - subset inside the node, but the bounding box of the node itself - (cubic). + \note The object constructed is not the bounding box of the node's contents, + but the bounding box of the node itself. + For a cubic orthtree, this will always be cubic. */ Bbox bbox(Node_index n) const { @@ -660,14 +651,14 @@ class Orthtree { * @return the data associated with the node. */ Node_data& data(Node_index n) { - return m_node_points[n]; + return m_node_contents[n]; } /*! * \brief const version of `data()` */ const Node_data& data(Node_index n) const { - return m_node_points[n]; + return m_node_contents[n]; } /*! @@ -829,8 +820,8 @@ class Orthtree { Only leaf nodes should be split. When a node is split it is no longer a leaf node. A number of `Degree::value` children are constructed automatically, and their values are set. - Contents of this node are _not_ propagated automatically. - It is the responsibility of the caller to redistribute the points contained by a node after splitting + Contents of this node are _not_ propagated automatically, this is responsibility of the + `distribute_node_contents_object` in the Traits class. */ void split(Node_index n) { @@ -867,7 +858,7 @@ class Orthtree { // Find the point around which the node is split Point center = barycenter(n); - // Add the node's points to its children + // Add the node's contents to its children m_traits.distribute_node_contents_object()(n, *this, center); } @@ -1073,7 +1064,6 @@ class Orthtree { // if this node is a leaf, then it's considered an intersecting node if (is_leaf(node)) { - // todo: output iterator should hold indices instead of pointers *output++ = node; return output; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 00c963ad18b7..eecaf2140642 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -67,6 +67,10 @@ void reassign_points( \tparam PointSet must be a model of range whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` + \warning The input point set is not copied. It is used directly + and is rearranged by the `Orthtree`. Altering the point range + after creating the orthtree might leave it in an invalid state. + \cgalModels{OrthtreeTraits} \sa `CGAL::Octree` \sa `CGAL::Orthtree_traits_2` From 332e4b2e3018a63895b85c51caf404a7ec17ccf4 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 14:02:02 +0200 Subject: [PATCH 245/520] Replace `typedef` with `using` for consistency --- .../CollectionPartitioningOrthtreeTraits.h | 8 +++---- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 22 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 6974370a3d08..ddf2218fda3e 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -26,7 +26,7 @@ class CollectionPartitioningOrthtreeTraits { /*! * Sphere type used for the shrinking-sphere approach for neighbor queries */ - typedef unspecified_type Sphere_d; + using Sphere_d = unspecified_type; /*! * \brief An element of the `Node_data` list-like type. @@ -34,14 +34,14 @@ class CollectionPartitioningOrthtreeTraits { * Must be constructible from the type produced by dereferencing a `Node_data` iterator. * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. */ - typedef unspecified_type Node_data_element; + using Node_data_element = unspecified_type; /*! * \brief Functor with an operator to produce a geometric object from a `Node_data_element`. * * The return type of the functor must be a valid argument to `CGAL::squared_distance`. */ - typedef unspecified_type Get_geometric_object_for_element; + using Get_geometric_object_for_element = unspecified_type; /// @} @@ -54,4 +54,4 @@ class CollectionPartitioningOrthtreeTraits { Get_geometric_object_for_element get_geometric_object_for_element_object() const; /// @} -}; \ No newline at end of file +}; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index d6b5c4343738..d49253521817 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -17,10 +17,10 @@ class OrthtreeTraits /// \name Types /// @{ - typedef unspecified_type Dimension; ///< Dimension type (see `CGAL::Dimension_tag`). - typedef unspecified_type FT; ///< The number type of the %Cartesian coordinates of types `Point_d` - typedef unspecified_type Point_d; ///< Point type. - typedef unspecified_type Bbox_d; ///< Bounding box type. Must be constructible from a pair of Point_d types. + using Dimension = unspecified_type; ///< Dimension type (see `CGAL::Dimension_tag`). + using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` + using Point_d = unspecified_type; ///< Point type. + using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of Point_d types. /*! A random access iterator type to enumerate the @@ -28,20 +28,20 @@ class OrthtreeTraits todo: This isn't used, should it be? */ - typedef unspecified_type Cartesian_const_iterator_d; + using Cartesian_const_iterator_d = unspecified_type; /*! * \brief The data type contained by each node. */ - typedef unspecified_type Node_data; + using Node_data = unspecified_type; /*! * \brief Number-type which can take on values indicating adjacency directions. * * Must be able to take on values ranging from 0 to the number of faces of the (hyper)cube type, equivalent to 2 * D. */ - typedef unspecified_type Adjacency; ///< Specify the adjacency directions + using Adjacency = unspecified_type; ///< Specify the adjacency directions /*! * \brief Functor with an operator to create the bounding box of the root node. @@ -50,7 +50,7 @@ class OrthtreeTraits * It may be tight-fitting. The orthtree constructor produces a bounding cube surrounding this region. * For traits which assign no data to each node, this can be defined to return a fixed region. */ - typedef unspecified_type Construct_root_node_bbox; + using Construct_root_node_bbox = unspecified_type; /*! * \brief Functor which initializes the contained elements for the root node. @@ -64,7 +64,7 @@ class OrthtreeTraits * For a tree in which each node contains an `std::span` this function would return the span containing all items. * */ - typedef unspecified_type Construct_root_node_contents; + using Construct_root_node_contents = unspecified_type; /*! * \brief Functor which distributes a node's contents to its children. @@ -76,7 +76,7 @@ class OrthtreeTraits * For a tree in which each node contains a span, this may mean rearranging the contents of the original node * and producing spans containing a subset of its contents for each of its children. */ - typedef unspecified_type Distribute_node_contents; + using Distribute_node_contents = unspecified_type; /*! * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. FT arguments. @@ -84,7 +84,7 @@ class OrthtreeTraits * For trees which use a different kernel for the Bbox type, * the return type of this functor must match the kernel used by the Bbox and not that of the contents. */ - typedef unspecified_type Construct_point_d; + using Construct_point_d = unspecified_type; /// @} From 4c6084159a0229bb6c8a3f0b41f8cbfd016c377c Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 14:25:47 +0200 Subject: [PATCH 246/520] Add boost::span to STL_Extension for compatibility with older versions of Boost --- Orthtree/include/CGAL/Orthtree.h | 2 +- .../CGAL/STL_Extension/internal/boost/data.h | 51 +++ STL_Extension/include/CGAL/span.h | 405 ++++++++++++++++++ 3 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h create mode 100644 STL_Extension/include/CGAL/span.h diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e0b444810ad1..e9f29084a548 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -25,9 +25,9 @@ #include #include #include +#include #include -#include #include #include diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h b/STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h new file mode 100644 index 000000000000..5dfabf05fe2e --- /dev/null +++ b/STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h @@ -0,0 +1,51 @@ +// Copyright 2023 Glen Joseph Fernandes +// (glenjofe@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. +// (http://www.boost.org/LICENSE_1_0.txt) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: BSL-1.0 +// +// NOTE: This file was taken from boost 1.83 for use by span.h. + +#ifndef BOOST_CORE_DATA_HPP +#define BOOST_CORE_DATA_HPP + +#include +#include + +namespace boost { + +template +inline constexpr auto +data(C& c) noexcept(noexcept(c.data())) -> decltype(c.data()) +{ + return c.data(); +} + +template +inline constexpr auto +data(const C& c) noexcept(noexcept(c.data())) -> decltype(c.data()) +{ + return c.data(); +} + +template +inline constexpr T* +data(T(&a)[N]) noexcept +{ + return a; +} + +template +inline constexpr const T* +data(std::initializer_list l) noexcept +{ + return l.begin(); +} + +} /* boost */ + +#endif \ No newline at end of file diff --git a/STL_Extension/include/CGAL/span.h b/STL_Extension/include/CGAL/span.h new file mode 100644 index 000000000000..5f4544667bfa --- /dev/null +++ b/STL_Extension/include/CGAL/span.h @@ -0,0 +1,405 @@ +// Copyright 2019-2023 Glen Joseph Fernandes +// (glenjofe@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. +// (http://www.boost.org/LICENSE_1_0.txt) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: BSL-1.0 +// +// NOTE: This file was taken from boost 1.83 for use in the Orthtree package. + +#ifndef BOOST_CORE_SPAN_HPP +#define BOOST_CORE_SPAN_HPP + +#include + +#include +#include +#include + +namespace boost { + +constexpr std::size_t dynamic_extent = static_cast(-1); + +template +class span; + +namespace detail { + +template +struct span_convertible { + static constexpr bool value = std::is_convertible::value; +}; + +template +struct span_capacity { + static constexpr bool value = E == boost::dynamic_extent || E == N; +}; + +template +struct span_compatible { + static constexpr bool value = span_capacity::value && + span_convertible::value; +}; + +template +using span_uncvref = typename std::remove_cv::type>::type; + +template +struct span_is_span { + static constexpr bool value = false; +}; + +template +struct span_is_span > { + static constexpr bool value = true; +}; + +template +struct span_is_array { + static constexpr bool value = false; +}; + +template +struct span_is_array > { + static constexpr bool value = true; +}; + +template +using span_ptr = decltype(boost::data(std::declval())); + +template +struct span_data { }; + +template +struct span_data >::value>::type> { + typedef typename std::remove_pointer >::type type; +}; + +template +struct span_has_data { + static constexpr bool value = false; +}; + +template +struct span_has_data::type, T>::value>::type> { + static constexpr bool value = true; +}; + +template +struct span_has_size { + static constexpr bool value = false; +}; + +template +struct span_has_size().size()), + std::size_t>::value>::type> { + static constexpr bool value = true; +}; + +template +struct span_is_range { + static constexpr bool value = (std::is_const::value || + std::is_lvalue_reference::value) && + !span_is_span >::value && + !span_is_array >::value && + !std::is_array >::value && + span_has_data::value && + span_has_size::value; +}; + +template +struct span_implicit { + static constexpr bool value = E == boost::dynamic_extent || + N != boost::dynamic_extent; +}; + +template +struct span_copyable { + static constexpr bool value = (N == boost::dynamic_extent || + span_capacity::value) && span_convertible::value; +}; + +template +struct span_sub { + static constexpr std::size_t value = E == boost::dynamic_extent ? + boost::dynamic_extent : E - O; +}; + +template +struct span_store { + constexpr span_store(T* p_, std::size_t) noexcept + : p(p_) { } + static constexpr std::size_t n = E; + T* p; +}; + +template +struct span_store { + constexpr span_store(T* p_, std::size_t n_) noexcept + : p(p_) + , n(n_) { } + T* p; + std::size_t n; +}; + +template +struct span_bytes { + static constexpr std::size_t value = sizeof(T) * E; +}; + +template +struct span_bytes { + static constexpr std::size_t value = boost::dynamic_extent; +}; + +} /* detail */ + +template +class span { +public: + typedef T element_type; + typedef typename std::remove_cv::type value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + static constexpr std::size_t extent = E; + + template::type = 0> + constexpr span() noexcept + : s_(0, 0) { } + + template::value, int>::type = 0> + constexpr span(I* f, size_type c) + : s_(f, c) { } + + template::value, int>::type = 0> + explicit constexpr span(I* f, size_type c) + : s_(f, c) { } + + template::value, int>::type = 0> + constexpr span(I* f, L* l) + : s_(f, l - f) { } + + template::value, int>::type = 0> + explicit constexpr span(I* f, L* l) + : s_(f, l - f) { } + + template::value, + int>::type = 0> + constexpr span(typename std::enable_if::type (&a)[N]) noexcept + : s_(a, N) { } + + template::value, + int>::type = 0> + constexpr span(std::array& a) noexcept + : s_(a.data(), N) { } + + template::value, int>::type = 0> + constexpr span(const std::array& a) noexcept + : s_(a.data(), N) { } + + template::value, int>::type = 0> + constexpr span(R&& r) noexcept(noexcept(boost::data(r)) && + noexcept(r.size())) + : s_(boost::data(r), r.size()) { } + + template::value, int>::type = 0> + explicit constexpr span(R&& r) noexcept(noexcept(boost::data(r)) && + noexcept(r.size())) + : s_(boost::data(r), r.size()) { } + + template::value && + detail::span_copyable::value, int>::type = 0> + constexpr span(const span& s) noexcept + : s_(s.data(), s.size()) { } + + template::value && + detail::span_copyable::value, int>::type = 0> + explicit constexpr span(const span& s) noexcept + : s_(s.data(), s.size()) { } + + template + constexpr span first() const { + static_assert(C <= E, "Count <= Extent"); + return span(s_.p, C); + } + + template + constexpr span last() const { + static_assert(C <= E, "Count <= Extent"); + return span(s_.p + (s_.n - C), C); + } + + template + constexpr typename std::enable_if::value> >::type subspan() const { + static_assert(O <= E, "Offset <= Extent"); + return span::value>(s_.p + O, s_.n - O); + } + + template + constexpr typename std::enable_if >::type subspan() const { + static_assert(O <= E && C <= E - O, + "Offset <= Extent && Count <= Extent - Offset"); + return span(s_.p + O, C); + } + + constexpr span first(size_type c) const { + return span(s_.p, c); + } + + constexpr span last(size_type c) const { + return span(s_.p + (s_.n - c), c); + } + + constexpr span subspan(size_type o, + size_type c = dynamic_extent) const { + return span(s_.p + o, + c == dynamic_extent ? s_.n - o : c); + } + + constexpr size_type size() const noexcept { + return s_.n; + } + + constexpr size_type size_bytes() const noexcept { + return s_.n * sizeof(T); + } + + constexpr bool empty() const noexcept { + return s_.n == 0; + } + + constexpr reference operator[](size_type i) const { + return s_.p[i]; + } + + constexpr reference front() const { + return *s_.p; + } + + constexpr reference back() const { + return s_.p[s_.n - 1]; + } + + constexpr pointer data() const noexcept { + return s_.p; + } + + constexpr iterator begin() const noexcept { + return s_.p; + } + + constexpr iterator end() const noexcept { + return s_.p + s_.n; + } + + constexpr reverse_iterator rbegin() const noexcept { + return reverse_iterator(s_.p + s_.n); + } + + constexpr reverse_iterator rend() const noexcept { + return reverse_iterator(s_.p); + } + + constexpr const_iterator cbegin() const noexcept { + return s_.p; + } + + constexpr const_iterator cend() const noexcept { + return s_.p + s_.n; + } + + constexpr const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(s_.p + s_.n); + } + + constexpr const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(s_.p); + } + +private: + detail::span_store s_; +}; + +template +constexpr std::size_t span::extent; + +#ifdef __cpp_deduction_guides +template +span(I*, L) -> span; + +template +span(T(&)[N]) -> span; + +template +span(std::array&) -> span; + +template +span(const std::array&) -> span; + +template +span(R&&) -> span::type>; + +template +span(span) -> span; +#endif + +#ifdef __cpp_lib_byte +template +inline span::value> +as_bytes(span s) noexcept +{ + return span::value>(reinterpret_cast(s.data()), + s.size_bytes()); +} + +template +inline typename std::enable_if::value, + span::value> >::type +as_writable_bytes(span s) noexcept +{ + return span::value>(reinterpret_cast(s.data()), s.size_bytes()); +} +#endif + +} /* boost */ + +#endif \ No newline at end of file From fbc49ad6a24c4cd8568a75c204f4b0473b1f239d Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 14:57:54 +0200 Subject: [PATCH 247/520] Replace boost::tie with optional type --- .../Point_set_normal_estimation_plugin.cpp | 26 +++++++------------ QP_solver/include/CGAL/QP_solver/basic.h | 1 + 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp index 2f05e7383088..e6cc5dacca4c 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp @@ -408,42 +408,36 @@ void Polyhedron_demo_point_set_normal_estimation_plugin::on_actionNormalOrientat CGAL::Timer task_timer; task_timer.start(); std::cerr << "Orient normals with along 2.5D scanlines..." << std::endl; - Point_set::Property_map scan_angle; - Point_set::Property_map scan_direction_flag; - bool angle_found = false, flag_found = false; + auto scan_angle = points->property_map("scan_angle"); + auto scan_direction_flag = points->property_map("scan_direction_flag"); - std::tie (scan_angle, angle_found) - = points->property_map("scan_angle"); - std::tie (scan_direction_flag, flag_found) - = points->property_map("scan_direction_flag"); - - if (!angle_found && !flag_found) + if (!scan_angle && !scan_direction_flag) { std::cerr << " using no additional properties" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters()); } - else if (!angle_found && flag_found) + else if (!scan_angle && scan_direction_flag) { std::cerr << " using scan direction flag" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters(). - scanline_id_map (scan_direction_flag)); + scanline_id_map (scan_direction_flag.value())); } - else if (angle_found && !flag_found) + else if (scan_angle && !scan_direction_flag) { std::cerr << " using scan angle" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters(). - scan_angle_map (scan_angle)); + scan_angle_map (scan_angle.value())); } - else // if (angle_found && flag_found) + else // if (scan_angle && scan_direction_flag) { std::cerr << " using scan angle and direction flag" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters(). - scan_angle_map (scan_angle). - scanline_id_map (scan_direction_flag)); + scan_angle_map (scan_angle.value()). + scanline_id_map (scan_direction_flag.value())); } std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Orient normals: " diff --git a/QP_solver/include/CGAL/QP_solver/basic.h b/QP_solver/include/CGAL/QP_solver/basic.h index 6b48264e8f7e..06c2572d4416 100644 --- a/QP_solver/include/CGAL/QP_solver/basic.h +++ b/QP_solver/include/CGAL/QP_solver/basic.h @@ -23,6 +23,7 @@ #include #include #include +#define _HAS_AUTO_PTR_ETC False // temporarility necessary for boost's compatibility with Clang 15 #include #endif // CGAL_QP_SOLVER_BASIC_H From 9a1701ccfc212b0ed19fae10069651c1f26cb3e9 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 15:53:04 +0200 Subject: [PATCH 248/520] More optional non-nullable maps --- .../Scene_edit_polyhedron_item.cpp | 17 +++++------------ .../include/CGAL/Weights/cotangent_weights.h | 14 +++++++------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp index c4a060696cdb..fc0abb809ce7 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp @@ -448,11 +448,7 @@ struct ROI_faces_pmap SMesh::Property_map irmap; ROI_faces_pmap(std::map*, SMesh* mesh) - :mesh(mesh) - { - //add a is_ROI property_map to mesh - irmap = mesh->add_property_map("f:is_roi",false).first; - } + : mesh(mesh), irmap(mesh->add_property_map("f:is_roi", false).first) {} friend value_type get(const ROI_faces_pmap&pmap, const key_type& f) { @@ -535,14 +531,11 @@ struct Is_constrained_map SMesh::Property_map icmap; SMesh* mesh; - Is_constrained_map() - {} + //Is_constrained_map() {} + Is_constrained_map(std::vector* vec, SMesh* mesh) - :mesh(mesh) - { - icmap = mesh->add_property_map("v:is_control", -1).first; - for(unsigned int i=0; isize(); ++i) - { + : mesh(mesh), icmap(mesh->add_property_map("v:is_control", -1).first) { + for (unsigned int i = 0; i < vec->size(); ++i) { icmap[sm_vertex_descriptor(i)] = (*vec)[i]; } } diff --git a/Weights/include/CGAL/Weights/cotangent_weights.h b/Weights/include/CGAL/Weights/cotangent_weights.h index dc070c5c493e..9b7e58a9522f 100644 --- a/Weights/include/CGAL/Weights/cotangent_weights.h +++ b/Weights/include/CGAL/Weights/cotangent_weights.h @@ -211,7 +211,7 @@ class Cotangent_weight // by the concept SurfaceMeshDeformationWeights. // A bit awkward, but better than duplicating code... PolygonMesh const * const m_pmesh_ptr; - const VertexPointMap m_vpm; + const std::optional m_vpm; const GeomTraits m_traits; bool m_use_clamped_version; @@ -293,7 +293,7 @@ class Cotangent_weight typename GeomTraits::FT operator()(const halfedge_descriptor he) const { CGAL_precondition(m_pmesh_ptr != nullptr); - return this->operator()(he, *m_pmesh_ptr, m_vpm, m_traits); + return this->operator()(he, *m_pmesh_ptr, m_vpm.value(), m_traits); } }; @@ -319,7 +319,7 @@ class Secure_cotangent_weight_with_voronoi_area private: const PolygonMesh& m_pmesh; - const VertexPointMap m_vpm; + const std::optional m_vpm; GeomTraits m_traits; Cotangent_weight cotangent_weight_calculator; @@ -329,7 +329,7 @@ class Secure_cotangent_weight_with_voronoi_area const VertexPointMap vpm, const GeomTraits& traits = GeomTraits()) : m_pmesh(pmesh), m_vpm(vpm), m_traits(traits), - cotangent_weight_calculator(m_pmesh, m_vpm, m_traits, + cotangent_weight_calculator(m_pmesh, m_vpm.value(), m_traits, true /*clamp*/, true /*bound from below*/) { } @@ -361,9 +361,9 @@ class Secure_cotangent_weight_with_voronoi_area const vertex_descriptor v1 = source(he, m_pmesh); const vertex_descriptor v2 = target(next(he, m_pmesh), m_pmesh); - const Point_ref p0 = get(m_vpm, v0); - const Point_ref p1 = get(m_vpm, v1); - const Point_ref p2 = get(m_vpm, v2); + const Point_ref p0 = get(m_vpm.value(), v0); + const Point_ref p1 = get(m_vpm.value(), v1); + const Point_ref p2 = get(m_vpm.value(), v2); const CGAL::Angle angle0 = CGAL::angle(p1, p0, p2); if((angle0 == CGAL::OBTUSE) || From aced88517a603fa9d6ee85f0042356e1d276861f Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 16:33:59 +0200 Subject: [PATCH 249/520] More optional non-nullable maps --- BGL/include/CGAL/boost/graph/property_maps.h | 16 ++++++++-------- .../Scene_edit_polyhedron_item.cpp | 2 -- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/property_maps.h b/BGL/include/CGAL/boost/graph/property_maps.h index e265d59dd963..c92bc7f12b37 100644 --- a/BGL/include/CGAL/boost/graph/property_maps.h +++ b/BGL/include/CGAL/boost/graph/property_maps.h @@ -82,7 +82,7 @@ template < class PolygonMesh, struct Segment_from_edge_descriptor_map{ Segment_from_edge_descriptor_map() - : m_pm(nullptr) + : m_pm(nullptr), m_vpm() {} Segment_from_edge_descriptor_map(PolygonMesh const * pm) @@ -105,7 +105,7 @@ struct Segment_from_edge_descriptor_map{ typedef boost::readable_property_map_tag category; //data std::remove_const_t* m_pm; - VertexPointMap m_vpm; + std::optional m_vpm; //get function for property map inline friend @@ -113,8 +113,8 @@ struct Segment_from_edge_descriptor_map{ get(const Segment_from_edge_descriptor_map& pmap, key_type h) { - return value_type(get(pmap.m_vpm, source(h, *pmap.m_pm) ), - get(pmap.m_vpm, target(h, *pmap.m_pm) ) ); + return value_type(get(pmap.m_vpm.value(), source(h, *pmap.m_pm) ), + get(pmap.m_vpm.value(), target(h, *pmap.m_pm) ) ); } inline friend @@ -122,8 +122,8 @@ struct Segment_from_edge_descriptor_map{ get(const Segment_from_edge_descriptor_map& pmap, const std::pair& h) { - return value_type(get(pmap.m_vpm, source(h.first, *pmap.m_pm) ), - get(pmap.m_vpm, target(h.first, *pmap.m_pm) ) ); + return value_type(get(pmap.m_vpm.value(), source(h.first, *pmap.m_pm) ), + get(pmap.m_vpm.value(), target(h.first, *pmap.m_pm) ) ); } }; @@ -160,7 +160,7 @@ struct One_point_from_face_descriptor_map{ get(const One_point_from_face_descriptor_map& m, key_type f) { - return get(*m.m_vpm, target(halfedge(f, *m.m_pm), *m.m_pm)); + return get(m.m_vpm.value(), target(halfedge(f, *m.m_pm), *m.m_pm)); } inline friend @@ -168,7 +168,7 @@ struct One_point_from_face_descriptor_map{ get(const One_point_from_face_descriptor_map& m, const std::pair& f) { - return get(*m.m_vpm, target(halfedge(f.first, *m.m_pm), *m.m_pm)); + return get(m.m_vpm.value(), target(halfedge(f.first, *m.m_pm), *m.m_pm)); } }; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp index fc0abb809ce7..db12442eeb73 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp @@ -531,8 +531,6 @@ struct Is_constrained_map SMesh::Property_map icmap; SMesh* mesh; - //Is_constrained_map() {} - Is_constrained_map(std::vector* vec, SMesh* mesh) : mesh(mesh), icmap(mesh->add_property_map("v:is_control", -1).first) { for (unsigned int i = 0; i < vec->size(); ++i) { From f021c0c941117c689903299e86e06c59851912de Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 16:52:09 +0200 Subject: [PATCH 250/520] Use optional vpm for all property maps --- BGL/include/CGAL/boost/graph/property_maps.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/property_maps.h b/BGL/include/CGAL/boost/graph/property_maps.h index c92bc7f12b37..9cbe9f2d7bfa 100644 --- a/BGL/include/CGAL/boost/graph/property_maps.h +++ b/BGL/include/CGAL/boost/graph/property_maps.h @@ -58,9 +58,9 @@ struct Triangle_from_face_descriptor_map{ std::remove_const_t& tm = *(pmap.m_tm); CGAL_precondition(halfedge(f,tm) == next(next(next(halfedge(f,tm),tm),tm),tm)); - return value_type( get(*pmap.m_vpm, target(halfedge(f,tm),tm)), - get(*pmap.m_vpm, target(next(halfedge(f,tm),tm),tm)), - get(*pmap.m_vpm, source(halfedge(f,tm),tm)) ); + return value_type( get(pmap.m_vpm.value(), target(halfedge(f,tm),tm)), + get(pmap.m_vpm.value(), target(next(halfedge(f,tm),tm),tm)), + get(pmap.m_vpm.value(), source(halfedge(f,tm),tm)) ); } inline friend @@ -71,9 +71,9 @@ struct Triangle_from_face_descriptor_map{ std::remove_const_t & tm = *(pmap.m_tm); CGAL_precondition(halfedge(f.first,tm) == next(next(next(halfedge(f.first,tm),tm),tm),tm)); - return value_type( get(*pmap.m_vpm, target(halfedge(f.first,tm),tm)), - get(*pmap.m_vpm, target(next(halfedge(f.first,tm),tm),tm)), - get(*pmap.m_vpm, source(halfedge(f.first,tm),tm)) ); + return value_type( get(pmap.m_vpm.value(), target(halfedge(f.first,tm),tm)), + get(pmap.m_vpm.value(), target(next(halfedge(f.first,tm),tm),tm)), + get(pmap.m_vpm.value(), source(halfedge(f.first,tm),tm)) ); } }; @@ -176,7 +176,7 @@ struct One_point_from_face_descriptor_map{ template < class PolygonMesh, class VertexPointMap = typename boost::property_map::type > struct Source_point_from_edge_descriptor_map{ - Source_point_from_edge_descriptor_map() : m_pm(nullptr) + Source_point_from_edge_descriptor_map() : m_pm(nullptr), m_vpm() {} Source_point_from_edge_descriptor_map(PolygonMesh const * g) @@ -197,7 +197,7 @@ struct Source_point_from_edge_descriptor_map{ //data std::remove_const_t* m_pm; - VertexPointMap m_vpm; + std::optional m_vpm; //get function for property map inline friend @@ -205,7 +205,7 @@ struct Source_point_from_edge_descriptor_map{ get(const Source_point_from_edge_descriptor_map& pmap, key_type h) { - return get(pmap.m_vpm, source(h, *pmap.m_pm) ); + return get(pmap.m_vpm.value(), source(h, *pmap.m_pm) ); } inline friend @@ -213,7 +213,7 @@ struct Source_point_from_edge_descriptor_map{ get(const Source_point_from_edge_descriptor_map& pmap, const std::pair& h) { - return get(pmap.m_vpm, source(h.first, *pmap.m_pm) ); + return get(pmap.m_vpm.value(), source(h.first, *pmap.m_pm) ); } }; From 674760974c3ea2939363e5c978ff1ae76e9e5b3e Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 17:10:27 +0200 Subject: [PATCH 251/520] Add a simple test for shared vertices --- Orthtree/test/Orthtree/test_octree_bbox.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 59410aa98631..2c944f7080a5 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -114,6 +114,19 @@ void test_25_nodes() { Octree::Bbox(0, 0.5, 0.5, 0.5, 1, 1)); assert(octree.bbox(octree.node(7, 7)) == Octree::Bbox(0.5, 0.5, 0.5, 1, 1, 1)); + + // All child nodes should share a vertex + auto center_of_last_child = octree.bbox(octree.node(7, 7)).vertex(0); + assert(octree.bbox(octree.node(7, 0)).vertex(7) == center_of_last_child); + assert(octree.bbox(octree.node(7, 1)).vertex(4) == center_of_last_child); + assert(octree.bbox(octree.node(7, 2)).vertex(6) == center_of_last_child); + assert(octree.bbox(octree.node(7, 3)).vertex(5) == center_of_last_child); + assert(octree.bbox(octree.node(7, 4)).vertex(2) == center_of_last_child); + assert(octree.bbox(octree.node(7, 5)).vertex(3) == center_of_last_child); + assert(octree.bbox(octree.node(7, 6)).vertex(1) == center_of_last_child); + + // Nodes of different sizes should also share vertices + assert(octree.bbox(octree.node(7, 0)).vertex(0) == octree.bbox(octree.node(0, 7)).vertex(7)); } int main(void) { From d930a95c384de70bff68754ffe6e7a4ff2c95aaf Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 17:27:09 +0200 Subject: [PATCH 252/520] Use structured bindings instead of boost::tie --- .../Display/Display_property_plugin.cpp | 72 ++++++++----------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp index 994dcd2e8d9b..1507def7cc7f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp @@ -616,11 +616,9 @@ private Q_SLOTS: return; // Here we only target the property maps added by this plugin, so 'double' is fine - SMesh::Property_map property; - bool found; - std::tie(property, found) = sm->property_map(property_name); - if(found) - sm->remove_property_map(property); + auto property = sm->get_property_map(property_name); + if(property) + sm->remove_property_map(property.value()); } void removeDisplayPluginProperties(Scene_item* item) @@ -669,18 +667,14 @@ private Q_SLOTS: return sector_angle; }; - bool not_initialized; - SMesh::Property_map fangle; - - if(extremum == MIN_VALUE) - std::tie(fangle, not_initialized) = sm->add_property_map("f:display_plugin_smallest_angle", 0); - else - std::tie(fangle, not_initialized) = sm->add_property_map("f:display_plugin_largest_angle", 0); + auto [fangle, fangle_created] = sm->add_property_map( + (extremum == MIN_VALUE ? "f:display_plugin_smallest_angle" : "f:display_plugin_largest_angle"), 0 + ); SMesh& mesh = *sm; auto vpm = get(boost::vertex_point, mesh); - if(not_initialized) + if(fangle_created) { for(face_descriptor f : faces(mesh)) { @@ -753,11 +747,9 @@ private Q_SLOTS: if(sm == nullptr) return; - bool not_initialized; - SMesh::Property_map fjacobian; - std::tie(fjacobian, not_initialized) = sm->add_property_map("f:display_plugin_scaled_jacobian", 0); + auto [fjacobian, fjacobian_created] = sm->add_property_map("f:display_plugin_scaled_jacobian", 0); - if(not_initialized) + if(fjacobian_created) { for(face_descriptor f : faces(*sm)) fjacobian[f] = scaled_jacobian(f, *sm); @@ -796,11 +788,9 @@ private Q_SLOTS: if(sm == nullptr) return; - bool not_initialized; - SMesh::Property_map farea; - std::tie(farea, not_initialized) = sm->add_property_map("f:display_plugin_area", 0); + auto [farea, farea_created] = sm->add_property_map("f:display_plugin_area", 0); - if(not_initialized) + if(farea_created) { for(face_descriptor f : faces(*sm)) farea[f] = area(f, *sm); @@ -1338,26 +1328,26 @@ call_on_PS_property(const std::string& name, const Point_set& ps, const Functor& functor) const { - if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); - else if(ps.template property_map(name).second) - return functor(ps.template property_map(name).first); + if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); + else if(ps.template property_map(name)) + return functor(ps.template property_map(name).value()); return false; } From ffeb2ae85ea470cf5e318869fe99cdd4ff086957 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 17:45:58 +0200 Subject: [PATCH 253/520] Remove reference to Node type in traversal concept --- .../doc/Orthtree/Concepts/OrthtreeTraversal.h | 6 ++--- .../Orthtree/octree_traversal_custom.cpp | 6 +++-- Orthtree/include/CGAL/Orthtree/Traversals.h | 24 ++++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index 5aeda690079e..6bde24602f21 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -20,15 +20,15 @@ class OrthtreeTraversal { public: - using Node = unspecified_type; ///< A specialization of [CGAL::Orthtree::Node](@ref CGAL::Orthtree::Node) + using Node_index = unspecified_type; ///< Index type of the orthtree to be traversed /*! \brief returns the first node to iterate to, given the root of the Orthtree. */ - Node first (Node root) const; + Node_index first_index() const; /*! \brief returns the next node to iterate to, given the previous node. */ - Node next(Node n) const; + Node_index next(Node_index n) const; }; diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index bbe4a08b76e5..573219433113 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -17,15 +17,17 @@ using Octree = CGAL::Octree; template struct First_branch_traversal { + using Node_index = typename Tree::Node_index; + const Tree& m_orthtree; explicit First_branch_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - typename Tree::Node_index first_index() const { + Node_index first_index() const { return m_orthtree.root(); } - typename Tree::Maybe_node_index next_index(typename Tree::Node_index n) const { + std::optional next_index(Node_index n) const { // Stop when we reach the base of the tree if (m_orthtree.is_leaf(n)) diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index aa2a76abbc36..e7ac61193b3a 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -47,13 +47,15 @@ struct Preorder_traversal { public: + using Node_index = typename Tree::Node_index; + Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - typename Tree::Node_index first_index() const { + Node_index first_index() const { return m_orthtree.root(); } - std::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(Node_index n) const { if (m_orthtree.is_leaf(n)) { @@ -87,13 +89,15 @@ struct Postorder_traversal { public: + using Node_index = typename Tree::Node_index; + Postorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - typename Tree::Node_index first_index() const { + Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); } - std::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(Node_index n) const { return m_orthtree.index(next(&m_orthtree[n])); } }; @@ -114,13 +118,15 @@ struct Leaves_traversal { public: + using Node_index = typename Tree::Node_index; + Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} - typename Tree::Node_index first_index() const { + Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); } - std::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(Node_index n) const { if (m_orthtree.next_sibling(n)) return m_orthtree.deepest_first_child(*m_orthtree.next_sibling(n)); @@ -150,17 +156,19 @@ struct Level_traversal { public: + using Node_index = typename Tree::Node_index; + /*! constructs a `depth`-level traversal. */ Level_traversal(const Tree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} - typename Tree::Node_index first_index() const { + Node_index first_index() const { // assumes the tree has at least one child at m_depth return *m_orthtree.first_child_at_depth(m_orthtree.root(), m_depth); } - std::optional next_index(typename Tree::Node_index n) const { + std::optional next_index(Node_index n) const { auto next = m_orthtree.next_sibling(n); From e39ca2aef8e26be2bbf6408caa184df073784ca6 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 18:08:35 +0200 Subject: [PATCH 254/520] Replace more `boost::tie`s with structured bindings --- Point_set_3/include/CGAL/Point_set_3.h | 3 +- .../Point_set/Point_set_clustering_plugin.cpp | 16 +++---- .../Point_set_to_mesh_distance_plugin.cpp | 43 ++++++++----------- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index c5e43dd08d43..62ac11172173 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -1122,6 +1122,7 @@ class Point_set_3 Property_back_inserter& operator*() { return *this; } Property_back_inserter& operator= (const value_type& p) { + if(ps.size() <= index) ps.insert(); put(map, index, p); @@ -1182,7 +1183,7 @@ class Point_set_3 /// \cgalAdvancedBegin /// Back inserter on points /// \cgalAdvancedEnd - typedef Property_back_inserter Point_back_inserter; + typedef Property_back_inserter Point_back_inserter; /// \cgalAdvancedType /// \cgalAdvancedBegin /// Property map for pushing new points diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index b998a35750e2..dbddde1db691 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -121,13 +121,10 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() QApplication::processEvents(); CGAL::Real_timer task_timer; task_timer.start(); - Point_set::Property_map cluster_map; - - if (add_property->isChecked()) - cluster_map = points->add_property_map ("cluster_map").first; - else + auto [cluster_map, _] = points->add_property_map( // Use long name to avoid overwriting potentially existing map - cluster_map = points->add_property_map ("cluster_point_set_property_map").first; + add_property->isChecked() ? "cluster_map" : "cluster_point_set_property_map" + ); // Default value if (neighbor_radius->value() == 0) @@ -176,14 +173,13 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() if (gen_color->isChecked()) { Scene_points_with_normal_item* colored; - Point_set::Property_map red, green, blue; colored = new Scene_points_with_normal_item; colored->setName (QString("%1 (clustering)").arg(item->name())); - red = colored->point_set()->add_property_map("red", 0).first; - green = colored->point_set()->add_property_map("green", 0).first; - blue = colored->point_set()->add_property_map("blue", 0).first; + auto red = colored->point_set()->add_property_map("red", 0).first; + auto green = colored->point_set()->add_property_map("green", 0).first; + auto blue = colored->point_set()->add_property_map("blue", 0).first; colored->point_set()->check_colors(); colored->point_set()->reserve (points->size()); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp index 68b777f9ab89..87aaf100fee9 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp @@ -177,27 +177,22 @@ class Point_set_to_mesh_distance_plugin : } private Q_SLOTS: - void select() - { + void select() { Scene_points_with_normal_item* item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if(!item || - !item->point_set()->has_property_map("distance")) - { + qobject_cast(scene->item(scene->mainSelectionIndex())); + if (!item || !item->point_set()->has_property_map("distance")) { CGAL::Three::Three::warning("You must select the resulting point set."); return; } - PMap distance_map; - boost::tie (distance_map, boost::tuples::ignore) = item->point_set()->property_map("distance"); - double distance = dock_widget->distance_spinbox->value(); - for (Point_set::iterator it = item->point_set()->begin(); - it != item->point_set()->end(); ++ it) - { - if(distance <= distance_map[*it]) - item->point_set()->select(*it); - } - item->invalidateOpenGLBuffers(); - item->itemChanged(); + auto distance_map = item->point_set()->property_map("distance").value(); + double distance = dock_widget->distance_spinbox->value(); + for (Point_set::iterator it = item->point_set()->begin(); + it != item->point_set()->end(); ++it) { + if (distance <= distance_map[*it]) + item->point_set()->select(*it); + } + item->invalidateOpenGLBuffers(); + item->itemChanged(); } void perform() { @@ -216,18 +211,14 @@ private Q_SLOTS: Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item(*pn); Color_ramp thermal_ramp; thermal_ramp.build_blue(); - PMap distance_map; - PMap fred_map; - PMap fgreen_map; - PMap fblue_map; - bool d, r, g, b; + new_item->point_set()->remove_colors(); //bind pmaps - boost::tie(distance_map , d) = new_item->point_set()->add_property_map("distance",0); - boost::tie(fred_map , r) = new_item->point_set()->add_property_map("red",0); - boost::tie(fgreen_map, g) = new_item->point_set()->add_property_map("green",0); - boost::tie(fblue_map , b) = new_item->point_set()->add_property_map("blue",0); + auto [distance_map, d] = new_item->point_set()->add_property_map("distance", 0); + auto [fred_map, r] = new_item->point_set()->add_property_map("red", 0); + auto [fgreen_map, g] = new_item->point_set()->add_property_map("green", 0); + auto [fblue_map, b] = new_item->point_set()->add_property_map("blue", 0); new_item->point_set()->check_colors(); Point_set* points = new_item->point_set(); From 3ac8ddf5af12c86bf15ecafe0541b5cac78d910b Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 18:38:07 +0200 Subject: [PATCH 255/520] More accounting for non-nullable maps --- .../Polygonal_surface_reconstruction/internal/hypothesis.h | 2 +- .../internal/point_set_with_planes.h | 2 +- .../Surface_reconstruction_advancing_front_impl.cpp | 6 ++---- .../Point_set/Surface_reconstruction_polygonal_impl.cpp | 6 ++---- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h index 19ee7849a78e..e4c1f4e004a5 100644 --- a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h +++ b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h @@ -747,7 +747,7 @@ namespace CGAL { if (v == Polygon_mesh::null_vertex()) // failed splitting edge return Polygon_mesh::null_halfedge(); - typename Polygon_mesh::template Property_map& coords = mesh.points(); + auto &coords = mesh.points(); coords[v] = *ep.pos; Edge_descriptor e1 = mesh.edge(h); diff --git a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h index 09d628e7f4e0..2061ed13b256 100644 --- a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h +++ b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h @@ -142,7 +142,7 @@ namespace CGAL { std::size_t idx = 0; for (typename PointRange::const_iterator it = points.begin(); it != points.end(); ++it) { Base_class::m_points[idx] = get(point_map, *it); - Base_class::m_normals[idx] = get(normal_map, *it); + Base_class::m_normals.value()[idx] = get(normal_map, *it); int plane_index = get(plane_index_map, *it); if (plane_index != -1) { auto it_and_bool = plane_index_remap.emplace(plane_index, planar_segments_.size()); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp index a8c93069b290..38dffa910f21 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp @@ -19,9 +19,8 @@ struct Construct{ template Construct(SMesh& mesh, const PointRange& points) - : mesh(mesh) + : mesh(mesh), vpmap(get(boost::vertex_point, mesh)) { - vpmap = get(boost::vertex_point, mesh); for (const auto& p : points) { typename boost::graph_traits::vertex_descriptor v; @@ -192,8 +191,7 @@ SMesh* advancing_front (const Point_set& points, if (structuring) // todo { - Point_set::Property_map shape_map - = points.property_map("shape").first; + auto shape_map = points.property_map("shape").value(); typedef CGAL::Point_set_with_structure Structuring; std::vector planes; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp index 3e320160372e..8e075d26ce46 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp @@ -26,11 +26,9 @@ SMesh* polygonal_reconstruct (const Point_set& points, CGAL_USE (model_complexity); CGAL_USE (solver_name); - Point_set::Property_map shape_map - = points.property_map("shape").first; + auto shape_map = points.property_map("shape").value(); - Polygonal_surface_reconstruction poly - (points, points.point_map(), points.normal_map(), shape_map); + Polygonal_surface_reconstruction poly(points, points.point_map(), points.normal_map(), shape_map); SMesh* mesh = new SMesh; From bf3bc031c7072056c6adcc422388374ef8068265 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 19:38:25 +0200 Subject: [PATCH 256/520] Eliminate another `boost::tie` --- .../Polyhedron/Plugins/Display/Heat_method_plugin.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp index fc6d564379b2..c5cffdac60b2 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp @@ -730,11 +730,9 @@ private Q_SLOTS: return; // Here we only target the property maps added by this plugin, so 'double' is fine - SMesh::Property_map property; - bool found; - std::tie(property, found) = sm->property_map(property_name); - if(found) - sm->remove_property_map(property); + auto property = sm->get_property_map(property_name); + if(property) + sm->remove_property_map(property.value()); } void removePluginProperties(Scene_item* item) From 9b738cbf4b9821d7326bfa4a069b530ac930f846 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 22:38:27 +0200 Subject: [PATCH 257/520] Eliminate more instances of boost::tie found with a global search The classification plugin should probably be refactored at some point. --- .../CGAL/Classification/Planimetric_grid.h | 6 +- .../Classification/Point_set_neighborhood.h | 6 +- .../CGAL/Classification/property_maps.h | 2 - .../Classification/Cluster_classification.cpp | 120 +++++++++--------- .../Classification/Cluster_classification.h | 30 ++--- .../Point_set_item_classification.cpp | 102 +++++++-------- .../Point_set_item_classification.h | 71 +++++------ .../Surface_mesh_item_classification.cpp | 26 ++-- .../Surface_mesh_item_classification.h | 5 +- .../demo/Polyhedron/include/Point_set_3.h | 12 +- .../include/CGAL/Search_traits_adapter.h | 26 ++-- 11 files changed, 193 insertions(+), 213 deletions(-) diff --git a/Classification/include/CGAL/Classification/Planimetric_grid.h b/Classification/include/CGAL/Classification/Planimetric_grid.h index 2fd1adf5773e..673727f2df9e 100644 --- a/Classification/include/CGAL/Classification/Planimetric_grid.h +++ b/Classification/include/CGAL/Classification/Planimetric_grid.h @@ -59,7 +59,7 @@ class Planimetric_grid using Image_bool = Image; const PointRange* m_points; - PointMap m_point_map; + std::optional m_point_map; Iso_cuboid_3 m_bbox; float m_resolution; @@ -342,7 +342,7 @@ class Planimetric_grid { if (m_lower_scale == nullptr) { - const Point_3& p = get(m_point_map, *(m_points->begin()+index)); + const Point_3& p = get(m_point_map.value(), *(m_points->begin()+index)); return (std::size_t)((p.x() - m_bbox.xmin()) / m_resolution); } @@ -356,7 +356,7 @@ class Planimetric_grid { if (m_lower_scale == nullptr) { - const Point_3& p = get(m_point_map, *(m_points->begin()+index)); + const Point_3& p = get(m_point_map.value(), *(m_points->begin()+index)); return (std::size_t)((p.y() - m_bbox.ymin()) / m_resolution); } diff --git a/Classification/include/CGAL/Classification/Point_set_neighborhood.h b/Classification/include/CGAL/Classification/Point_set_neighborhood.h index c27c874f0c23..4527ab53f05f 100644 --- a/Classification/include/CGAL/Classification/Point_set_neighborhood.h +++ b/Classification/include/CGAL/Classification/Point_set_neighborhood.h @@ -69,7 +69,7 @@ class Point_set_neighborhood using key_type = std::uint32_t; using category = boost::readable_property_map_tag; - My_point_property_map () { } + //My_point_property_map () { } My_point_property_map (const PointRange *input, PointMap point_map) : input (input), point_map (point_map) { } @@ -88,7 +88,7 @@ class Point_set_neighborhood using Knn = Orthogonal_k_neighbor_search; std::shared_ptr m_tree; - Distance m_distance; + std::optional m_distance; public: @@ -300,7 +300,7 @@ class Point_set_neighborhood void k_neighbors (const Point& query, const unsigned int k, OutputIterator output) const { CGAL_assertion (m_tree != nullptr); - Knn search (*m_tree, query, k, 0, true, m_distance); + Knn search (*m_tree, query, k, 0, true, m_distance.value()); for (typename Knn::iterator it = search.begin(); it != search.end(); ++ it) *(output ++) = it->first; } diff --git a/Classification/include/CGAL/Classification/property_maps.h b/Classification/include/CGAL/Classification/property_maps.h index f03e837d8067..bad32f9d7ee7 100644 --- a/Classification/include/CGAL/Classification/property_maps.h +++ b/Classification/include/CGAL/Classification/property_maps.h @@ -59,8 +59,6 @@ class Face_descriptor_to_center_of_mass_map public: - Face_descriptor_to_center_of_mass_map () - : m_mesh (nullptr) { } Face_descriptor_to_center_of_mass_map (const FaceGraph* mesh) : m_mesh (mesh), m_vpm (get (vertex_point, *m_mesh)) { } Face_descriptor_to_center_of_mass_map (const FaceGraph* mesh, VertexPointMap vpm) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp index b26135e231ae..2c861f7c0c71 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp @@ -30,9 +30,8 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po backup_existing_colors_and_add_new(); - bool cluster_found = false; - boost::tie (m_cluster_id, cluster_found) = m_points->point_set()->property_map("shape"); - if (!cluster_found) + auto m_cluster_id = m_points->point_set()->property_map("shape"); + if (!m_cluster_id) { std::cerr << "Error! Cluster not found!" << std::endl; abort(); @@ -40,7 +39,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po CGAL::Classification::create_clusters_from_indices (*(m_points->point_set()), m_points->point_set()->point_map(), - m_cluster_id, + m_cluster_id.value(), m_clusters); std::cerr << m_clusters.size() << " cluster(s) found" << std::endl; @@ -57,20 +56,20 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po if (!classif_found) { - Point_set::Property_map las_classif; - boost::tie (las_classif, las_found) = m_points->point_set()->property_map("classification"); - if (las_found) + auto las_classif = m_points->point_set()->property_map("classification"); + las_found = las_classif.has_value(); + if (las_classif) { m_input_is_las = true; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - unsigned char uc = las_classif[*it]; - m_classif[*it] = int(uc); + unsigned char uc = las_classif.value()[*it]; + m_classif.value()[*it] = int(uc); if (!training_found) - m_training[*it] = int(uc); + m_training.value()[*it] = int(uc); } - m_points->point_set()->remove_property_map (las_classif); + m_points->point_set()->remove_property_map (las_classif.value()); classif_found = true; training_found = true; } @@ -85,7 +84,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po { if (training_found) { - int l = m_training[*it]; + int l = m_training.value()[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -95,7 +94,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po } if (classif_found) { - int l = m_classif[*it]; + int l = m_classif.value()[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -125,31 +124,31 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int c = m_cluster_id[*it]; + int c = m_cluster_id.value()[*it]; if (training_found) { if (std::size_t(current_idx) != used_indices.size()) // Empty indices -> reorder indices in point set { - if (las_found && (m_training[*it] == 0 || m_training[*it] == 1)) // Unclassified class in LAS - m_training[*it] = -1; - else if (m_training[*it] != -1) - m_training[*it] = used_indices[std::size_t(m_training[*it])]; + if (las_found && (m_training.value()[*it] == 0 || m_training.value()[*it] == 1)) // Unclassified class in LAS + m_training.value()[*it] = -1; + else if (m_training.value()[*it] != -1) + m_training.value()[*it] = used_indices[std::size_t(m_training.value()[*it])]; } - if (c != -1 && m_training[*it] != -1) - m_clusters[c].training() = m_training[*it]; + if (c != -1 && m_training.value()[*it] != -1) + m_clusters[c].training() = m_training.value()[*it]; } if (classif_found) { if (std::size_t(current_idx) != used_indices.size()) // Empty indices -> reorder indices in point set { - if (las_found && (m_classif[*it] == 0 || m_classif[*it] == 1)) // Unclassified class in LAS - m_classif[*it] = -1; - else if (m_classif[*it] != -1) - m_classif[*it] = used_indices[std::size_t(m_classif[*it])]; + if (las_found && (m_classif.value()[*it] == 0 || m_classif.value()[*it] == 1)) // Unclassified class in LAS + m_classif.value()[*it] = -1; + else if (m_classif.value()[*it] != -1) + m_classif.value()[*it] = used_indices[std::size_t(m_classif.value()[*it])]; } - if (c != -1 && m_classif[*it] != -1) - m_clusters[c].label() = m_classif[*it]; + if (c != -1 && m_classif.value()[*it] != -1) + m_clusters[c].label() = m_classif.value()[*it]; } } @@ -240,11 +239,11 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po Delaunay dt (boost::make_transform_iterator (m_points->point_set()->begin(), Point_set_with_cluster_info (m_points->point_set(), - m_cluster_id)), + m_cluster_id.value())), boost::make_transform_iterator (m_points->point_set()->end(), Point_set_with_cluster_info (m_points->point_set(), - m_cluster_id))); + m_cluster_id.value()))); std::set > adjacencies; @@ -294,16 +293,16 @@ Cluster_classification::~Cluster_classification() for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int c = m_cluster_id[*it]; + int c = m_cluster_id.value()[*it]; if (c != -1) { - m_training[*it] = m_clusters[c].training(); - m_classif[*it] = m_clusters[c].label(); + m_training.value()[*it] = m_clusters[c].training(); + m_classif.value()[*it] = m_clusters[c].label(); } else { - m_training[*it] = -1; - m_classif[*it] = -1; + m_training.value()[*it] = -1; + m_classif.value()[*it] = -1; } } @@ -359,22 +358,22 @@ Cluster_classification::~Cluster_classification() for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif[*it]; + int c = m_classif.value()[*it]; unsigned char lc = 1; // unclassified in LAS standard if (c != -1) lc = label_indices[std::size_t(c)]; las_classif[*it] = lc; - int t = m_training[*it]; + int t = m_training.value()[*it]; unsigned char lt = 1; // unclassified in LAS standard if (t != -1) lt = label_indices[std::size_t(t)]; - m_training[*it] = int(lt); + m_training.value()[*it] = int(lt); } - m_points->point_set()->remove_property_map (m_classif); + m_points->point_set()->remove_property_map (m_classif.value()); } @@ -391,7 +390,7 @@ void Cluster_classification::backup_existing_colors_and_add_new() m_color = m_points->point_set()->add_property_map("real_color").first; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_color[*it] = CGAL::IO::Color ((unsigned char)(255 * m_points->point_set()->red(*it)), + m_color.value()[*it] = CGAL::IO::Color ((unsigned char)(255 * m_points->point_set()->red(*it)), (unsigned char)(255 * m_points->point_set()->green(*it)), (unsigned char)(255 * m_points->point_set()->blue(*it))); @@ -403,15 +402,15 @@ void Cluster_classification::backup_existing_colors_and_add_new() void Cluster_classification::reset_colors() { - if (m_color == Point_set::Property_map()) + if (!m_color) m_points->point_set()->remove_colors(); else { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color[*it]); + m_points->point_set()->set_color(*it, m_color.value()[*it]); - m_points->point_set()->remove_property_map(m_color); + m_points->point_set()->remove_property_map(m_color.value()); } } @@ -438,7 +437,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color[*it]); + m_points->point_set()->set_color(*it, m_color.value()[*it]); } else if (index_color == 1) // classif { @@ -446,7 +445,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { std::size_t c = m_clusters[cid].label(); @@ -464,7 +463,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; float div = 1; if (cid != -1) @@ -486,7 +485,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { @@ -516,7 +515,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { float v = (std::max) (0.f, (std::min)(1.f, m_label_probabilities[corrected_index][cid])); @@ -553,7 +552,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { if (feature->value(cid) > max) @@ -567,7 +566,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { float v = (feature->value(cid) - min) / (max - min); @@ -598,15 +597,14 @@ int Cluster_classification::real_index_color() const { int out = m_index_color; - if (out == 0 && m_color == Point_set::Property_map()) + if (out == 0 && !m_color) out = -1; return out; } void Cluster_classification::reset_indices () { - Point_set::Property_map indices - = m_points->point_set()->property_map("index").first; + auto indices = m_points->point_set()->property_map("index").value(); m_points->point_set()->unselect_all(); Point_set::Index idx; @@ -629,18 +627,16 @@ void Cluster_classification::compute_features (std::size_t nb_scales, float voxe m_features.clear(); - Point_set::Vector_map normal_map; + std::optional normal_map; bool normals = m_points->point_set()->has_normal_map(); if (normals) normal_map = m_points->point_set()->normal_map(); - bool colors = (m_color != Point_set::Property_map()); + bool colors = m_color.has_value(); - Point_set::Property_map echo_map; - bool echo; - boost::tie (echo_map, echo) = m_points->point_set()->template property_map("echo"); - if (!echo) - boost::tie (echo_map, echo) = m_points->point_set()->template property_map("number_of_returns"); + auto echo_map = m_points->point_set()->template property_map("echo"); + if (!echo_map) + echo_map = m_points->point_set()->template property_map("number_of_returns").value(); Feature_set pointwise_features; @@ -655,11 +651,11 @@ void Cluster_classification::compute_features (std::size_t nb_scales, float voxe generator.generate_point_based_features(pointwise_features); if (normals) - generator.generate_normal_based_features (pointwise_features, normal_map); + generator.generate_normal_based_features (pointwise_features, normal_map.value()); if (colors) - generator.generate_color_based_features (pointwise_features, m_color); - if (echo) - generator.generate_echo_based_features (pointwise_features, echo_map); + generator.generate_color_based_features (pointwise_features, m_color.value()); + if (echo_map) + generator.generate_echo_based_features (pointwise_features, echo_map.value()); #ifdef CGAL_LINKED_WITH_TBB pointwise_features.end_parallel_additions(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h index ef4d006e2ce0..c89e25e747ba 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h @@ -125,13 +125,12 @@ class Cluster_classification : public Item_classification_base { typedef typename Point_set::template Property_map Pmap; bool okay = false; - Pmap pmap; - boost::tie (pmap, okay) = m_points->point_set()->template property_map(name.c_str()); - if (okay) + auto pmap = m_points->point_set()->template property_map(name.c_str()); + if (pmap) feature_set.template add > - (*(m_points->point_set()), pmap, name.c_str()); + (*(m_points->point_set()), pmap.value(), name.c_str()); - return okay; + return pmap.has_value(); } void add_selection_to_training_set (std::size_t label) @@ -139,7 +138,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { m_clusters[cid].training() = int(label); @@ -165,7 +164,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { m_clusters[cid].training() = -1; @@ -187,7 +186,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) m_clusters[cid].training() = m_clusters[cid].label(); } @@ -212,7 +211,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { int c = m_clusters[cid].label(); @@ -238,7 +237,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id[*it]; + int cid = m_cluster_id.value()[*it]; if (cid != -1) { int c = m_clusters[cid].label(); @@ -376,13 +375,10 @@ class Cluster_classification : public Item_classification_base std::vector m_clusters; - Point_set::Property_map m_red; - Point_set::Property_map m_green; - Point_set::Property_map m_blue; - Point_set::Property_map m_color; - Point_set::Property_map m_cluster_id; - Point_set::Property_map m_training; - Point_set::Property_map m_classif; + std::optional> m_color; + std::optional> m_cluster_id; + std::optional> m_training; + std::optional> m_classif; std::vector > m_label_probabilities; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp index 12bc858fea91..3c827920cb5f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp @@ -16,23 +16,20 @@ #include Point_set_item_classification::Point_set_item_classification(Scene_points_with_normal_item* points) - : m_points (points) - , m_generator (nullptr) - , m_input_is_las (false) -{ + : m_points(points), m_generator(nullptr), m_input_is_las(false), + m_training(), + m_classif(){ m_index_color = 1; reset_indices(); - Point_set::Property_map cluster_id; - bool cluster_found = false; - boost::tie (cluster_id, cluster_found) = m_points->point_set()->property_map("shape"); - if (cluster_found) + auto cluster_id = m_points->point_set()->property_map("shape"); + if (cluster_id) { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int c = cluster_id[*it]; + int c = cluster_id.value()[*it]; if (c == -1) continue; if (std::size_t(c) >= m_clusters.size()) @@ -54,20 +51,20 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n if (!classif_found) { - Point_set::Property_map las_classif; - boost::tie (las_classif, las_found) = m_points->point_set()->property_map("classification"); - if (las_found) + auto las_classif = m_points->point_set()->property_map("classification"); + las_found = las_classif.has_value(); + if (las_classif) { m_input_is_las = true; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - unsigned char uc = las_classif[*it]; - m_classif[*it] = int(uc); + unsigned char uc = las_classif.value()[*it]; + m_classif.value()[*it] = int(uc); if (!training_found) - m_training[*it] = int(uc); + m_training.value()[*it] = int(uc); } - m_points->point_set()->remove_property_map (las_classif); + m_points->point_set()->remove_property_map (las_classif.value()); classif_found = true; training_found = true; } @@ -82,7 +79,7 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n { if (training_found) { - int l = m_training[*it]; + int l = m_training.value()[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -92,7 +89,7 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n } if (classif_found) { - int l = m_classif[*it]; + int l = m_classif.value()[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -125,17 +122,17 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n { if (training_found) { - if (las_found && (m_training[*it] == 0 || m_training[*it] == 1)) // Unclassified class in LAS - m_training[*it] = -1; - else if (m_training[*it] != -1) - m_training[*it] = used_indices[std::size_t(m_training[*it])]; + if (las_found && (m_training.value()[*it] == 0 || m_training.value()[*it] == 1)) // Unclassified class in LAS + m_training.value()[*it] = -1; + else if (m_training.value()[*it] != -1) + m_training.value()[*it] = used_indices[std::size_t(m_training.value()[*it])]; } if (classif_found) { - if (las_found && (m_classif[*it] == 0 || m_classif[*it] == 1)) // Unclassified class in LAS - m_classif[*it] = -1; - else if (m_classif[*it] != -1) - m_classif[*it] = used_indices[std::size_t(m_classif[*it])]; + if (las_found && (m_classif.value()[*it] == 0 || m_classif.value()[*it] == 1)) // Unclassified class in LAS + m_classif.value()[*it] = -1; + else if (m_classif.value()[*it] != -1) + m_classif.value()[*it] = used_indices[std::size_t(m_classif.value()[*it])]; } } } @@ -295,22 +292,22 @@ Point_set_item_classification::~Point_set_item_classification() for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif[*it]; + int c = m_classif.value()[*it]; unsigned char lc = 1; // unclassified in LAS standard if (c != -1) lc = label_indices[std::size_t(c)]; las_classif[*it] = lc; - int t = m_training[*it]; + int t = m_training.value()[*it]; unsigned char lt = 1; // unclassified in LAS standard if (t != -1) lt = label_indices[std::size_t(t)]; - m_training[*it] = int(lt); + m_training.value()[*it] = int(lt); } - m_points->point_set()->remove_property_map (m_classif); + m_points->point_set()->remove_property_map (m_classif.value()); } reset_colors(); @@ -327,7 +324,7 @@ void Point_set_item_classification::backup_existing_colors_and_add_new() m_color = m_points->point_set()->add_property_map("real_color").first; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_color[*it] = CGAL::IO::Color((unsigned char)(255 * m_points->point_set()->red(*it)), + m_color.value()[*it] = CGAL::IO::Color((unsigned char)(255 * m_points->point_set()->red(*it)), (unsigned char)(255 * m_points->point_set()->green(*it)), (unsigned char)(255 * m_points->point_set()->blue(*it))); @@ -339,15 +336,15 @@ void Point_set_item_classification::backup_existing_colors_and_add_new() void Point_set_item_classification::reset_colors() { - if (m_color == Point_set::Property_map()) + if (!m_color) m_points->point_set()->remove_colors(); else { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color[*it]); + m_points->point_set()->set_color(*it, m_color.value()[*it]); - m_points->point_set()->remove_property_map(m_color); + m_points->point_set()->remove_property_map(m_color.value()); } } @@ -374,7 +371,7 @@ void Point_set_item_classification::change_color (int index, float* vmin, float* for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color[*it]); + m_points->point_set()->set_color(*it, m_color.value()[*it]); } else if (index_color == 1) // classif { @@ -382,7 +379,7 @@ void Point_set_item_classification::change_color (int index, float* vmin, float* it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - std::size_t c = m_classif[*it]; + std::size_t c = m_classif.value()[*it]; if (c != std::size_t(-1)) color = label_qcolor (m_labels[c]); @@ -396,8 +393,8 @@ void Point_set_item_classification::change_color (int index, float* vmin, float* it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - int c = m_training[*it]; - int c2 = m_classif[*it]; + int c = m_training.value()[*it]; + int c2 = m_classif.value()[*it]; if (c != -1) color = label_qcolor (m_labels[std::size_t(c)]); @@ -490,15 +487,14 @@ int Point_set_item_classification::real_index_color() const { int out = m_index_color; - if (out == 0 && m_color == Point_set::Property_map()) + if (out == 0 && !m_color) out = -1; return out; } void Point_set_item_classification::reset_indices () { - Point_set::Property_map indices - = m_points->point_set()->property_map("index").first; + auto indices = m_points->point_set()->property_map("index").value(); m_points->point_set()->unselect_all(); Point_set::Index idx; @@ -524,18 +520,16 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales, flo m_features.clear(); - Point_set::Vector_map normal_map; + std::optional normal_map; bool normals = m_points->point_set()->has_normal_map(); if (normals) normal_map = m_points->point_set()->normal_map(); - bool colors = (m_color != Point_set::Property_map()); + bool colors = m_color.has_value(); - Point_set::Property_map echo_map; - bool echo; - boost::tie (echo_map, echo) = m_points->point_set()->template property_map("echo"); - if (!echo) - boost::tie (echo_map, echo) = m_points->point_set()->template property_map("number_of_returns"); + auto echo_map = m_points->point_set()->template property_map("echo"); + if (!echo_map) + echo_map = m_points->point_set()->template add_property_map("number_of_returns").first; m_generator = new Generator (*(m_points->point_set()), m_points->point_set()->point_map(), nb_scales, voxel_size); @@ -548,11 +542,11 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales, flo m_generator->generate_point_based_features(m_features); if (normals) - m_generator->generate_normal_based_features (m_features, normal_map); + m_generator->generate_normal_based_features (m_features, normal_map.value()); if (colors) - m_generator->generate_color_based_features (m_features, m_color); - if (echo) - m_generator->generate_echo_based_features (m_features, echo_map); + m_generator->generate_color_based_features (m_features, m_color.value()); + if (echo_map) + m_generator->generate_echo_based_features (m_features, echo_map.value()); #ifdef CGAL_LINKED_WITH_TBB m_features.end_parallel_additions(); @@ -709,7 +703,7 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - training[*it] = m_training[*it]; + training[*it] = m_training.value()[*it]; if (training[*it] != -1) { nb_label[std::size_t(training[*it])] ++; @@ -758,7 +752,7 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_classif[*it] = indices[*it]; + m_classif.value()[*it] = indices[*it]; if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h index f52e2438aabd..0b0f21e96f6f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h @@ -47,13 +47,9 @@ class Point_set_item_classification : public Item_classification_base Point_set::Property_map cluster_id; std::vector* clusters; - Cluster_neighborhood (Point_set* point_set, - std::vector& clusters) - : point_set (point_set) - , clusters (&clusters) - { - cluster_id = point_set->property_map("shape").first; - } + Cluster_neighborhood(Point_set* point_set, + std::vector& clusters) + : point_set(point_set), clusters(&clusters), cluster_id(point_set->add_property_map("shape").first) {} template OutputIterator operator() (const Point_set::Index& idx, @@ -147,18 +143,16 @@ class Point_set_item_classification : public Item_classification_base bool try_adding_simple_feature (const std::string& name) { typedef typename Point_set::template Property_map Pmap; - bool okay = false; - Pmap pmap; - boost::tie (pmap, okay) = m_points->point_set()->template property_map(name.c_str()); - if (okay) + auto pmap = m_points->point_set()->template property_map(name.c_str()); + if (pmap) { std::cerr << "Adding property<" << CGAL::demangle(typeid(Type).name()) << ">(" << name << ") as feature" << std::endl; m_features.template add > - (*(m_points->point_set()), pmap, name.c_str()); + (*(m_points->point_set()), pmap.value(), name.c_str()); } - return okay; + return pmap.has_value(); } void add_selection_to_training_set (std::size_t label) @@ -166,8 +160,8 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - m_training[*it] = int(label); - m_classif[*it] = int(label); + m_training.value()[*it] = int(label); + m_classif.value()[*it] = int(label); } m_points->resetSelection(); @@ -178,8 +172,8 @@ class Point_set_item_classification : public Item_classification_base { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) - if (m_training[*it] == int(label)) - m_training[*it] = -1; + if (m_training.value()[*it] == int(label)) + m_training.value()[*it] = -1; if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); } @@ -188,8 +182,8 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - m_training[*it] = -1; - m_classif[*it] = -1; + m_training.value()[*it] = -1; + m_classif.value()[*it] = -1; } if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); @@ -198,7 +192,7 @@ class Point_set_item_classification : public Item_classification_base { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) - m_training[*it] = -1; + m_training.value()[*it] = -1; if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); } @@ -206,7 +200,7 @@ class Point_set_item_classification : public Item_classification_base { for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) - m_training[*it] = m_classif[*it]; + m_training.value()[*it] = m_classif.value()[*it]; m_points->resetSelection(); if (m_index_color == 1 || m_index_color == 2) @@ -229,7 +223,7 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif[*it]; + int c = m_classif.value()[*it]; if (c == label) points_item->point_set()->insert (m_points->point_set()->point(*it)); } @@ -251,7 +245,7 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif[*it]; + int c = m_classif.value()[*it]; if (c != -1) points_item[c]->point_set()->insert (m_points->point_set()->point(*it)); } @@ -271,15 +265,15 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - if (m_training[*it] == int(position)) - m_training[*it] = -1; - else if (m_training[*it] > int(position)) - m_training[*it] --; - - if (m_classif[*it] == int(position)) - m_classif[*it] = -1; - else if (m_classif[*it] > int(position)) - m_classif[*it] --; + if (m_training.value()[*it] == int(position)) + m_training.value()[*it] = -1; + else if (m_training.value()[*it] > int(position)) + m_training.value()[*it] --; + + if (m_classif.value()[*it] == int(position)) + m_classif.value()[*it] = -1; + else if (m_classif.value()[*it] > int(position)) + m_classif.value()[*it] --; } update_comments_of_point_set_item(); } @@ -364,8 +358,8 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - m_classif[*it] = indices[*it]; - ground_truth[*it] = m_training[*it]; + m_classif.value()[*it] = indices[*it]; + ground_truth[*it] = m_training.value()[*it]; } if (m_index_color == 1 || m_index_color == 2) @@ -396,14 +390,11 @@ class Point_set_item_classification : public Item_classification_base std::vector m_clusters; - Point_set::Property_map m_red; - Point_set::Property_map m_green; - Point_set::Property_map m_blue; - Point_set::Property_map m_color; + std::optional> m_color; std::vector > m_label_probabilities; - Point_set::Property_map m_training; - Point_set::Property_map m_classif; + std::optional> m_training; + std::optional> m_classif; Generator* m_generator; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp index 4880c0917db3..00a9e29a5327 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp @@ -15,13 +15,13 @@ Surface_mesh_item_classification::Surface_mesh_item_classification(Scene_surface_mesh_item* mesh) : m_mesh (mesh), m_selection (nullptr), - m_generator (nullptr) + m_generator (nullptr), + m_training(m_mesh->polyhedron()->add_property_map("f:training", std::size_t(-1)).first), + m_classif(m_mesh->polyhedron()->add_property_map("f:label", std::size_t(-1)).first) { m_index_color = 1; backup_existing_colors_and_add_new(); - m_training = m_mesh->polyhedron()->add_property_map("f:training", std::size_t(-1)).first; - m_classif = m_mesh->polyhedron()->add_property_map("f:label", std::size_t(-1)).first; m_labels.add("ground"); m_labels.add("vegetation"); @@ -64,8 +64,8 @@ void Surface_mesh_item_classification::backup_existing_colors_and_add_new() = m_mesh->polyhedron()->add_property_map("f:real_color").first; for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) { - m_real_color[fd] = m_color[fd]; - m_color[fd] = CGAL::IO::Color(128, 128, 128); + m_real_color.value()[fd] = m_color.value()[fd]; + m_color.value()[fd] = CGAL::IO::Color(128, 128, 128); } } else @@ -77,7 +77,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo { m_index_color = index; int index_color = index; - if (index == 0 && m_real_color == Mesh::Property_map()) + if (index == 0 && !m_real_color) index_color = -1; static Color_ramp ramp; @@ -86,12 +86,12 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo if (index_color == -1) // item color { for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) - m_color[fd] = CGAL::IO::Color(128,128,128); + m_color.value()[fd] = CGAL::IO::Color(128,128,128); } else if (index_color == 0) // real colors { for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) - m_color[fd] = m_real_color[fd]; + m_color.value()[fd] = m_real_color.value()[fd]; } else if (index_color == 1) // classif { @@ -103,7 +103,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo if (c != std::size_t(-1)) color = label_qcolor (m_labels[c]); - m_color[fd] = CGAL::IO::Color(color.red(), color.green(), color.blue()); + m_color.value()[fd] = CGAL::IO::Color(color.red(), color.green(), color.blue()); } } else if (index_color == 2) // training @@ -120,7 +120,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo float div = 1; if (c != c2) div = 2; - m_color[fd] = CGAL::IO::Color(color.red() / div, + m_color.value()[fd] = CGAL::IO::Color(color.red() / div, color.green() / div, color.blue() / div); } @@ -135,7 +135,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo { for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) { - m_color[fd] = CGAL::IO::Color((unsigned char)(128), + m_color.value()[fd] = CGAL::IO::Color((unsigned char)(128), (unsigned char)(128), (unsigned char)(128)); } @@ -145,7 +145,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) { float v = (std::max) (0.f, (std::min)(1.f, m_label_probabilities[corrected_index][fd])); - m_color[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), + m_color.value()[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), (unsigned char)(ramp.g(v) * 255), (unsigned char)(ramp.b(v) * 255)); } @@ -189,7 +189,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo if (v < 0.f) v = 0.f; if (v > 1.f) v = 1.f; - m_color[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), + m_color.value()[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), (unsigned char)(ramp.g(v) * 255), (unsigned char)(ramp.b(v) * 255)); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h index 797e88de4385..df71c098b744 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h @@ -200,8 +200,9 @@ class Surface_mesh_item_classification : public Item_classification_base Scene_polyhedron_selection_item* m_selection; Mesh::Property_map m_training; Mesh::Property_map m_classif; - Mesh::Property_map m_color; - Mesh::Property_map m_real_color; + + std::optional> m_color; + std::optional> m_real_color; std::vector > m_label_probabilities; diff --git a/Polyhedron/demo/Polyhedron/include/Point_set_3.h b/Polyhedron/demo/Polyhedron/include/Point_set_3.h index 64f5a31be31a..c3b00ac3258a 100644 --- a/Polyhedron/demo/Polyhedron/include/Point_set_3.h +++ b/Polyhedron/demo/Polyhedron/include/Point_set_3.h @@ -295,17 +295,17 @@ class Point_set_3 : public CGAL::Point_set_3 void set_color (const Index& index, const ColorRange& color) { - m_red[index] = color[0]; - m_green[index] = color[1]; - m_blue[index] = color[2]; + m_red.value()[index] = color[0]; + m_green.value()[index] = color[1]; + m_blue.value()[index] = color[2]; } diff --git a/Spatial_searching/include/CGAL/Search_traits_adapter.h b/Spatial_searching/include/CGAL/Search_traits_adapter.h index ff640dc639a4..a43dca7bd308 100644 --- a/Spatial_searching/include/CGAL/Search_traits_adapter.h +++ b/Spatial_searching/include/CGAL/Search_traits_adapter.h @@ -66,13 +66,15 @@ struct Get_iso_box_d template class Search_traits_adapter : public Base_traits{ - PointPropertyMap ppmap; + std::optional ppmap; public: typedef Base_traits Base; typedef typename internal::Get_iso_box_d::type Iso_box_d; - Search_traits_adapter(const PointPropertyMap& ppmap_=PointPropertyMap(), + Search_traits_adapter() {} + + Search_traits_adapter(const PointPropertyMap& ppmap_, const Base_traits& base=Base_traits() ):Base_traits(base),ppmap(ppmap_){} @@ -246,28 +248,30 @@ class Search_traits_adapter : public Base_traits{ } Iso_box_d operator() (const Point_with_info& p, const Point_with_info& q) const { - return Base_functor::operator() (get(ppmap,p),get(ppmap,q)); + return Base_functor::operator() (get(ppmap.value(),p),get(ppmap.value(),q)); } }; - const PointPropertyMap& point_property_map() const {return ppmap;} + const PointPropertyMap& point_property_map() const {return ppmap.value();} Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const { return Construct_cartesian_const_iterator_d( Base::construct_cartesian_const_iterator_d_object(), - ppmap); + ppmap.value()); } }; template class Distance_adapter : public Base_distance { - PointPropertyMap ppmap; + std::optional ppmap; public: - Distance_adapter( const PointPropertyMap& ppmap_=PointPropertyMap(), - const Base_distance& distance=Base_distance() - ):Base_distance(distance),ppmap(ppmap_){} + Distance_adapter() {} + + Distance_adapter(const PointPropertyMap& ppmap_, + const Base_distance& distance = Base_distance()) + : Base_distance(distance), ppmap(ppmap_) {} using Base_distance::transformed_distance; @@ -275,11 +279,11 @@ class Distance_adapter : public Base_distance { typedef Point_with_info Point_d; typedef typename Base_distance::Query_item Query_item; - const PointPropertyMap& point_property_map() const {return ppmap;} + const PointPropertyMap& point_property_map() const {return ppmap.value();} FT transformed_distance(const Query_item& p1, const Point_with_info& p2) const { - return Base_distance::transformed_distance(p1,get(ppmap,p2)); + return Base_distance::transformed_distance(p1,get(ppmap.value(),p2)); } template From c575cd29cdb0ec7c6a2bd8726607cc9d1bc3c331 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 24 Sep 2023 23:30:05 +0200 Subject: [PATCH 258/520] Remove a couple of default initializations --- .../Plugins/Surface_mesh/Parameterization_plugin.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp index fffd332a8322..596a315eb747 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp @@ -269,10 +269,9 @@ public : pen.setWidth(0); painter->setPen(pen); painter->setBrush(brush); - SMesh::Property_map u,v; - u = graph->add_property_map("h:u", 0.0f).first; - v = graph->add_property_map("h:v", 0.0f).first; + auto u = graph->add_property_map("h:u", 0.0f).first; + auto v = graph->add_property_map("h:v", 0.0f).first; for( Component::iterator fi = components->at(m_current_component).begin(); @@ -926,11 +925,8 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio QApplication::restoreOverrideCursor(); QPointF pmin(FLT_MAX, FLT_MAX), pmax(-FLT_MAX, -FLT_MAX); - SMesh::Property_map umap; - SMesh::Property_map vmap; - - umap = tMesh.add_property_map("h:u", 0.0f).first; - vmap = tMesh.add_property_map("h:v", 0.0f).first; + auto umap = tMesh.add_property_map("h:u", 0.0f).first; + auto vmap = tMesh.add_property_map("h:v", 0.0f).first; tMesh.property_stats(std::cerr); Base_face_graph::Halfedge_iterator it; From 6f86c932c8ab22e8fe32214381ad5ed2e491b4ee Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 27 Sep 2023 21:13:46 +0200 Subject: [PATCH 259/520] Update function documentation for consistency --- Orthtree/include/CGAL/Orthtree.h | 288 ++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 103 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e9f29084a548..2c0de4864f85 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -268,8 +268,7 @@ class Orthtree { while nodes that were not split and for which `split_predicate` returns `true` are split. - \param split_predicate determines whether or not a node needs to - be subdivided. + \param split_predicate determines whether or not a node needs to be subdivided. */ void refine(const Split_predicate& split_predicate) { @@ -303,7 +302,6 @@ class Orthtree { } /*! - \brief Convenience overload that refines an orthtree using a maximum depth and maximum number of inliers in a node as split predicate. @@ -331,6 +329,8 @@ class Orthtree { /*! \brief refines the orthtree such that the difference of depth between two immediate neighbor leaves is never more than 1. + + This is done only by adding nodes, nodes are never removed. */ void grade() { @@ -459,6 +459,10 @@ class Orthtree { \note The object constructed is not the bounding box of the node's contents, but the bounding box of the node itself. For a cubic orthtree, this will always be cubic. + + \param n node to generate a bounding box for + + \return the bounding box of the node n */ Bbox bbox(Node_index n) const { @@ -479,7 +483,15 @@ class Orthtree { /// @{ /*! - * \brief pass-through to `get_or_add_property` for node properties. + \brief Gets a property for nodes, adding it if it doesn't already exist. + + \tparam T the type of the property to add + + \param name the name of the new property + \param default_value the default value assigned to nodes for this property + + \return pair of a reference to the new node property array + and a boolean which is True if the property needed to be created */ template std::pair>, bool> @@ -488,7 +500,14 @@ class Orthtree { } /*! - * \brief pass-through to `add_property` for node properties. + \brief Adds a property for nodes. + + \tparam T the type of the property to add + + \param name the name of the new property + \param default_value the default value assigned to nodes for this property + + \return a reference to the new property array */ template Node_property_container::Array & add_node_property(const std::string& name, const T default_value = T()) { @@ -496,7 +515,15 @@ class Orthtree { } /*! - * \brief pass-through to `get_property` for node properties. + \brief Gets a property of the tree nodes. + + The property to be retrieved must exist in the tree. + + \tparam T the type of the property to retrieve + + \param name the name of the property + + \return a reference to the property array */ template Node_property_container::Array & get_node_property(const std::string& name) { @@ -504,7 +531,13 @@ class Orthtree { } /*! - * \brief pass-through to `get_property_if_exists` for node properties. + \brief Gets a property of the tree nodes if it exists. + + \tparam T the type of the property to retrieve + + \param name the name of the property + + \return an optional containing a reference to the property array if it exists */ template std::optional>> @@ -512,8 +545,6 @@ class Orthtree { return m_node_properties.get_property_if_exists(name); } - // todo: is it ever useful to be able to delete/reset properties? - /// @} /// \name Queries @@ -527,6 +558,7 @@ class Orthtree { the region enclosed by the orthtree (bbox of the root node). \param point query point. + \return the index of the node which contains the point. */ const Node_index locate(const Point& point) const { @@ -563,12 +595,15 @@ class Orthtree { \note this function requires the function `bool CGAL::do_intersect(QueryType, Traits::Bbox_d)` to be defined. - This function finds all the intersecting nodes and writes their indices to the ouput iterator. + This function finds all the intersecting leaf nodes and writes their indices to the ouput iterator. \tparam Query the primitive class (e.g. sphere, ray) \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types + \param query the intersecting primitive. \param output output iterator. + + \return the output iterator after writing */ template OutputIterator intersected_nodes(const Query& query, OutputIterator output) const { @@ -585,6 +620,10 @@ class Orthtree { Trees may be considered equivalent even if they have different contents. Equivalent trees must have the same root bounding box and the same node structure. + + \param rhs the other orthtree + + \return boolean, True if the trees have the same topology */ bool operator==(const Self& rhs) const { @@ -602,6 +641,10 @@ class Orthtree { /*! \brief compares the topology of the orthtree with that of `rhs`. + + \param rhs the other orthtree + + \return boolean, False if the trees have the same topology */ bool operator!=(const Self& rhs) const { return !operator==(rhs); @@ -613,63 +656,79 @@ class Orthtree { /// @{ /*! - * \brief Determines whether the node specified by index `n` is a leaf node. - * - * @param n index of the node to check. - * @return true of the node is a leaf, false otherwise. + \brief Determines whether the node specified by index `n` is a leaf node. + + \param n index of the node to check. + + \return true of the node is a leaf, false otherwise. */ bool is_leaf(Node_index n) const { return !m_node_children[n].has_value(); } /*! - * \brief Determines whether the node specified by index `n` is a root node. - * - * @param n index of the node to check. - * @return true of the node is a root, false otherwise. + \brief Determines whether the node specified by index `n` is a root node. + + \param n index of the node to check. + + \return True of the node is a root, False otherwise. */ bool is_root(Node_index n) const { return n == 0; } /*! - * \brief Determines the depth of the node specified. - * - * The root node has depth 0, its children have depth 1, and so on. - * - * @param n index of the node to check. - * @return the depth of the node within its tree. + \brief Determines the depth of the node specified. + + The root node has depth 0, its children have depth 1, and so on. + + \param n index of the node to check. + + \return the depth of the node n within its tree. */ std::size_t depth(Node_index n) const { return m_node_depths[n]; } /*! - * \brief Retrieves a reference to the Node_data associated with the node specified by `n`. - * - * @param n index of the node to retrieve data for. - * @return the data associated with the node. + \brief Retrieves a reference to the Node_data associated with the node specified by `n`. + + \param n index of the node to retrieve the contents of. + + \return a reference to the data associated with the node. */ Node_data& data(Node_index n) { return m_node_contents[n]; } /*! - * \brief const version of `data()` + \brief const version of `data()` + + \param n index of the node to retrieve the contents of. + + \return a const reference to the data associated with the node. */ const Node_data& data(Node_index n) const { return m_node_contents[n]; } /*! - * \brief Retrieves the global coordinates of the node. + \brief Retrieves the global coordinates of the node. + + \param n index of the node to retrieve the coordinates of. + + \return the global coordinates of the node within the tree */ Global_coordinates global_coordinates(Node_index n) const { return m_node_coordinates[n]; } /*! - * \brief Retrieves the local coordinates of the node. + \brief Retrieves the local coordinates of the node. + + \param n index of the node to retrieve the coordinates of. + + \return the local coordinates of the node within the tree */ Local_coordinates local_coordinates(Node_index n) const { Local_coordinates result; @@ -679,35 +738,46 @@ class Orthtree { } /*! - \brief returns this node's parent. + \brief returns this n's parent. + \pre `!is_root()` + + \param n index of the node to retrieve the parent of + + \return the index of the parent of node n */ - Node_index parent(Node_index node) const { - CGAL_precondition (!is_root(node)); - return *m_node_parents[node]; + Node_index parent(Node_index n) const { + CGAL_precondition (!is_root(n)); + return *m_node_parents[n]; } /*! - \brief returns this node's `i`th child. + \brief returns a node's `i`th child. + \pre `!is_leaf()` + + \param n index of the node to retrieve the child of + + \return the index of the `i`th child of node n */ - Node_index child(Node_index node, std::size_t i) const { - CGAL_precondition (!is_leaf(node)); - return *m_node_children[node] + i; + Node_index child(Node_index n, std::size_t i) const { + CGAL_precondition (!is_leaf(n)); + return *m_node_children[n] + i; } /*! - * \brief Retrieves an arbitrary descendant of the node specified by `node`. - * - * Convenience function to avoid the need to call `orthtree.child(orthtree.child(node, 0), 1)`. - * - * Each index in `indices` specifies which child to enter as descending the tree from `node` down. - * Indices are evaluated in the order they appear as parameters, so - * `descendant(root, 0, 1)` returns the second child of the first child of the root. - * - * @param node the node to descend - * @param indices the descent to perform - * @return the index of the specified descendant node. + \brief Retrieves an arbitrary descendant of the node specified by `node`. + + Convenience function to avoid the need to call `orthtree.child(orthtree.child(node, 0), 1)`. + + Each index in `indices` specifies which child to enter as descending the tree from `node` down. + Indices are evaluated in the order they appear as parameters, so + `descendant(root, 0, 1)` returns the second child of the first child of the root. + + \param node the node to descend + \param indices the integer indices specifying the descent to perform + + \return the index of the specified descendant node */ template Node_index descendant(Node_index node, Indices... indices) { @@ -715,7 +785,13 @@ class Orthtree { } /*! - * \brief Convenience function for retrieving arbitrary nodes, equivalent to `tree.descendant(tree.root(), indices...)`. + \brief Convenience function for retrieving arbitrary nodes. + + Equivalent to `tree.descendant(tree.root(), indices...)`. + + \param indices the integer indices specifying the descent to perform, starting from root + + \return the index of the specified node */ template Node_index node(Indices... indices) { @@ -723,12 +799,14 @@ class Orthtree { } /*! - * \brief Finds the next sibling in the parent of the node specified by the index `n`. - * - * Traverses the tree in increasing order of local index (e.g. 000, 001, 010, etc.) - * - * @param n the node to find the sibling of. - * @return the next sibling of `n` if `n` is not the last node in its parent, otherwise nothing. + \brief Finds the next sibling in the parent of the node specified by the index `n`. + + Traverses the tree in increasing order of local index (e.g. 000, 001, 010, etc.) + + \param n the index of the node to find the sibling of + + \return the index of the next sibling of n + if n is not the last node in its parent, otherwise nothing. */ const Maybe_node_index next_sibling(Node_index n) const { @@ -747,10 +825,12 @@ class Orthtree { } /*! - * \brief Finds the next sibling of the parent of the node specified by `n` if it exists. - * - * @param n the node to find the sibling up of. - * @return The next sibling of the parent of `n` if `n` is not the root and its parent has a sibling, otherwise nothing. + \brief Finds the next sibling of the parent of the node specified by `n` if it exists. + + \param n the index node to find the sibling up of. + + \return The index of the next sibling of the parent of n + if n is not the root and its parent has a sibling, otherwise nothing. */ const Maybe_node_index next_sibling_up(Node_index n) const { @@ -769,12 +849,13 @@ class Orthtree { } /*! - * \brief Finds the leaf node reached when descending the tree and always choosing child 0. - * - * This is the starting point of a depth-first traversal. - * - * @param n the node to find the deepest first child of. - * @return the index of the deepest first child. + \brief Finds the leaf node reached when descending the tree and always choosing child 0. + + This is the starting point of a depth-first traversal. + + \param n the index of the node to find the deepest first child of. + + \return the index of the deepest first child of node n. */ Node_index deepest_first_child(Node_index n) const { @@ -786,13 +867,14 @@ class Orthtree { } /*! - * \brief Finds node reached when descending the tree to a depth `d` and always choosing child 0. - * - * Similar to `deepest_first_child`, but does not go to a fixed depth. - * - * @param n the node to find the `d`th first child of. - * @param d the depth to descend to. - * @return the index of the `d`th first child, nothing if the tree is not deep enough. + \brief Finds node reached when descending the tree to a depth `d` and always choosing child 0. + + Similar to `deepest_first_child`, but does not go to a fixed depth. + + \param n the index of the node to find the `d`th first child of. + \param d the depth to descend to. + + \return the index of the `d`th first child, nothing if the tree is not deep enough. */ Maybe_node_index first_child_at_depth(Node_index n, std::size_t d) const { @@ -815,13 +897,15 @@ class Orthtree { } /*! - \brief splits the node into subnodes. + \brief splits a node into subnodes. - Only leaf nodes should be split. - When a node is split it is no longer a leaf node. - A number of `Degree::value` children are constructed automatically, and their values are set. - Contents of this node are _not_ propagated automatically, this is responsibility of the - `distribute_node_contents_object` in the Traits class. + Only leaf nodes should be split. + When a node is split it is no longer a leaf node. + A number of `Degree::value` children are constructed automatically, and their values are set. + Contents of this node are _not_ propagated automatically, this is responsibility of the + `distribute_node_contents_object` in the Traits class. + + \param n index of the node to split */ void split(Node_index n) { @@ -863,22 +947,11 @@ class Orthtree { } /*! - * \brief eliminates this node's children, making it a leaf node. + * \brief Finds the center point of a node. * - * When a node is un-split, its children are automatically deleted. - * After un-splitting a node it will be considered a leaf node. - * Idempotent, un-splitting a leaf node has no effect. - */ - void unsplit(Node_index n) { - // todo: the child nodes should be de-allocated! - // This may need to be done recursively - } - - /*! - * \brief Finds the center point of the node specified by `n`. + * @param n index of the node to find the center point for * - * @param n The node to find the center point for. - * @return the center point of node `n`. + * @return the center point of node n */ Point barycenter(Node_index n) const { @@ -898,13 +971,14 @@ class Orthtree { } /*! - * \brief Determines whether a pair of subtrees have the same topology. - * - * @param lhsNode a node in lhsTree - * @param lhsTree an Orthtree - * @param rhsNode a node in rhsTree - * @param rhsTree another Orthtree - * @return true if lhsNode and rhsNode have the same topology, false otherwise + \brief Determines whether a pair of subtrees have the same topology. + + \param lhsNode index of a node in lhsTree + \param lhsTree an Orthtree + \param rhsNode index of a node in rhsTree + \param rhsTree another Orthtree + + @return true if lhsNode and rhsNode have the same topology, false otherwise */ static bool is_topology_equal(Node_index lhsNode, const Self& lhsTree, Node_index rhsNode, const Self& rhsTree) { @@ -928,7 +1002,12 @@ class Orthtree { } /*! - * \brief Helper function for calling `is_topology_equal` on the root nodes of two trees. + \brief Helper function for calling `is_topology_equal` on the root nodes of two trees. + + \param lhsTree an Orthtree + \param rhsTree another Orthtree + + \return True if lhsTree and rhsTree have the same topology */ static bool is_topology_equal(const Self& lhs, const Self& rhs) { return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); @@ -937,7 +1016,6 @@ class Orthtree { /*! \brief finds the directly adjacent node in a specific direction - \pre `!is_null()` \pre `direction.to_ulong < 2 * Dimension::value` Adjacent nodes are found according to several properties: @@ -978,6 +1056,7 @@ class Orthtree { there is no adjacent node in that direction, it returns a null node. + \param n index of the node to find a neighbor of \param direction which way to find the adjacent node relative to this one. Each successive bit selects the direction for the corresponding dimension: for an Octree in 3D, 010 means: negative @@ -1030,6 +1109,9 @@ class Orthtree { /*! \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. + + \param n index of the node to find a neighbor of + \param direction which way to find the adjacent node relative to this one */ Maybe_node_index adjacent_node(Node_index n, Adjacency adjacency) const { return adjacent_node(n, std::bitset(static_cast(adjacency))); From 047a9494bde833678915f3b897ca88c429454d7e Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 30 Sep 2023 09:24:20 +0200 Subject: [PATCH 260/520] Fix mismatched parameter names in documentation --- Orthtree/include/CGAL/Orthtree.h | 7 ++++--- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2c0de4864f85..cd59162d91f0 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -757,6 +757,7 @@ class Orthtree { \pre `!is_leaf()` \param n index of the node to retrieve the child of + \param i in [0, 2^D) specifying the child to retrieve \return the index of the `i`th child of node n */ @@ -1004,8 +1005,8 @@ class Orthtree { /*! \brief Helper function for calling `is_topology_equal` on the root nodes of two trees. - \param lhsTree an Orthtree - \param rhsTree another Orthtree + \param lhs an Orthtree + \param rhs another Orthtree \return True if lhsTree and rhsTree have the same topology */ @@ -1111,7 +1112,7 @@ class Orthtree { \brief equivalent to `adjacent_node()`, with an adjacency direction rather than a bitset. \param n index of the node to find a neighbor of - \param direction which way to find the adjacent node relative to this one + \param adjacency which way to find the adjacent node relative to this one */ Maybe_node_index adjacent_node(Node_index n, Adjacency adjacency) const { return adjacent_node(n, std::bitset(static_cast(adjacency))); diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 190a49fd70f0..270ac53179c9 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -129,6 +129,7 @@ namespace Orthtrees { \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits \tparam OutputIterator must be a model of `OutputIterator` that accepts points + \param orthtree the tree to search within \param query_sphere the region to search within \param k the number of points to find @@ -172,6 +173,7 @@ OutputIterator nearest_k_neighbors_in_radius( \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. + \param orthtree the tree to search within \param query query point. \param k number of neighbors. @@ -193,6 +195,7 @@ OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Poin \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. + \param orthtree the tree to search within \param query query sphere. \param output output iterator. From feb87c737ae15e99d9a7f37609516f9a27bf23b3 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 5 Oct 2023 10:24:38 +0200 Subject: [PATCH 261/520] Eliminate most whitespace changes --- .../include/CGAL/Surface_mesh/Surface_mesh.h | 2853 +++++++++-------- 1 file changed, 1475 insertions(+), 1378 deletions(-) diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 9b0acad0868f..86713e830d11 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -59,7 +59,7 @@ namespace CGAL { class SM_Index { public: - typedef std::uint32_t size_type; + typedef std::uint32_t size_type; /// Constructor. %Default construction creates an invalid index. /// We write -1, which is /// (std::numeric_limits::max)() @@ -341,7 +341,6 @@ class Surface_mesh public: #ifndef DOXYGEN_RUNNING - template using Property_container = Properties::Property_container; @@ -353,299 +352,311 @@ class Surface_mesh #endif // DOXYGEN_RUNNING - /// \name Basic Types - /// - ///@{ + /// \name Basic Types + /// + ///@{ - /// The point type. - typedef P Point; + /// The point type. + typedef P Point; - /// The type used to represent an index. - typedef std::uint32_t size_type; + /// The type used to represent an index. + typedef std::uint32_t size_type; - ///@} + ///@} - /// \name Basic Elements - /// - ///@{ + /// \name Basic Elements + /// + ///@{ #ifdef DOXYGEN_RUNNING - /// This class represents a vertex. - /// \cgalModels{Index,LessThanComparable,Hashable} - /// \sa `Halfedge_index`, `Edge_index`, `Face_index` - class Vertex_index - { - public: - /// %Default constructor. - Vertex_index(){} + /// This class represents a vertex. + /// \cgalModels{Index,LessThanComparable,Hashable} + /// \sa `Halfedge_index`, `Edge_index`, `Face_index` + class Vertex_index + { + public: + /// %Default constructor. + Vertex_index(){} - Vertex_index(size_type _idx){} + Vertex_index(size_type _idx){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Vertex_index const& v) - {} - }; + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Vertex_index const& v) + {} + }; #else typedef SM_Vertex_index Vertex_index; #endif #ifdef DOXYGEN_RUNNING - /// This class represents a halfedge. - /// \cgalModels{Index,LessThanComparable,Hashable} - /// \sa `Vertex_index`, `Edge_index`, `Face_index` - class Halfedge_index - { - public: - /// %Default constructor - Halfedge_index(){} + /// This class represents a halfedge. + /// \cgalModels{Index,LessThanComparable,Hashable} + /// \sa `Vertex_index`, `Edge_index`, `Face_index` + class Halfedge_index + { + public: + /// %Default constructor + Halfedge_index(){} - Halfedge_index(size_type _idx){} + Halfedge_index(size_type _idx){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Halfedge_index const& h) - { - } + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Halfedge_index const& h) + { + } - }; + }; #else typedef SM_Halfedge_index Halfedge_index; #endif #ifdef DOXYGEN_RUNNING - /// This class represents a face - /// \cgalModels{Index,LessThanComparable,Hashable} - /// \sa `Vertex_index`, `Halfedge_index`, `Edge_index` - class Face_index - { - public: - /// %Default constructor - Face_index(){} + /// This class represents a face + /// \cgalModels{Index,LessThanComparable,Hashable} + /// \sa `Vertex_index`, `Halfedge_index`, `Edge_index` + class Face_index + { + public: + /// %Default constructor + Face_index(){} - Face_index(size_type _idx){} + Face_index(size_type _idx){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Face_index const& f) - {} - }; + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Face_index const& f) + {} + }; #else typedef SM_Face_index Face_index; #endif #ifdef DOXYGEN_RUNNING - /// This class represents an edge. - /// \cgalModels{Index,LessThanComparable,Hashable} - /// \sa `Vertex_index`, `Halfedge_index`, `Face_index` - class Edge_index - { - public: - /// %Default constructor - Edge_index(){} + /// This class represents an edge. + /// \cgalModels{Index,LessThanComparable,Hashable} + /// \sa `Vertex_index`, `Halfedge_index`, `Face_index` + class Edge_index + { + public: + /// %Default constructor + Edge_index(){} - Edge_index(size_type idx){} + Edge_index(size_type idx){} - /// constructs an `Edge_index` from a halfedge. - Edge_index(Halfedge_index he){} + /// constructs an `Edge_index` from a halfedge. + Edge_index(Halfedge_index he){} - /// prints the index and a short identification string to an ostream. - friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Edge_index const& e) - {} - }; + /// prints the index and a short identification string to an ostream. + friend std::ostream& operator<<(std::ostream& os, typename Surface_mesh::Edge_index const& e) + {} + }; #else typedef SM_Edge_index Edge_index; #endif - ///@} + ///@} #ifndef CGAL_TEST_SURFACE_MESH private: //-------------------------------------------------- connectivity types #endif - /// This type stores the vertex connectivity - /// \sa `Halfedge_connectivity`, `Face_connectivity` - struct Vertex_connectivity { - /// an incoming halfedge per vertex (it will be a border halfedge for border vertices) - Halfedge_index halfedge_; - }; + /// This type stores the vertex connectivity + /// \sa `Halfedge_connectivity`, `Face_connectivity` + struct Vertex_connectivity + { + /// an incoming halfedge per vertex (it will be a border halfedge for border vertices) + Halfedge_index halfedge_; + }; - /// This type stores the halfedge connectivity - /// \sa `Vertex_connectivity`, `Face_connectivity` - struct Halfedge_connectivity { - /// face incident to halfedge - Face_index face_; - /// vertex the halfedge points to - Vertex_index vertex_; - /// next halfedge within a face (or along a border) - Halfedge_index next_halfedge_; - /// previous halfedge within a face (or along a border) - Halfedge_index prev_halfedge_; - }; + /// This type stores the halfedge connectivity + /// \sa `Vertex_connectivity`, `Face_connectivity` + struct Halfedge_connectivity + { + /// face incident to halfedge + Face_index face_; + /// vertex the halfedge points to + Vertex_index vertex_; + /// next halfedge within a face (or along a border) + Halfedge_index next_halfedge_; + /// previous halfedge within a face (or along a border) + Halfedge_index prev_halfedge_; + }; - /// This type stores the face connectivity - /// \sa `Vertex_connectivity`, `Halfedge_connectivity` - struct Face_connectivity { - /// a halfedge that is part of the face - Halfedge_index halfedge_; - }; + /// This type stores the face connectivity + /// \sa `Vertex_connectivity`, `Halfedge_connectivity` + struct Face_connectivity + { + /// a halfedge that is part of the face + Halfedge_index halfedge_; + }; private: //------------------------------------------------------ iterator types - template - class Index_iterator - : public boost::iterator_facade, - Index_, - std::random_access_iterator_tag, - Index_ - > { - typedef boost::iterator_facade, - Index_, - std::random_access_iterator_tag, - Index_> Facade; - public: - Index_iterator() : hnd_(), mesh_(nullptr) {} - - Index_iterator(const Index_& h, const Surface_mesh* m) - : hnd_(h), mesh_(m) { - if (mesh_ && mesh_->has_garbage()) { - while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; - } - } - - private: - friend class boost::iterator_core_access; - - void increment() { - ++hnd_; - CGAL_assertion(mesh_ != nullptr); - - if (mesh_->has_garbage()) - while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; - } - - void decrement() { - --hnd_; - CGAL_assertion(mesh_ != nullptr); - if (mesh_->has_garbage()) - while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) --hnd_; - } - - void advance(std::ptrdiff_t n) { - CGAL_assertion(mesh_ != nullptr); - - if (mesh_->has_garbage()) { - if (n > 0) - for (std::ptrdiff_t i = 0; i < n; ++i) - increment(); - else - for (std::ptrdiff_t i = 0; i < -n; ++i) - decrement(); - } else - hnd_ += n; - } + template + class Index_iterator + : public boost::iterator_facade< Index_iterator, + Index_, + std::random_access_iterator_tag, + Index_ + > + { + typedef boost::iterator_facade< Index_iterator, + Index_, + std::random_access_iterator_tag, + Index_> Facade; + public: + Index_iterator() : hnd_(), mesh_(nullptr) {} + Index_iterator(const Index_& h, const Surface_mesh* m) + : hnd_(h), mesh_(m) { + if (mesh_ && mesh_->has_garbage()){ + while (mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; + } + } + private: + friend class boost::iterator_core_access; + void increment() + { + ++hnd_; + CGAL_assertion(mesh_ != nullptr); - std::ptrdiff_t distance_to(const Index_iterator& other) const { - if (mesh_->has_garbage()) { - bool forward = (other.hnd_ > hnd_); + if(mesh_->has_garbage()) + while ( mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) ++hnd_; + } - std::ptrdiff_t out = 0; - Index_iterator it = *this; - while (!it.equal(other)) { - if (forward) { - ++it; - ++out; - } else { - --it; - --out; - } + void decrement() + { + --hnd_; + CGAL_assertion(mesh_ != nullptr); + if(mesh_->has_garbage()) + while ( mesh_->has_valid_index(hnd_) && mesh_->is_removed(hnd_)) --hnd_; } - return out; - } - // else - return std::ptrdiff_t(other.hnd_) - std::ptrdiff_t(this->hnd_); - } + void advance(std::ptrdiff_t n) + { + CGAL_assertion(mesh_ != nullptr); + + if (mesh_->has_garbage()) + { + if (n > 0) + for (std::ptrdiff_t i = 0; i < n; ++ i) + increment(); + else + for (std::ptrdiff_t i = 0; i < -n; ++ i) + decrement(); + } + else + hnd_ += n; + } - bool equal(const Index_iterator& other) const { - return this->hnd_ == other.hnd_; - } + std::ptrdiff_t distance_to(const Index_iterator& other) const + { + if (mesh_->has_garbage()) + { + bool forward = (other.hnd_ > hnd_); + + std::ptrdiff_t out = 0; + Index_iterator it = *this; + while (!it.equal(other)) + { + if (forward) + { + ++ it; + ++ out; + } + else + { + -- it; + -- out; + } + } + return out; + } + + // else + return std::ptrdiff_t(other.hnd_) - std::ptrdiff_t(this->hnd_); + } - Index_ dereference() const { return hnd_; } + bool equal(const Index_iterator& other) const + { + return this->hnd_ == other.hnd_; + } - Index_ hnd_; - const Surface_mesh* mesh_; + Index_ dereference() const { return hnd_; } - }; + Index_ hnd_; + const Surface_mesh* mesh_; + }; public: - /// \name Range Types - /// - /// Each range `R` in this section has a nested type `R::iterator`, - /// is convertible to `std::pair`, so that one can use `boost::tie()`, - /// and can be used with `BOOST_FOREACH()`, as well as with the C++11 range based for-loop. + /// \name Range Types + /// + /// Each range `R` in this section has a nested type `R::iterator`, + /// is convertible to `std::pair`, so that one can use `boost::tie()`, + /// and can be used with `BOOST_FOREACH()`, as well as with the C++11 range based for-loop. - ///@{ + ///@{ #ifndef DOXYGEN_RUNNING - typedef Index_iterator Vertex_iterator; + typedef Index_iterator Vertex_iterator; #endif - /// \brief The range over all vertex indices. - /// - /// A model of BidirectionalRange with value type `Vertex_index`. - /// \sa `vertices()` - /// \sa `Halfedge_range`, `Edge_range`, `Face_range` + /// \brief The range over all vertex indices. + /// + /// A model of BidirectionalRange with value type `Vertex_index`. + /// \sa `vertices()` + /// \sa `Halfedge_range`, `Edge_range`, `Face_range` #ifdef DOXYGEN_RUNNING - typedef unspecified_type Vertex_range; + typedef unspecified_type Vertex_range; #else - typedef Iterator_range Vertex_range; + typedef Iterator_range Vertex_range; #endif #ifndef DOXYGEN_RUNNING - typedef Index_iterator Halfedge_iterator; + typedef Index_iterator Halfedge_iterator; #endif - /// \brief The range over all halfedge indices. - /// - /// A model of BidirectionalRange with value type `Halfedge_index`. - /// \sa `halfedges()` - /// \sa `Vertex_range`, `Edge_range`, `Face_range` + /// \brief The range over all halfedge indices. + /// + /// A model of BidirectionalRange with value type `Halfedge_index`. + /// \sa `halfedges()` + /// \sa `Vertex_range`, `Edge_range`, `Face_range` #ifdef DOXYGEN_RUNNING - typedef unspecified_type Halfedge_range; + typedef unspecified_type Halfedge_range; #else - typedef Iterator_range Halfedge_range; + typedef Iterator_range Halfedge_range; #endif #ifndef DOXYGEN_RUNNING - typedef Index_iterator Edge_iterator; + typedef Index_iterator Edge_iterator; #endif - /// \brief The range over all edge indices. - /// - /// A model of BidirectionalRange with value type `Edge_index`. - /// \sa `edges()` - /// \sa `Halfedge_range`, `Vertex_range`, `Face_range` + /// \brief The range over all edge indices. + /// + /// A model of BidirectionalRange with value type `Edge_index`. + /// \sa `edges()` + /// \sa `Halfedge_range`, `Vertex_range`, `Face_range` #ifdef DOXYGEN_RUNNING - typedef unspecified_type Edge_range; + typedef unspecified_type Edge_range; #else - typedef Iterator_range Edge_range; + typedef Iterator_range Edge_range; #endif #ifndef DOXYGEN_RUNNING - typedef Index_iterator Face_iterator; + typedef Index_iterator Face_iterator; #endif - /// \brief The range over all face indices. - /// - /// A model of BidirectionalRange with value type `Face_index`. - /// \sa `faces()` - /// \sa `Vertex_range`, `Halfedge_range`, `Edge_range` -#ifdef DOXYGEN_RUNNING - typedef unspecified_type Face_range; + /// \brief The range over all face indices. + /// + /// A model of BidirectionalRange with value type `Face_index`. + /// \sa `faces()` + /// \sa `Vertex_range`, `Halfedge_range`, `Edge_range` + #ifdef DOXYGEN_RUNNING + typedef unspecified_type Face_range; #else - typedef Iterator_range Face_range; + typedef Iterator_range Face_range; #endif #ifndef DOXYGEN_RUNNING @@ -653,215 +664,229 @@ class Surface_mesh typedef CGAL::Vertex_around_target_iterator Vertex_around_target_iterator; typedef Iterator_range Vertex_around_target_range; - typedef CGAL::Halfedge_around_target_iterator Halfedge_around_target_iterator; + typedef CGAL::Halfedge_around_target_iterator Halfedge_around_target_iterator; typedef Iterator_range Halfedge_around_target_range; - typedef CGAL::Face_around_target_iterator Face_around_target_iterator; + typedef CGAL::Face_around_target_iterator Face_around_target_iterator; typedef Iterator_range Face_around_target_range; - typedef CGAL::Vertex_around_face_iterator Vertex_around_face_iterator; + typedef CGAL::Vertex_around_face_iterator Vertex_around_face_iterator; typedef Iterator_range Vertex_around_face_range; - typedef CGAL::Halfedge_around_face_iterator Halfedge_around_face_iterator; + typedef CGAL::Halfedge_around_face_iterator Halfedge_around_face_iterator; typedef Iterator_range Halfedge_around_face_range; - typedef CGAL::Face_around_face_iterator Face_around_face_iterator; + typedef CGAL::Face_around_face_iterator Face_around_face_iterator; typedef Iterator_range Face_around_face_range; #endif - /// @cond CGAL_BEGIN_END - /// Start iterator for vertices. - Vertex_iterator vertices_begin() const { - return Vertex_iterator(Vertex_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for vertices. + Vertex_iterator vertices_begin() const + { + return Vertex_iterator(Vertex_index(0), this); + } - /// End iterator for vertices. - Vertex_iterator vertices_end() const { - return Vertex_iterator(Vertex_index(number_of_vertices()), this); - } - /// @endcond + /// End iterator for vertices. + Vertex_iterator vertices_end() const + { + return Vertex_iterator(Vertex_index(number_of_vertices()), this); + } + /// @endcond - /// returns the iterator range of the vertices of the mesh. - Vertex_range vertices() const { - return make_range(vertices_begin(), vertices_end()); - } + /// returns the iterator range of the vertices of the mesh. + Vertex_range vertices() const { + return make_range(vertices_begin(), vertices_end()); + } - /// @cond CGAL_BEGIN_END - /// Start iterator for halfedges. - Halfedge_iterator halfedges_begin() const { - return Halfedge_iterator(Halfedge_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for halfedges. + Halfedge_iterator halfedges_begin() const + { + return Halfedge_iterator(Halfedge_index(0), this); + } - /// End iterator for halfedges. - Halfedge_iterator halfedges_end() const { - return Halfedge_iterator(Halfedge_index(number_of_halfedges()), this); - } - /// @endcond + /// End iterator for halfedges. + Halfedge_iterator halfedges_end() const + { + return Halfedge_iterator(Halfedge_index(number_of_halfedges()), this); + } + /// @endcond - /// returns the iterator range of the halfedges of the mesh. - Halfedge_range halfedges() const { - return make_range(halfedges_begin(), halfedges_end()); - } + /// returns the iterator range of the halfedges of the mesh. + Halfedge_range halfedges() const { + return make_range(halfedges_begin(), halfedges_end()); + } - /// @cond CGAL_BEGIN_END - /// Start iterator for edges. - Edge_iterator edges_begin() const { - return Edge_iterator(Edge_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for edges. + Edge_iterator edges_begin() const + { + return Edge_iterator(Edge_index(0), this); + } - /// End iterator for edges. - Edge_iterator edges_end() const { - return Edge_iterator(Edge_index(number_of_edges()), this); - } - /// @endcond + /// End iterator for edges. + Edge_iterator edges_end() const + { + return Edge_iterator(Edge_index(number_of_edges()), this); + } + /// @endcond - /// returns the iterator range of the edges of the mesh. - Edge_range edges() const { - return make_range(edges_begin(), edges_end()); - } + /// returns the iterator range of the edges of the mesh. + Edge_range edges() const + { + return make_range(edges_begin(), edges_end()); + } - /// @cond CGAL_BEGIN_END - /// Start iterator for faces. - Face_iterator faces_begin() const { - return Face_iterator(Face_index(0), this); - } + /// @cond CGAL_BEGIN_END + /// Start iterator for faces. + Face_iterator faces_begin() const + { + return Face_iterator(Face_index(0), this); + } - /// End iterator for faces. - Face_iterator faces_end() const { - return Face_iterator(Face_index(number_of_faces()), this); - } - /// @endcond + /// End iterator for faces. + Face_iterator faces_end() const + { + return Face_iterator(Face_index(number_of_faces()), this); + } + /// @endcond - /// returns the iterator range of the faces of the mesh. - Face_range faces() const { - return make_range(faces_begin(), faces_end()); - } + /// returns the iterator range of the faces of the mesh. + Face_range faces() const { + return make_range(faces_begin(), faces_end()); + } #ifndef DOXYGEN_RUNNING + /// returns the iterator range for vertices around vertex `target(h)`, starting at `source(h)`. + Vertex_around_target_range vertices_around_target(Halfedge_index h) const + { + return CGAL::vertices_around_target(h,*this); + } - /// returns the iterator range for vertices around vertex `target(h)`, starting at `source(h)`. - Vertex_around_target_range vertices_around_target(Halfedge_index h) const { - return CGAL::vertices_around_target(h, *this); - } - - /// returns the iterator range for incoming halfedges around vertex `target(h)`, starting at `h`. - Halfedge_around_target_range halfedges_around_target(Halfedge_index h) const { - return CGAL::halfedges_around_target(h, *this); - } + /// returns the iterator range for incoming halfedges around vertex `target(h)`, starting at `h`. + Halfedge_around_target_range halfedges_around_target(Halfedge_index h) const + { + return CGAL::halfedges_around_target(h,*this); + } - /// returns the iterator range for faces around vertex `target(h)`, starting at `face(h)`. - Face_around_target_range faces_around_target(Halfedge_index h) const { - return CGAL::faces_around_target(h, *this); - } + /// returns the iterator range for faces around vertex `target(h)`, starting at `face(h)`. + Face_around_target_range faces_around_target(Halfedge_index h) const + { + return CGAL::faces_around_target(h,*this); + } - /// returns the iterator range for vertices around face `face(h)`, starting at `target(h)`. - Vertex_around_face_range vertices_around_face(Halfedge_index h) const { - return CGAL::vertices_around_face(h, *this); - } + /// returns the iterator range for vertices around face `face(h)`, starting at `target(h)`. + Vertex_around_face_range vertices_around_face(Halfedge_index h) const + { + return CGAL::vertices_around_face(h,*this); + } - /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. - Halfedge_around_face_range halfedges_around_face(Halfedge_index h) const { - return CGAL::halfedges_around_face(h, *this); - } + /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. + Halfedge_around_face_range halfedges_around_face(Halfedge_index h) const + { + return CGAL::halfedges_around_face(h,*this); + } - /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. - Face_around_face_range faces_around_face(Halfedge_index h) const { - return CGAL::faces_around_face(h, *this); - } + /// returns the iterator range for halfedges around face `face(h)`, starting at `h`. + Face_around_face_range faces_around_face(Halfedge_index h) const + { + return CGAL::faces_around_face(h,*this); + } #endif - ///@} + ///@} public: #ifndef DOXYGEN_RUNNING - /// \name Circulator Types - /// - /// The following circulators enable to iterate through the elements around a face or vertex. - /// As explained in the \ref SurfaceMeshOrientation "User Manual", we can speak of a - /// *clockwise* or *counterclockwise* - /// traversal, by looking at the surface from the right side. - ///@{ + /// \name Circulator Types + /// + /// The following circulators enable to iterate through the elements around a face or vertex. + /// As explained in the \ref SurfaceMeshOrientation "User Manual", we can speak of a + /// *clockwise* or *counterclockwise* + /// traversal, by looking at the surface from the right side. + ///@{ - /// \brief This class circulates clockwise through all - /// one-ring neighbors of a vertex. - /// A model of `BidirectionalCirculator` with value type `Vertex_index`. - /// \sa `Halfedge_around_target_circulator`, `Face_around_target_circulator` + /// \brief This class circulates clockwise through all + /// one-ring neighbors of a vertex. + /// A model of `BidirectionalCirculator` with value type `Vertex_index`. + /// \sa `Halfedge_around_target_circulator`, `Face_around_target_circulator` typedef CGAL::Vertex_around_target_circulator Vertex_around_target_circulator; - /// \brief This class circulates clockwise through all incident faces of a vertex. - /// A model of `BidirectionalCirculator` with value type `Face_index`. - /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` + /// \brief This class circulates clockwise through all incident faces of a vertex. + /// A model of `BidirectionalCirculator` with value type `Face_index`. + /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` typedef CGAL::Face_around_target_circulator Face_around_target_circulator; - /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as target. - /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. - /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` + /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as target. + /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. + /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` typedef CGAL::Halfedge_around_target_circulator Halfedge_around_target_circulator; - /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as source. - /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. - /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` + /// \brief This class circulates clockwise through all halfedges around a vertex that have this vertex as source. + /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. + /// \sa `Vertex_around_target_circulator`, `Halfedge_around_target_circulator` typedef CGAL::Halfedge_around_source_circulator Halfedge_around_source_circulator; - /// \brief This class circulates counterclockwise through all vertices around a face. - /// A model of `BidirectionalCirculator` with value type `Vertex_index`. + /// \brief This class circulates counterclockwise through all vertices around a face. + /// A model of `BidirectionalCirculator` with value type `Vertex_index`. - typedef CGAL::Vertex_around_face_circulator Vertex_around_face_circulator; + typedef CGAL::Vertex_around_face_circulator Vertex_around_face_circulator; - /// \brief This class circulates counterclockwise through all halfedges around a face. - /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. + /// \brief This class circulates counterclockwise through all halfedges around a face. + /// A model of `BidirectionalCirculator` with value type `Halfedge_index`. - typedef CGAL::Halfedge_around_face_circulator Halfedge_around_face_circulator; + typedef CGAL::Halfedge_around_face_circulator Halfedge_around_face_circulator; - /// \brief This class circulates counterclockwise through all faces around a face. - /// A model of `BidirectionalCirculator` with value type `Face_index`. - /// Note that the face index is the same after `operator++`, if the neighboring faces share - /// several halfedges. + /// \brief This class circulates counterclockwise through all faces around a face. + /// A model of `BidirectionalCirculator` with value type `Face_index`. + /// Note that the face index is the same after `operator++`, if the neighboring faces share + /// several halfedges. - typedef CGAL::Face_around_face_circulator Face_around_face_circulator; + typedef CGAL::Face_around_face_circulator Face_around_face_circulator; /// @} #endif /// @cond CGAL_DOCUMENT_INTERNALS // typedefs which make it easier to write the partial specialisation of boost::graph_traits - typedef Vertex_index vertex_index; - typedef P vertex_property_type; + typedef Vertex_index vertex_index; + typedef P vertex_property_type; typedef Halfedge_index halfedge_index; - typedef Edge_index edge_index; - typedef Face_index face_index; + typedef Edge_index edge_index; + typedef Face_index face_index; - typedef Vertex_iterator vertex_iterator; - typedef Halfedge_iterator halfedge_iterator; - typedef Edge_iterator edge_iterator; - typedef Face_iterator face_iterator; - typedef CGAL::Out_edge_iterator out_edge_iterator; + typedef Vertex_iterator vertex_iterator; + typedef Halfedge_iterator halfedge_iterator; + typedef Edge_iterator edge_iterator; + typedef Face_iterator face_iterator; + typedef CGAL::Out_edge_iterator out_edge_iterator; - typedef boost::undirected_tag directed_category; + typedef boost::undirected_tag directed_category; typedef boost::disallow_parallel_edge_tag edge_parallel_category; struct traversal_category : public virtual boost::bidirectional_graph_tag, public virtual boost::vertex_list_graph_tag, - public virtual boost::edge_list_graph_tag { - }; + public virtual boost::edge_list_graph_tag + {}; typedef size_type vertices_size_type; typedef size_type halfedges_size_type; @@ -869,288 +894,297 @@ class Surface_mesh typedef size_type faces_size_type; typedef size_type degree_size_type; - /// @endcond + /// @endcond public: - /// \name Construction, Destruction, Assignment - /// - /// Copy constructors as well as assignment do also copy simplices marked as removed. - ///@{ - - /// %Default constructor. - Surface_mesh() : - vconn_(vprops_.add_property("v:connectivity")), - hconn_(hprops_.add_property("h:connectivity")), - fconn_(fprops_.add_property("f:connectivity")), - vpoint_(vprops_.add_property("v:point")), - anonymous_property_(0) {} - - /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. - Surface_mesh(const Surface_mesh& rhs) : - vprops_(rhs.vprops_), - hprops_(rhs.hprops_), - fprops_(rhs.fprops_), - eprops_(rhs.eprops_), - vconn_(vprops_.get_property("v:connectivity")), - vpoint_(vprops_.get_property("v:point")), - hconn_(hprops_.get_property("h:connectivity")), - fconn_(fprops_.get_property("f:connectivity")), - anonymous_property_(0) {} - - /// Move constructor. - Surface_mesh(Surface_mesh&& sm) : - vprops_(std::move(sm.vprops_)), - hprops_(std::move(sm.hprops_)), - eprops_(std::move(sm.eprops_)), - fprops_(std::move(sm.fprops_)), - vconn_(vprops_.get_property("v:connectivity")), - vpoint_(vprops_.get_property("v:point")), - hconn_(hprops_.get_property("h:connectivity")), - fconn_(fprops_.get_property("f:connectivity")), - anonymous_property_(0) {} - - /// assigns `rhs` to `*this`. Performs a deep copy of all properties. - Surface_mesh& operator=(const Surface_mesh& rhs); - - /// move assignment - Surface_mesh& operator=(Surface_mesh&& sm) { - vprops_ = std::move(sm.vprops_); - hprops_ = std::move(sm.hprops_); - eprops_ = std::move(sm.eprops_); - fprops_ = std::move(sm.fprops_); - return *this; - } - - /// assigns `rhs` to `*this`. Does not copy custom properties. - //Surface_mesh& assign(const Surface_mesh& rhs); + /// \name Construction, Destruction, Assignment + /// + /// Copy constructors as well as assignment do also copy simplices marked as removed. + ///@{ + + /// %Default constructor. + Surface_mesh() : + vconn_(vprops_.add_property("v:connectivity")), + hconn_(hprops_.add_property("h:connectivity")), + fconn_(fprops_.add_property("f:connectivity")), + vpoint_(vprops_.add_property("v:point")), + anonymous_property_(0) {} + + /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. + Surface_mesh(const Surface_mesh& rhs) : + vprops_(rhs.vprops_), + hprops_(rhs.hprops_), + fprops_(rhs.fprops_), + eprops_(rhs.eprops_), + vconn_(vprops_.get_property("v:connectivity")), + vpoint_(vprops_.get_property("v:point")), + hconn_(hprops_.get_property("h:connectivity")), + fconn_(fprops_.get_property("f:connectivity")), + anonymous_property_(0) {} + + /// Move constructor. + Surface_mesh(Surface_mesh&& sm) : + vprops_(std::move(sm.vprops_)), + hprops_(std::move(sm.hprops_)), + eprops_(std::move(sm.eprops_)), + fprops_(std::move(sm.fprops_)), + vconn_(vprops_.get_property("v:connectivity")), + vpoint_(vprops_.get_property("v:point")), + hconn_(hprops_.get_property("h:connectivity")), + fconn_(fprops_.get_property("f:connectivity")), + anonymous_property_(0) {} + + /// assigns `rhs` to `*this`. Performs a deep copy of all properties. + Surface_mesh& operator=(const Surface_mesh& rhs); + + /// move assignment + Surface_mesh& operator=(Surface_mesh&& sm) + { + vprops_ = std::move(sm.vprops_); + hprops_ = std::move(sm.hprops_); + eprops_ = std::move(sm.eprops_); + fprops_ = std::move(sm.fprops_); + return *this; + } - ///@} + ///@} public: - /// \name Adding Vertices, Edges, and Faces - ///@{ + /// \name Adding Vertices, Edges, and Faces + ///@{ - /// adds a new vertex, and resizes vertex properties if necessary. - Vertex_index add_vertex() { - if (recycle_) - return vprops_.emplace(); - else - return vprops_.emplace_back(); - } + /// adds a new vertex, and resizes vertex properties if necessary. + Vertex_index add_vertex() + { + if(recycle_) + return vprops_.emplace(); + else + return vprops_.emplace_back(); + } + + /// adds a new vertex, resizes vertex properties if necessary, + /// and sets the \em point property to `p`. + /// \note Several vertices may have the same point property. + Vertex_index add_vertex(const Point& p) + { + Vertex_index v = add_vertex(); + vpoint_[v] = p; + return v; + } - /// adds a new vertex, resizes vertex properties if necessary, - /// and sets the \em point property to `p`. - /// \note Several vertices may have the same point property. - Vertex_index add_vertex(const Point& p) { - Vertex_index v = add_vertex(); - vpoint_[v] = p; - return v; - } public: - /// adds a new edge, and resizes edge and halfedge properties if necessary. - Halfedge_index add_edge() { - - // Add properties for a new edge - if (recycle_) - eprops_.emplace(); - else - eprops_.emplace_back(); - - // Add properties for a pair of new half-edges - // The new half-edges are placed adjacently, and we return the index of the first - if (recycle_) - return hprops_.emplace_group(2); - else - return hprops_.emplace_group_back(2); - } + /// adds a new edge, and resizes edge and halfedge properties if necessary. + Halfedge_index add_edge() + { - /// adds two opposite halfedges, and resizes edge and halfedge properties if necessary. - /// Sets the targets of the halfedge to the given vertices, but does not modify the halfedge - /// associated to the vertices. - /// \note The function does not check whether there is already an edge between the vertices. - /// \returns the halfedge with `v1` as target + // Add properties for a new edge + if (recycle_) + eprops_.emplace(); + else + eprops_.emplace_back(); + + // Add properties for a pair of new half-edges + // The new half-edges are placed adjacently, and we return the index of the first + if (recycle_) + return hprops_.emplace_group(2); + else + return hprops_.emplace_group_back(2); + } - Halfedge_index add_edge(Vertex_index v0, Vertex_index v1) { - CGAL_assertion(v0 != v1); - Halfedge_index h = add_edge(); + /// adds two opposite halfedges, and resizes edge and halfedge properties if necessary. + /// Sets the targets of the halfedge to the given vertices, but does not modify the halfedge + /// associated to the vertices. + /// \note The function does not check whether there is already an edge between the vertices. + /// \returns the halfedge with `v1` as target - set_target(h, v1); - set_target(opposite(h), v0); + Halfedge_index add_edge(Vertex_index v0, Vertex_index v1) + { + CGAL_assertion(v0 != v1); + Halfedge_index h = add_edge(); - return h; - } + set_target(h, v1); + set_target(opposite(h), v0); - /// adds a new face, and resizes face properties if necessary. - Face_index add_face() { - if (recycle_) - return fprops_.emplace(); - else - return fprops_.emplace_back(); - } + return h; + } - /// if possible, adds a new face with vertices from a range with value type `Vertex_index`. - /// The function adds halfedges between successive vertices if they are not yet indicent to halfedges, - /// or updates the connectivity of halfedges already in place. - /// Resizes halfedge, edge, and face properties if necessary. - /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. - template - Face_index add_face(const Range& vertices); - - - /// adds a new triangle connecting vertices `v0`, `v1`, `v2`. - /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. - Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2) { - std::array - v = {{v0, v1, v2}}; - return add_face(v); - } + /// adds a new face, and resizes face properties if necessary. + Face_index add_face() + { + if(recycle_) + return fprops_.emplace(); + else + return fprops_.emplace_back(); + } - /// adds a new quad connecting vertices `v0`, `v1`, `v2`, `v3`. - /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. - Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2, Vertex_index v3) { - std::array - v = {{v0, v1, v2, v3}}; - return add_face(v); - } + /// if possible, adds a new face with vertices from a range with value type `Vertex_index`. + /// The function adds halfedges between successive vertices if they are not yet indicent to halfedges, + /// or updates the connectivity of halfedges already in place. + /// Resizes halfedge, edge, and face properties if necessary. + /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. + template + Face_index add_face(const Range& vertices); - ///@} + /// adds a new triangle connecting vertices `v0`, `v1`, `v2`. + /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. + Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2) + { + std::array + v = {{v0, v1, v2}}; + return add_face(v); + } + /// adds a new quad connecting vertices `v0`, `v1`, `v2`, `v3`. + /// \returns the face index of the added face, or `Surface_mesh::null_face()` if the face could not be added. + Face_index add_face(Vertex_index v0, Vertex_index v1, Vertex_index v2, Vertex_index v3) + { + std::array + v = {{v0, v1, v2, v3}}; + return add_face(v); + } - /// \name Low-Level Removal Functions - /// - /// Although the elements are only marked as removed - /// their connectivity and properties should not be used. - /// - /// \warning Functions in this group do not adjust any of - /// connected elements and usually leave the surface mesh in an - /// invalid state. - /// - /// - /// @{ + ///@} - /// removes vertex `v` from the halfedge data structure without - /// adjusting anything. - void remove_vertex(Vertex_index v) { - // todo: confirm this behaves correctly - vprops_.erase(v); - } - /// removes the two halfedges corresponding to `e` from the halfedge data structure without - /// adjusting anything. - void remove_edge(Edge_index e) { - // todo: confirm this behaves correctly - eprops_.erase(e); - } - /// removes face `f` from the halfedge data structure without - /// adjusting anything. + /// \name Low-Level Removal Functions + /// + /// Although the elements are only marked as removed + /// their connectivity and properties should not be used. + /// + /// \warning Functions in this group do not adjust any of + /// connected elements and usually leave the surface mesh in an + /// invalid state. + /// + /// + /// @{ - void remove_face(Face_index f) { - // todo: confirm this behaves correctly - fprops_.erase(f); - } + /// removes vertex `v` from the halfedge data structure without + /// adjusting anything. + void remove_vertex(Vertex_index v) + { + // todo: confirm this behaves correctly + vprops_.erase(v); + } + + /// removes the two halfedges corresponding to `e` from the halfedge data structure without + /// adjusting anything. + void remove_edge(Edge_index e) + { + // todo: confirm this behaves correctly + eprops_.erase(e); + } + + /// removes face `f` from the halfedge data structure without + /// adjusting anything. + + void remove_face(Face_index f) + { + // todo: confirm this behaves correctly + fprops_.erase(f); + } - ///@} + ///@} - /// \name Memory Management - /// - /// Functions to check the number of elements, the amount of space - /// allocated for elements, and to clear the structure. - ///@{ + /// \name Memory Management + /// + /// Functions to check the number of elements, the amount of space + /// allocated for elements, and to clear the structure. + ///@{ #ifndef DOXYGEN_RUNNING - /// returns the number of used and removed vertices in the mesh. - size_type num_vertices() const { return (size_type) vprops_.size(); } + /// returns the number of used and removed vertices in the mesh. + size_type num_vertices() const { return (size_type) vprops_.size(); } - /// returns the number of used and removed halfedges in the mesh. - size_type num_halfedges() const { return (size_type) hprops_.size(); } + /// returns the number of used and removed halfedges in the mesh. + size_type num_halfedges() const { return (size_type) hprops_.size(); } - /// returns the number of used and removed edges in the mesh. - size_type num_edges() const { return (size_type) eprops_.size(); } + /// returns the number of used and removed edges in the mesh. + size_type num_edges() const { return (size_type) eprops_.size(); } - /// returns the number of used and removed faces in the mesh. - size_type num_faces() const { return (size_type) fprops_.size(); } + /// returns the number of used and removed faces in the mesh. + size_type num_faces() const { return (size_type) fprops_.size(); } #endif /// returns the number of vertices in the mesh. - size_type number_of_vertices() const { + size_type number_of_vertices() const + { return vprops_.size(); } /// returns the number of halfedges in the mesh. - size_type number_of_halfedges() const { + size_type number_of_halfedges() const + { return hprops_.size(); } /// returns the number of edges in the mesh. - size_type number_of_edges() const { + size_type number_of_edges() const + { return eprops_.size(); } /// returns the number of faces in the mesh. - size_type number_of_faces() const { + size_type number_of_faces() const + { return fprops_.size(); } - /// returns `true` iff the mesh is empty, i.e., has no vertices, halfedges and faces. - bool is_empty() const { + /// returns `true` iff the mesh is empty, i.e., has no vertices, halfedges and faces. + bool is_empty() const + { return (vprops_.size() == 0 && hprops_.size() == 0 && fprops_.size() == 0); } - /// removes all vertices, halfedge, edges and faces. Collects garbage and removes all property maps added by a call to `add_property_map()` for all simplex types. - /// - /// After calling this method, the object is the same as a newly constructed object. The additional property maps are also removed and must thus be re-added if needed. - void clear() { - clear_without_removing_property_maps(); - vprops_.remove_all_properties_except({"v:connectivity", "v:point"}); - hprops_.remove_all_properties_except({"h:connectivity"}); - fprops_.remove_all_properties_except({"f:connectivity"}); - eprops_.remove_all_properties_except({}); - } - - void clear_without_removing_property_maps() { - vprops_.reserve(0); - hprops_.reserve(0); - eprops_.reserve(0); - fprops_.reserve(0); - } + /// removes all vertices, halfedge, edges and faces. Collects garbage and removes all property maps added by a call to `add_property_map()` for all simplex types. + /// + /// After calling this method, the object is the same as a newly constructed object. The additional property maps are also removed and must thus be re-added if needed. + void clear() + { + clear_without_removing_property_maps(); + vprops_.remove_all_properties_except({"v:connectivity", "v:point"}); + hprops_.remove_all_properties_except({"h:connectivity"}); + fprops_.remove_all_properties_except({"f:connectivity"}); + eprops_.remove_all_properties_except({}); + } + void clear_without_removing_property_maps() + { + vprops_.reserve(0); + hprops_.reserve(0); + eprops_.reserve(0); + fprops_.reserve(0); + } - /// reserves space for vertices, halfedges, edges, faces, and their currently - /// associated properties. - void reserve(size_type nvertices, - size_type nedges, - size_type nfaces) { - vprops_.reserve(nvertices); - hprops_.reserve(2 * nedges); - eprops_.reserve(nedges); - fprops_.reserve(nfaces); - } -// void resize(size_type nvertices, -// size_type nedges, -// size_type nfaces) { -// vprops_.resize(nvertices); -// hprops_.resize(2 * nedges); -// eprops_.resize(nedges); -// fprops_.resize(nfaces); -// } + /// reserves space for vertices, halfedges, edges, faces, and their currently + /// associated properties. + void reserve(size_type nvertices, + size_type nedges, + size_type nfaces ) + { + vprops_.reserve(nvertices); + hprops_.reserve(2*nedges); + eprops_.reserve(nedges); + fprops_.reserve(nfaces); + } /// copies the simplices from `other`, and copies values of /// properties that already exist under the same name in `*this`. /// In case `*this` has a property that does not exist in `other` /// the copied simplices get the default value of the property. - bool join(const Surface_mesh& other) { + bool join(const Surface_mesh& other) + { // Record the original sizes of the property maps const size_type nv = number_of_vertices(), nh = number_of_halfedges(), nf = number_of_faces(); @@ -1164,645 +1198,678 @@ class Surface_mesh // todo: the below code assumes no gaps were present in the properties! That might be okay for this situation. // translate halfedge index in vertex -> halfedge - for (size_type i = nv; i < nv + other.number_of_vertices(); i++) { + for(size_type i = nv; i < nv+other.number_of_vertices(); i++){ Vertex_index vi(i); - if (vconn_[vi].halfedge_ != null_halfedge()) { - vconn_[vi].halfedge_ = Halfedge_index(size_type(vconn_[vi].halfedge_) + nh); + if(vconn_[vi].halfedge_ != null_halfedge()){ + vconn_[vi].halfedge_ = Halfedge_index(size_type(vconn_[vi].halfedge_)+nh); } } // translate halfedge index in face -> halfedge - for (size_type i = nf; i < nf + other.number_of_faces(); i++) { + for(size_type i = nf; i < nf+other.number_of_faces(); i++){ Face_index fi(i); - if (fconn_[fi].halfedge_ != null_halfedge()) { - fconn_[fi].halfedge_ = Halfedge_index(size_type(fconn_[fi].halfedge_) + nh); + if(fconn_[fi].halfedge_ != null_halfedge()){ + fconn_[fi].halfedge_ = Halfedge_index(size_type(fconn_[fi].halfedge_)+nh); } } // translate indices in halfedge -> face, halfedge -> target, halfedge -> prev, and halfedge -> next - for (size_type i = nh; i < nh + other.number_of_halfedges(); i++) { + for(size_type i = nh; i < nh+other.number_of_halfedges(); i++){ Halfedge_index hi(i); - if (hconn_[hi].face_ != null_face()) { - hconn_[hi].face_ = Face_index(size_type(hconn_[hi].face_) + nf); + if(hconn_[hi].face_ != null_face()){ + hconn_[hi].face_ = Face_index(size_type(hconn_[hi].face_)+nf); } - if (hconn_[hi].vertex_ != null_vertex()) { - hconn_[hi].vertex_ = Vertex_index(size_type(hconn_[hi].vertex_) + nv); + if( hconn_[hi].vertex_ != null_vertex()){ + hconn_[hi].vertex_ = Vertex_index(size_type(hconn_[hi].vertex_)+nv); } - if (hconn_[hi].next_halfedge_ != null_halfedge()) { - hconn_[hi].next_halfedge_ = Halfedge_index(size_type(hconn_[hi].next_halfedge_) + nh); + if(hconn_[hi].next_halfedge_ != null_halfedge()){ + hconn_[hi].next_halfedge_ = Halfedge_index(size_type(hconn_[hi].next_halfedge_)+nh); } - if (hconn_[hi].prev_halfedge_ != null_halfedge()) { - hconn_[hi].prev_halfedge_ = Halfedge_index(size_type(hconn_[hi].prev_halfedge_) + nh); + if(hconn_[hi].prev_halfedge_ != null_halfedge()){ + hconn_[hi].prev_halfedge_ = Halfedge_index(size_type(hconn_[hi].prev_halfedge_)+nh); } } return true; } - ///@} + ///@} - /// \name Garbage Collection - /// - /// While removing elements only marks them as removed - /// garbage collection really removes them. - /// The API in this section allows to check whether - /// an element is removed, to get the number of - /// removed elements, and to collect garbage. - /// The number of elements together with the number of removed elements is - /// an upperbound on the index, and is needed - /// by algorithms that temporarily store a - /// property in a vector of the appropriate size. - /// Note however that by garbage collecting elements get new indices. - /// In case you store indices in an auxiliary data structure - /// or in a property these indices are potentially no longer - /// referring to the right elements. - /// When adding elements, by default elements that are marked as removed - /// are recycled. - - ///@{ - - /// returns the number of vertices in the mesh which are marked removed. - size_type number_of_removed_vertices() const { return vprops_.capacity() - vprops_.size(); } - - /// returns the number of halfedges in the mesh which are marked removed. - size_type number_of_removed_halfedges() const { return hprops_.capacity() - hprops_.size(); } - - /// returns the number of edges in the mesh which are marked removed. - size_type number_of_removed_edges() const { return eprops_.capacity() - eprops_.size(); } - - /// returns the number offaces in the mesh which are marked removed. - size_type number_of_removed_faces() const { return fprops_.capacity() - fprops_.size(); } - - - /// returns whether vertex `v` is marked removed. - bool is_removed(Vertex_index v) const { - return vprops_.is_erased(v); - } + /// \name Garbage Collection + /// + /// While removing elements only marks them as removed + /// garbage collection really removes them. + /// The API in this section allows to check whether + /// an element is removed, to get the number of + /// removed elements, and to collect garbage. + /// The number of elements together with the number of removed elements is + /// an upperbound on the index, and is needed + /// by algorithms that temporarily store a + /// property in a vector of the appropriate size. + /// Note however that by garbage collecting elements get new indices. + /// In case you store indices in an auxiliary data structure + /// or in a property these indices are potentially no longer + /// referring to the right elements. + /// When adding elements, by default elements that are marked as removed + /// are recycled. + + ///@{ + + /// returns the number of vertices in the mesh which are marked removed. + size_type number_of_removed_vertices() const { return vprops_.capacity() - vprops_.size(); } + + /// returns the number of halfedges in the mesh which are marked removed. + size_type number_of_removed_halfedges() const { return hprops_.capacity() - hprops_.size(); } + + /// returns the number of edges in the mesh which are marked removed. + size_type number_of_removed_edges() const { return eprops_.capacity() - eprops_.size(); } + + /// returns the number offaces in the mesh which are marked removed. + size_type number_of_removed_faces() const { return fprops_.capacity() - fprops_.size(); } + + + /// returns whether vertex `v` is marked removed. + bool is_removed(Vertex_index v) const + { + return vprops_.is_erased(v); + } + /// returns whether halfedge `h` is marked removed. + bool is_removed(Halfedge_index h) const + { + return hprops_.is_erased(h); + } + /// returns whether edge `e` is marked removed. + bool is_removed(Edge_index e) const + { + return eprops_.is_erased(e); + } + /// returns whether face `f` is marked removed. + bool is_removed(Face_index f) const + { + return fprops_.is_erased(f); + } - /// returns whether halfedge `h` is marked removed. - bool is_removed(Halfedge_index h) const { - return hprops_.is_erased(h); - } + /// checks if any vertices, halfedges, edges, or faces are marked as removed. + /// \sa collect_garbage + bool has_garbage() const { + return number_of_removed_vertices() != 0 || + number_of_removed_edges() != 0 || + number_of_removed_halfedges() != 0 || + number_of_removed_faces() != 0; + } - /// returns whether edge `e` is marked removed. - bool is_removed(Edge_index e) const { - return eprops_.is_erased(e); - } + /// really removes vertices, halfedges, edges, and faces which are marked removed. + /// \sa `has_garbage()` + /// \attention By garbage collecting elements get new indices. + /// In case you store indices in an auxiliary data structure + /// or in a property these indices are potentially no longer + /// referring to the right elements. + void collect_garbage() { + // todo: this should compress the array + } - /// returns whether face `f` is marked removed. - bool is_removed(Face_index f) const { - return fprops_.is_erased(f); - } + //undocumented convenience function that allows to get old-index->new-index information + template + void collect_garbage(Visitor& visitor) { + // todo: this should compress the array and remap indices + } - /// checks if any vertices, halfedges, edges, or faces are marked as removed. - /// \sa collect_garbage - // todo: remove - bool has_garbage() const { - return number_of_removed_vertices() != 0 || - number_of_removed_edges() != 0 || - number_of_removed_halfedges() != 0 || - number_of_removed_faces() != 0; - } + /// controls the recycling or not of simplices previously marked as removed + /// upon addition of new elements. + /// When set to `true` (default value), new elements are first picked in the garbage (if any) + /// while if set to `false` only new elements are created. + void set_recycle_garbage(bool b) { recycle_ = b; } - /// really removes vertices, halfedges, edges, and faces which are marked removed. - /// \sa `has_garbage()` - /// \attention By garbage collecting elements get new indices. - /// In case you store indices in an auxiliary data structure - /// or in a property these indices are potentially no longer - /// referring to the right elements. - void collect_garbage() { - // todo: this should compress the array - } + /// Getter + bool does_recycle_garbage() const { return recycle_; } - // undocumented convenience function that allows to get old-index->new-index information - template - void collect_garbage(Visitor& visitor) { - // todo: this should compress the array and remap indices - } + /// @cond CGAL_DOCUMENT_INTERNALS + /// removes unused memory from vectors. This shrinks the storage + /// of all properties to the minimal required size. + /// \attention Invalidates all existing references to properties. - /// controls the recycling or not of simplices previously marked as removed - /// upon addition of new elements. - /// When set to `true` (default value), new elements are first picked in the garbage (if any) - /// while if set to `false` only new elements are created. - void set_recycle_garbage(bool b) { recycle_ = b; } + /// @endcond - /// Getter - bool does_recycle_garbage() const { return recycle_; } + ///@} - /// @cond CGAL_DOCUMENT_INTERNALS - /// removes unused memory from vectors. This shrinks the storage - /// of all properties to the minimal required size. - /// \attention Invalidates all existing references to properties. - -// void shrink_to_fit() { -// vprops_.shrink_to_fit(); -// hprops_.shrink_to_fit(); -// eprops_.shrink_to_fit(); -// fprops_.shrink_to_fit(); -// } - /// @endcond + /// @cond CGAL_DOCUMENT_INTERNALS + /// + /// \name Simple Validity Checks + /// + /// Functions in this group check if the index is valid, that is between + /// `0` and the currently allocated maximum amount of the + /// elements. They do not check if an element is marked as removed. + ///@{ - ///@} + /// returns whether the index of vertex `v` is valid, that is within the current array bounds. + bool has_valid_index(Vertex_index v) const + { + return ((size_type)v < number_of_vertices()); + } - /// @cond CGAL_DOCUMENT_INTERNALS - /// - /// \name Simple Validity Checks - /// - /// Functions in this group check if the index is valid, that is between - /// `0` and the currently allocated maximum amount of the - /// elements. They do not check if an element is marked as removed. - ///@{ - - /// returns whether the index of vertex `v` is valid, that is within the current array bounds. - bool has_valid_index(Vertex_index v) const { - return ((size_type) v < number_of_vertices()); - } + /// returns whether the index of halfedge `h` is valid, that is within the current array bounds. + bool has_valid_index(Halfedge_index h) const + { + return ((size_type)h < number_of_halfedges()); + } + /// returns whether the index of edge `e` is valid, that is within the current array bounds. + bool has_valid_index(Edge_index e) const + { + return ((size_type)e < number_of_edges()); + } + /// returns whether the index of face `f` is valid, that is within the current array bounds. + bool has_valid_index(Face_index f) const + { + return ((size_type)f < number_of_faces()); + } - /// returns whether the index of halfedge `h` is valid, that is within the current array bounds. - bool has_valid_index(Halfedge_index h) const { - return ((size_type) h < number_of_halfedges()); - } + /// @} + /// @endcond - /// returns whether the index of edge `e` is valid, that is within the current array bounds. - bool has_valid_index(Edge_index e) const { - return ((size_type) e < number_of_edges()); - } + /// \name Validity Checks + /// + /// Functions in this group perform checks for structural + /// consistency of a complete surface mesh, or an individual element. + /// They are expensive and should only be used in debug configurations. - /// returns whether the index of face `f` is valid, that is within the current array bounds. - bool has_valid_index(Face_index f) const { - return ((size_type) f < number_of_faces()); - } + ///@{ - /// @} - /// @endcond + /// perform an expensive validity check on the data structure and + /// print found errors to `std::cerr` when `verbose == true`. + bool is_valid(bool verbose = false) const + { + bool valid = true; + size_type vcount = 0, hcount = 0, fcount = 0; + for(Halfedge_iterator it = halfedges_begin(); it != halfedges_end(); ++it) { + ++hcount; + valid = valid && next(*it).is_valid(); + valid = valid && opposite(*it).is_valid(); + if(!valid) { + if (verbose) + std::cerr << "Integrity of halfedge " << *it << " corrupted." << std::endl; + break; + } + + valid = valid && (opposite(*it) != *it); + valid = valid && (opposite(opposite(*it)) == *it); + if(!valid) { + if (verbose) + std::cerr << "Integrity of opposite halfedge of " << *it << " corrupted." << std::endl; + break; + } + + valid = valid && (next(prev(*it)) == *it); + if(!valid) { + if (verbose) + std::cerr << "Integrity of previous halfedge of " << *it << " corrupted." << std::endl; + break; + } + + valid = valid && (prev(next(*it)) == *it); + if(!valid) { + if (verbose) + std::cerr << "Integrity of next halfedge of " << *it << " corrupted." << std::endl; + break; + } + + valid = valid && target(*it).is_valid(); + if(!valid) { + if (verbose) + std::cerr << "Integrity of vertex of halfedge " << *it << " corrupted." << std::endl; + break; + } + + valid = valid && (target(*it) == target(opposite(next(*it)))); + if(!valid) { + if (verbose) + std::cerr << "Halfedge vertex of next opposite is not the same for " << *it << "." << std::endl; + break; + } + } - /// \name Validity Checks - /// - /// Functions in this group perform checks for structural - /// consistency of a complete surface mesh, or an individual element. - /// They are expensive and should only be used in debug configurations. - - ///@{ - - /// perform an expensive validity check on the data structure and - /// print found errors to `std::cerr` when `verbose == true`. - bool is_valid(bool verbose = false) const { - bool valid = true; - size_type vcount = 0, hcount = 0, fcount = 0; - for (Halfedge_iterator it = halfedges_begin(); it != halfedges_end(); ++it) { - ++hcount; - valid = valid && next(*it).is_valid(); - valid = valid && opposite(*it).is_valid(); - if (!valid) { - if (verbose) - std::cerr << "Integrity of halfedge " << *it << " corrupted." << std::endl; - break; - } - - valid = valid && (opposite(*it) != *it); - valid = valid && (opposite(opposite(*it)) == *it); - if (!valid) { - if (verbose) - std::cerr << "Integrity of opposite halfedge of " << *it << " corrupted." << std::endl; - break; - } + for(Vertex_iterator it = vertices_begin(); it != vertices_end(); ++it) { + ++vcount; + if(halfedge(*it).is_valid()) { + // not an isolated vertex + valid = valid && (target(halfedge(*it)) == *it); + if(!valid) { + if (verbose) + std::cerr << "Halfedge of " << *it << " is not an incoming halfedge." << std::endl; + break; + } + } + } + for(Face_iterator it = faces_begin(); it != faces_end(); ++it) { + ++fcount; + } - valid = valid && (next(prev(*it)) == *it); - if (!valid) { - if (verbose) - std::cerr << "Integrity of previous halfedge of " << *it << " corrupted." << std::endl; - break; - } + valid = valid && (vcount == number_of_vertices()); + if(!valid && verbose){ + std::cerr << "#vertices: iterated: " << vcount << " vs number_of_vertices(): " << number_of_vertices()<< std::endl; + } - valid = valid && (prev(next(*it)) == *it); - if (!valid) { - if (verbose) - std::cerr << "Integrity of next halfedge of " << *it << " corrupted." << std::endl; - break; - } + valid = valid && (hcount == number_of_halfedges()); + if(!valid && verbose){ + std::cerr << "#halfedges: iterated: " << hcount << " vs number_of_halfedges(): " << number_of_halfedges()<< std::endl; + } - valid = valid && target(*it).is_valid(); - if (!valid) { - if (verbose) - std::cerr << "Integrity of vertex of halfedge " << *it << " corrupted." << std::endl; - break; - } + valid = valid && (fcount == number_of_faces()); + if(!valid && verbose){ + std::cerr << "#faces: iterated: " << fcount << " vs number_of_faces(): " << number_of_faces()<< std::endl; + } - valid = valid && (target(*it) == target(opposite(next(*it)))); - if (!valid) { - if (verbose) - std::cerr << "Halfedge vertex of next opposite is not the same for " << *it << "." << std::endl; - break; - } + return valid; } - for (Vertex_iterator it = vertices_begin(); it != vertices_end(); ++it) { - ++vcount; - if (halfedge(*it).is_valid()) { - // not an isolated vertex - valid = valid && (target(halfedge(*it)) == *it); - if (!valid) { - if (verbose) - std::cerr << "Halfedge of " << *it << " is not an incoming halfedge." << std::endl; - break; - } - } - } - for (Face_iterator it = faces_begin(); it != faces_end(); ++it) { - ++fcount; - } + /// performs a validity check on a single vertex. + bool is_valid(Vertex_index v, + bool verbose = false) const + { + Verbose_ostream verr(verbose); - valid = valid && (vcount == number_of_vertices()); - if (!valid && verbose) { - std::cerr << "#vertices: iterated: " << vcount << " vs number_of_vertices(): " << number_of_vertices() - << std::endl; - } + if(!has_valid_index(v)) + { + verr << "Vertex has invalid index: " << (size_type)v << std::endl; + return false; + } - valid = valid && (hcount == number_of_halfedges()); - if (!valid && verbose) { - std::cerr << "#halfedges: iterated: " << hcount << " vs number_of_halfedges(): " << number_of_halfedges() - << std::endl; + Halfedge_index h = vconn_[v].halfedge_; + if(h != null_halfedge() && (!has_valid_index(h) || is_removed(h))) { + verr << "Vertex connectivity halfedge error: Vertex " << (size_type)v + << " with " << (size_type)h << std::endl; + return false; + } + return true; } - valid = valid && (fcount == number_of_faces()); - if (!valid && verbose) { - std::cerr << "#faces: iterated: " << fcount << " vs number_of_faces(): " << number_of_faces() << std::endl; - } + /// performs a validity check on a single halfedge. + bool is_valid(Halfedge_index h, + bool verbose = false) const + { + Verbose_ostream verr(verbose); - return valid; - } + if(!has_valid_index(h)) + { + verr << "Halfedge has invalid index: " << (size_type)h << std::endl; + return false; + } - /// performs a validity check on a single vertex. - bool is_valid(Vertex_index v, - bool verbose = false) const { - Verbose_ostream verr(verbose); + Face_index f = hconn_[h].face_; + Vertex_index v = hconn_[h].vertex_; + Halfedge_index hn = hconn_[h].next_halfedge_; + Halfedge_index hp = hconn_[h].prev_halfedge_; + + bool valid = true; + // don't validate the face if this is a border halfedge + if(!is_border(h)) { + if(!has_valid_index(f) || is_removed(f)) { + verr << "Halfedge connectivity error: Face " + << (!has_valid_index(f) ? "invalid" : "removed") + << " in " << (size_type)h << std::endl; + valid = false; + } + } - if (!has_valid_index(v)) { - verr << "Vertex has invalid index: " << (size_type) v << std::endl; - return false; - } + if(!has_valid_index(v) || is_removed(v)) { + verr << "Halfedge connectivity error: Vertex " + << (!has_valid_index(v) ? "invalid" : "removed") + << " in " << (size_type)h << std::endl; + valid = false; + } - Halfedge_index h = vconn_[v].halfedge_; - if (h != null_halfedge() && (!has_valid_index(h) || is_removed(h))) { - verr << "Vertex connectivity halfedge error: Vertex " << (size_type) v - << " with " << (size_type) h << std::endl; - return false; + if(!has_valid_index(hn) || is_removed(hn)) { + verr << "Halfedge connectivity error: hnext " + << (!has_valid_index(hn) ? "invalid" : "removed") + << " in " << (size_type)h << std::endl; + valid = false; + } + if(!has_valid_index(hp) || is_removed(hp)) { + verr << "Halfedge connectivity error: hprev " + << (!has_valid_index(hp) ? "invalid" : "removed") + << " in " << (size_type)h << std::endl; + valid = false; + } + return valid; } - return true; - } - - /// performs a validity check on a single halfedge. - bool is_valid(Halfedge_index h, - bool verbose = false) const { - Verbose_ostream verr(verbose); - if (!has_valid_index(h)) { - verr << "Halfedge has invalid index: " << (size_type) h << std::endl; - return false; - } - Face_index f = hconn_[h].face_; - Vertex_index v = hconn_[h].vertex_; - Halfedge_index hn = hconn_[h].next_halfedge_; - Halfedge_index hp = hconn_[h].prev_halfedge_; + /// performs a validity check on a single edge. + bool is_valid(Edge_index e, + bool verbose = false) const + { + Verbose_ostream verr(verbose); - bool valid = true; - // don't validate the face if this is a border halfedge - if (!is_border(h)) { - if (!has_valid_index(f) || is_removed(f)) { - verr << "Halfedge connectivity error: Face " - << (!has_valid_index(f) ? "invalid" : "removed") - << " in " << (size_type) h << std::endl; - valid = false; + if(!has_valid_index(e)) + { + verr << "Edge has invalid index: " << (size_type)e << std::endl; + return false; } - } - if (!has_valid_index(v) || is_removed(v)) { - verr << "Halfedge connectivity error: Vertex " - << (!has_valid_index(v) ? "invalid" : "removed") - << " in " << (size_type) h << std::endl; - valid = false; + Halfedge_index h = halfedge(e); + return is_valid(h, verbose) && is_valid(opposite(h), verbose); } - if (!has_valid_index(hn) || is_removed(hn)) { - verr << "Halfedge connectivity error: hnext " - << (!has_valid_index(hn) ? "invalid" : "removed") - << " in " << (size_type) h << std::endl; - valid = false; - } - if (!has_valid_index(hp) || is_removed(hp)) { - verr << "Halfedge connectivity error: hprev " - << (!has_valid_index(hp) ? "invalid" : "removed") - << " in " << (size_type) h << std::endl; - valid = false; - } - return valid; - } + /// performs a validity check on a single face. + bool is_valid(Face_index f, + bool verbose = false) const + { + Verbose_ostream verr(verbose); - /// performs a validity check on a single edge. - bool is_valid(Edge_index e, - bool verbose = false) const { - Verbose_ostream verr(verbose); + if(!has_valid_index(f)) + { + verr << "Face has invalid index: " << (size_type)f << std::endl; + return false; + } - if (!has_valid_index(e)) { - verr << "Edge has invalid index: " << (size_type) e << std::endl; - return false; + Halfedge_index h = fconn_[f].halfedge_; + if(!has_valid_index(h) || is_removed(h)) { + verr << "Face connectivity halfedge error: Face " << (size_type)f + << " with " << (size_type)h << std::endl; + return false; + } + return true; } - Halfedge_index h = halfedge(e); - return is_valid(h, verbose) && is_valid(opposite(h), verbose); - } + ///@} - /// performs a validity check on a single face. - bool is_valid(Face_index f, - bool verbose = false) const { - Verbose_ostream verr(verbose); - if (!has_valid_index(f)) { - verr << "Face has invalid index: " << (size_type) f << std::endl; - return false; - } + /// \name Low-Level Connectivity + ///@{ - Halfedge_index h = fconn_[f].halfedge_; - if (!has_valid_index(h) || is_removed(h)) { - verr << "Face connectivity halfedge error: Face " << (size_type) f - << " with " << (size_type) h << std::endl; - return false; + /// returns the vertex the halfedge `h` points to. + Vertex_index target(Halfedge_index h) const + { + return hconn_[h].vertex_; } - return true; - } - ///@} - - - - /// \name Low-Level Connectivity - ///@{ - - /// returns the vertex the halfedge `h` points to. - Vertex_index target(Halfedge_index h) const { - return hconn_[h].vertex_; - } - - /// sets the vertex the halfedge `h` points to to `v`. - void set_target(Halfedge_index h, Vertex_index v) { - hconn_[h].vertex_ = v; - } + /// sets the vertex the halfedge `h` points to to `v`. + void set_target(Halfedge_index h, Vertex_index v) + { + hconn_[h].vertex_ = v; + } - /// returns the face incident to halfedge `h`. - Face_index face(Halfedge_index h) const { - return hconn_[h].face_; - } + /// returns the face incident to halfedge `h`. + Face_index face(Halfedge_index h) const + { + return hconn_[h].face_; + } - /// sets the incident face to halfedge `h` to `f`. - void set_face(Halfedge_index h, Face_index f) { - hconn_[h].face_ = f; - } + /// sets the incident face to halfedge `h` to `f`. + void set_face(Halfedge_index h, Face_index f) + { + hconn_[h].face_ = f; + } - /// returns the next halfedge within the incident face. - Halfedge_index next(Halfedge_index h) const { - return hconn_[h].next_halfedge_; - } + /// returns the next halfedge within the incident face. + Halfedge_index next(Halfedge_index h) const + { + return hconn_[h].next_halfedge_; + } - /// returns the previous halfedge within the incident face. - Halfedge_index prev(Halfedge_index h) const { - return hconn_[h].prev_halfedge_; - } + /// returns the previous halfedge within the incident face. + Halfedge_index prev(Halfedge_index h) const + { + return hconn_[h].prev_halfedge_; + } - /// @cond CGAL_DOCUMENT_INTERNALS - // sets the next halfedge of `h` within the face to `nh`. - void set_next_only(Halfedge_index h, Halfedge_index nh) { - hconn_[h].next_halfedge_ = nh; - } + /// @cond CGAL_DOCUMENT_INTERNALS + // sets the next halfedge of `h` within the face to `nh`. + void set_next_only(Halfedge_index h, Halfedge_index nh) + { + hconn_[h].next_halfedge_ = nh; + } - // sets previous halfedge of `h` to `nh`. - void set_prev_only(Halfedge_index h, Halfedge_index nh) { - if (h != null_halfedge()) { - hconn_[h].prev_halfedge_ = nh; + // sets previous halfedge of `h` to `nh`. + void set_prev_only(Halfedge_index h, Halfedge_index nh) + { + if(h != null_halfedge()){ + hconn_[h].prev_halfedge_ = nh; + } } - } - /// @endcond + /// @endcond - /// sets the next halfedge of `h` within the face to `nh` and - /// the previous halfedge of `nh` to `h`. - void set_next(Halfedge_index h, Halfedge_index nh) { - set_next_only(h, nh); - set_prev_only(nh, h); - } + /// sets the next halfedge of `h` within the face to `nh` and + /// the previous halfedge of `nh` to `h`. + void set_next(Halfedge_index h, Halfedge_index nh) + { + set_next_only(h, nh); + set_prev_only(nh, h); + } - /// returns an incoming halfedge of vertex `v`. - /// If `v` is a border vertex this will be a border halfedge. - /// \invariant `target(halfedge(v)) == v` - Halfedge_index halfedge(Vertex_index v) const { - return vconn_[v].halfedge_; - } + /// returns an incoming halfedge of vertex `v`. + /// If `v` is a border vertex this will be a border halfedge. + /// \invariant `target(halfedge(v)) == v` + Halfedge_index halfedge(Vertex_index v) const + { + return vconn_[v].halfedge_; + } - /// sets the incoming halfedge of vertex `v` to `h`. - void set_halfedge(Vertex_index v, Halfedge_index h) { - vconn_[v].halfedge_ = h; - } + /// sets the incoming halfedge of vertex `v` to `h`. + void set_halfedge(Vertex_index v, Halfedge_index h) + { + vconn_[v].halfedge_ = h; + } - /// returns a halfedge of face `f`. - Halfedge_index halfedge(Face_index f) const { - return fconn_[f].halfedge_; - } + /// returns a halfedge of face `f`. + Halfedge_index halfedge(Face_index f) const + { + return fconn_[f].halfedge_; + } - /// sets the halfedge of face `f` to `h`. - void set_halfedge(Face_index f, Halfedge_index h) { - fconn_[f].halfedge_ = h; - } + /// sets the halfedge of face `f` to `h`. + void set_halfedge(Face_index f, Halfedge_index h) + { + fconn_[f].halfedge_ = h; + } - /// returns the opposite halfedge of `h`. Note that there is no function `set_opposite()`. - Halfedge_index opposite(Halfedge_index h) const { - return Halfedge_index(((size_type) h & 1) ? (size_type) h - 1 : (size_type) h + 1); - } + /// returns the opposite halfedge of `h`. Note that there is no function `set_opposite()`. + Halfedge_index opposite(Halfedge_index h) const + { + return Halfedge_index(((size_type)h & 1) ? (size_type)h-1 : (size_type)h+1); + } - ///@} + ///@} - /// \name Low-Level Connectivity Convenience Functions - ///@{ + /// \name Low-Level Connectivity Convenience Functions + ///@{ - /// returns the vertex the halfedge `h` emanates from. - Vertex_index source(Halfedge_index h) const { - return target(opposite(h)); - } + /// returns the vertex the halfedge `h` emanates from. + Vertex_index source(Halfedge_index h) const + { + return target(opposite(h)); + } - /// returns `opposite(next(h))`, that is the next halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the target vertex of `h`. - Halfedge_index next_around_target(Halfedge_index h) const { - return opposite(next(h)); - } + /// returns `opposite(next(h))`, that is the next halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the target vertex of `h`. + Halfedge_index next_around_target(Halfedge_index h) const + { + return opposite(next(h)); + } - /// returns `prev(opposite(h))`, that is the previous halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the target vertex of `h`. - Halfedge_index prev_around_target(Halfedge_index h) const { - return prev(opposite(h)); - } + /// returns `prev(opposite(h))`, that is the previous halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the target vertex of `h`. + Halfedge_index prev_around_target(Halfedge_index h) const + { + return prev(opposite(h)); + } - /// returns `next(opposite(h))`, that is the next halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the source vertex of `h`. - Halfedge_index next_around_source(Halfedge_index h) const { - return next(opposite(h)); - } + /// returns `next(opposite(h))`, that is the next halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the source vertex of `h`. + Halfedge_index next_around_source(Halfedge_index h) const + { + return next(opposite(h)); + } - /// returns `opposite(prev(h))`, that is the previous halfedge \ref SurfaceMeshOrientation - /// "clockwise" around the source vertex of `h`. - Halfedge_index prev_around_source(Halfedge_index h) const { - return opposite(prev(h)); - } + /// returns `opposite(prev(h))`, that is the previous halfedge \ref SurfaceMeshOrientation + /// "clockwise" around the source vertex of `h`. + Halfedge_index prev_around_source(Halfedge_index h) const + { + return opposite(prev(h)); + } - /// returns the i'th vertex of edge `e`, for `i=0` or `1`. - Vertex_index vertex(Edge_index e, unsigned int i) const { - CGAL_assertion(i <= 1); - return target(halfedge(e, i)); - } + /// returns the i'th vertex of edge `e`, for `i=0` or `1`. + Vertex_index vertex(Edge_index e, unsigned int i) const + { + CGAL_assertion(i<=1); + return target(halfedge(e, i)); + } - /// finds a halfedge between two vertices. Returns a default constructed - /// `Halfedge_index`, if `source` and `target` are not connected. - Halfedge_index halfedge(Vertex_index source, Vertex_index target) const; + /// finds a halfedge between two vertices. Returns a default constructed + /// `Halfedge_index`, if `source` and `target` are not connected. + Halfedge_index halfedge(Vertex_index source, Vertex_index target) const; - ///@} + ///@} - /// \name Switching between Halfedges and Edges - ///@{ + /// \name Switching between Halfedges and Edges + ///@{ - /// returns the edge that contains halfedge `h` as one of its two halfedges. - Edge_index edge(Halfedge_index h) const { - return Edge_index(h); - } + /// returns the edge that contains halfedge `h` as one of its two halfedges. + Edge_index edge(Halfedge_index h) const + { + return Edge_index(h); + } - /// returns the halfedge corresponding to the edge `e`. - Halfedge_index halfedge(Edge_index e) const { - return Halfedge_index(e.halfedge()); - } + /// returns the halfedge corresponding to the edge `e`. + Halfedge_index halfedge(Edge_index e) const + { + return Halfedge_index(e.halfedge()); + } - /// returns the i'th halfedge of edge `e`, for `i=0` or `1`. - Halfedge_index halfedge(Edge_index e, unsigned int i) const { - CGAL_assertion(i <= 1); - return Halfedge_index(((size_type) e << 1) + i); - } + /// returns the i'th halfedge of edge `e`, for `i=0` or `1`. + Halfedge_index halfedge(Edge_index e, unsigned int i) const + { + CGAL_assertion(i<=1); + return Halfedge_index(((size_type)e << 1) + i); + } - ///@} + ///@} - /// \name Degree Functions - ///@{ + /// \name Degree Functions + ///@{ - /// returns the number of incident halfedges of vertex `v`. - size_type degree(Vertex_index v) const; + /// returns the number of incident halfedges of vertex `v`. + size_type degree(Vertex_index v) const; - /// returns the number of incident halfedges of face `f`. - size_type degree(Face_index f) const; + /// returns the number of incident halfedges of face `f`. + size_type degree(Face_index f) const; - ///@} + ///@} - /// \name Borders - /// - /// A halfedge, or edge is on the border of a surface mesh - /// if it is incident to a `null_face()`. A vertex is on a border - /// if it is isolated or incident to a border halfedge. While for a halfedge and - /// edge this is a constant time operation, for a vertex it means - /// to look at all incident halfedges. If algorithms operating on a - /// surface mesh maintain that the halfedge associated to a border vertex is - /// a border halfedge, this is a constant time operation too. - /// This section provides functions to check if an element is on a - /// border and to change the halfedge associated to a border vertex. - ///@{ - - /// returns whether `v` is a border vertex. - /// \cgalAdvancedBegin - /// With the default value for - /// `check_all_incident_halfedges` the function iteratates over the incident halfedges. - /// With `check_all_incident_halfedges == false` the function returns `true`, if the incident - /// halfedge associated to vertex `v` is a border halfedge, or if the vertex is isolated. - /// \cgalAdvancedEnd - /// \attention If the data contained in the `Surface_mesh` is not a 2-manifold, then - /// this operation is not guaranteed to return the right result. - bool is_border(Vertex_index v, bool check_all_incident_halfedges = true) const { - Halfedge_index h(halfedge(v)); - if (h == null_halfedge()) { - return true; - } - if (check_all_incident_halfedges) { - Halfedge_around_target_circulator hatc(h, *this), done(hatc); - do { - if (is_border(*hatc)) { + /// \name Borders + /// + /// A halfedge, or edge is on the border of a surface mesh + /// if it is incident to a `null_face()`. A vertex is on a border + /// if it is isolated or incident to a border halfedge. While for a halfedge and + /// edge this is a constant time operation, for a vertex it means + /// to look at all incident halfedges. If algorithms operating on a + /// surface mesh maintain that the halfedge associated to a border vertex is + /// a border halfedge, this is a constant time operation too. + /// This section provides functions to check if an element is on a + /// border and to change the halfedge associated to a border vertex. + ///@{ + + /// returns whether `v` is a border vertex. + /// \cgalAdvancedBegin + /// With the default value for + /// `check_all_incident_halfedges` the function iteratates over the incident halfedges. + /// With `check_all_incident_halfedges == false` the function returns `true`, if the incident + /// halfedge associated to vertex `v` is a border halfedge, or if the vertex is isolated. + /// \cgalAdvancedEnd + /// \attention If the data contained in the `Surface_mesh` is not a 2-manifold, then + /// this operation is not guaranteed to return the right result. + bool is_border(Vertex_index v, bool check_all_incident_halfedges = true) const + { + Halfedge_index h(halfedge(v)); + if (h == null_halfedge()){ return true; } - } while (++hatc != done); - return false; + if(check_all_incident_halfedges){ + Halfedge_around_target_circulator hatc(h,*this), done(hatc); + do { + if(is_border(*hatc)){ + return true; + } + }while(++hatc != done); + return false; + } + return is_border(h); } - return is_border(h); - } - /// returns whether `h` is a border halfege, that is if its incident face is `sm.null_face()`. - bool is_border(Halfedge_index h) const { - return !face(h).is_valid(); - } + /// returns whether `h` is a border halfege, that is if its incident face is `sm.null_face()`. + bool is_border(Halfedge_index h) const + { + return !face(h).is_valid(); + } - /// returns whether `e` is a border edge, i.e., if any - /// of its two halfedges is a border halfedge. - bool is_border(Edge_index e) const { - return is_border(e.halfedge()) || is_border(opposite(e.halfedge())); - } + /// returns whether `e` is a border edge, i.e., if any + /// of its two halfedges is a border halfedge. + bool is_border(Edge_index e) const + { + return is_border(e.halfedge()) || is_border(opposite(e.halfedge())); + } /// iterates over the incident halfedges and sets the incident halfedge /// associated to vertex `v` to a border halfedge and returns `true` if it exists. - bool set_vertex_halfedge_to_border_halfedge(Vertex_index v) { - if (halfedge(v) == null_halfedge()) { + bool set_vertex_halfedge_to_border_halfedge(Vertex_index v) + { + if(halfedge(v) == null_halfedge()){ return false; } - Halfedge_around_target_circulator hatc(halfedge(v), *this), done(hatc); + Halfedge_around_target_circulator hatc(halfedge(v),*this), done(hatc); do { - if (is_border(*hatc)) { - set_halfedge(v, *hatc); + if(is_border(*hatc)){ + set_halfedge(v,*hatc); return true; } - } while (++hatc != done); + }while(++hatc != done); return false; } /// applies `set_vertex_halfedge_to_border_halfedge(Vertex_index)` on all vertices /// around the face associated to `h`. - void set_vertex_halfedge_to_border_halfedge(Halfedge_index h) { - if (is_border(h)) { - Halfedge_around_face_circulator hafc(h, *this), done(hafc); + void set_vertex_halfedge_to_border_halfedge(Halfedge_index h) + { + if(is_border(h)){ + Halfedge_around_face_circulator hafc(h,*this),done(hafc); do { - set_halfedge(target(*hafc), *hafc); - } while (++hafc != done); + set_halfedge(target(*hafc),*hafc); + }while(++hafc != done); } else { - Vertex_around_face_circulator vafc(h, *this), done(vafc); + Vertex_around_face_circulator vafc(h,*this),done(vafc); do { set_vertex_halfedge_to_border_halfedge(*vafc); - } while (++vafc != done); + }while(++vafc != done); } } /// applies `set_vertex_halfedge_to_border_halfedge(Vertex_index)` on all vertices /// of the surface mesh. - void set_vertex_halfedge_to_border_halfedge() { - for (Halfedge_index h: halfedges()) { - if (is_border(h)) { - set_halfedge(target(h), h); - } + void set_vertex_halfedge_to_border_halfedge() + { + for(Halfedge_index h : halfedges()){ + if(is_border(h)){ + set_halfedge(target(h),h); + } } } - /// returns whether `v` is isolated, i.e., incident to `Surface_mesh::null_halfedge()`. - bool is_isolated(Vertex_index v) const { - return !halfedge(v).is_valid(); - } + /// returns whether `v` is isolated, i.e., incident to `Surface_mesh::null_halfedge()`. + bool is_isolated(Vertex_index v) const + { + return !halfedge(v).is_valid(); + } - ///@} + ///@} -public: //--------------------------------------------------- property handling +private: //--------------------------------------------------- property handling template Property_container& get_property_container() { @@ -1829,17 +1896,17 @@ class Surface_mesh } -public: + public: - /*! \name Property Handling + /*! \name Property Handling - A `Properties::Property_map` allows to associate properties of type `T` to a vertex, halfdge, edge, or face index type I. - Properties can be added, and looked up with a string, and they can be removed at runtime. - The \em point property of type `P` is associated to the string "v:point". + A `Properties::Property_map` allows to associate properties of type `T` to a vertex, halfdge, edge, or face index type I. + Properties can be added, and looked up with a string, and they can be removed at runtime. + The \em point property of type `P` is associated to the string "v:point". - */ - ///@{ + */ + ///@{ /// Model of `LvaluePropertyMap` with `I` as key type and `T` as value type, where `I` /// is either a vertex, halfedge, edge, or face index type. @@ -1852,247 +1919,256 @@ class Surface_mesh #endif - // todo: I can't see a good reason for these two functions to exist separately, but do almost the same thing - - /// adds a property map named `name` with value type `T` and default `t` - /// for index type `I`. Returns the property map together with a Boolean - /// that is `true` if a new map was created. In case it already exists - /// the existing map together with `false` is returned. - template - std::pair, bool> - add_property_map(std::string name = std::string(), const T t = T()) { - if (name.empty()) { - // todo: maybe this should be done by the property container itself? - std::ostringstream oss; - oss << "anonymous-property-" << anonymous_property_++; - name = std::string(oss.str()); - } - // todo: double check this is working - auto [array, created] = - const_cast*>(this)->get_property_container().template get_or_add_property(name, t); - return {{array.get()}, created}; - } + // todo: I can't see a good reason for these two functions to exist separately, but do almost the same thing + + /// adds a property map named `name` with value type `T` and default `t` + /// for index type `I`. Returns the property map together with a Boolean + /// that is `true` if a new map was created. In case it already exists + /// the existing map together with `false` is returned. + template + std::pair, bool> + add_property_map(std::string name=std::string(), const T t=T()) { + if(name.empty()){ + // todo: maybe this should be done by the property container itself? + std::ostringstream oss; + oss << "anonymous-property-" << anonymous_property_++; + name = std::string(oss.str()); + } + // todo: double check this is working + auto [array, created] = + const_cast*>(this)->get_property_container().template get_or_add_property(name, t); + return {{array.get()}, created}; + } - /// returns a property map named `name` with key type `I` and value type `T`, - /// and a Boolean that is `true` if the property was created. - template - std::pair, bool> - property_map(const std::string& name) const { - auto [array, created] = - const_cast*>(this)->get_property_container().template get_or_add_property(name); - return {{array.get()}, created}; - } + /// returns a property map named `name` with key type `I` and value type `T`, + /// and a Boolean that is `true` if the property was created. + template + std::pair, bool> + property_map(const std::string& name) const { + auto [array, created] = + const_cast*>(this)->get_property_container().template get_or_add_property(name); + return {{array.get()}, created}; + } - /// returns a property map named `name` with key type `I` and value type `T`, - /// if such a property map exists - template - std::optional> - get_property_map(const std::string& name) { - auto maybe_property_map = get_property_container().template get_property_if_exists(name); - if (!maybe_property_map) return {}; - else return {{maybe_property_map.value()}}; - } + /// returns a property map named `name` with key type `I` and value type `T`, + /// if such a property map exists + template + std::optional> + get_property_map(const std::string& name) { + auto maybe_property_map = get_property_container().template get_property_if_exists(name); + if (!maybe_property_map) return {}; + else return {{maybe_property_map.value()}}; + } - template - std::optional> - get_property_map(const std::string& name) const { - auto maybe_property_map = const_cast*>(this)->get_property_container().template get_property_if_exists(name); - if (!maybe_property_map) return {}; - else return {{maybe_property_map.value()}}; - } + template + std::optional> + get_property_map(const std::string& name) const { + auto maybe_property_map = const_cast*>(this)->get_property_container().template get_property_if_exists(name); + if (!maybe_property_map) return {}; + else return {{maybe_property_map.value()}}; + } - /// removes property map `p`. The memory allocated for that property map is - /// freed. - template - void remove_property_map(Property_map p) { - // Maybe this could be replaced with removal by name? - const_cast*>(this)->get_property_container().template remove_property(p.array()); - } + /// removes property map `p`. The memory allocated for that property map is + /// freed. + template + void remove_property_map(Property_map p) { + // Maybe this could be replaced with removal by name? + const_cast*>(this)->get_property_container().template remove_property(p.array()); + } - /// @cond CGAL_DOCUMENT_INTERNALS - /// returns the std::type_info of the value type of the - /// property identified by `name`. `typeid(void)` if `name` - /// does not identify any property. - /// - /// @tparam I The key type of the property. + /// @cond CGAL_DOCUMENT_INTERNALS + /// returns the std::type_info of the value type of the + /// property identified by `name`. `typeid(void)` if `name` + /// does not identify any property. + /// + /// @tparam I The key type of the property. - template - const std::type_info& property_type(const std::string& name) { - return get_property_container().property_type(name); - } - /// @endcond + template + const std::type_info& property_type(const std::string& name) + { + return get_property_container().property_type(name); + } + /// @endcond - /// returns a vector with all strings that describe properties with the key type `I`. - /// @tparam I The key type of the properties. - template - std::vector properties() const { - return get_property_container().properties(); - } + /// returns a vector with all strings that describe properties with the key type `I`. + /// @tparam I The key type of the properties. + template + std::vector properties() const + { + return get_property_container().properties(); + } - /// returns the property for the string "v:point". - // todo: shouldn't this return a const pmap? - // In the original version, there was no difference between const & non-const maps - Property_array& - points() const { return vpoint_; } + /// returns the property for the string "v:point". + // todo: shouldn't this return a const pmap? + // In the original version, there was no difference between const & non-const maps + Property_array& + points() const { return vpoint_; } - Property_array& - points() { return vpoint_; } + Property_array& + points() { return vpoint_; } - /// returns the point associated to vertex `v`. - const Point& - point(Vertex_index v) const { return vpoint_[v]; } + /// returns the point associated to vertex `v`. + const Point& + point(Vertex_index v) const { return vpoint_[v]; } - /// returns the point associated to vertex `v`. - Point& - point(Vertex_index v) { return vpoint_[v]; } + /// returns the point associated to vertex `v`. + Point& + point(Vertex_index v) { return vpoint_[v]; } - /// @cond CGAL_DOCUMENT_INTERNALS - /// prints property statistics to the stream `out`. The output is human-readable but - /// not machine-friendly. - /// - void property_stats(std::ostream& out = std::cout) const; - /// @endcond - ///@} + /// @cond CGAL_DOCUMENT_INTERNALS + /// prints property statistics to the stream `out`. The output is human-readable but + /// not machine-friendly. + /// + void property_stats(std::ostream& out = std::cout) const; + /// @endcond + ///@} - /// \name Null Elements - ///@{ + /// \name Null Elements + ///@{ /// returns `Vertex_index(std::numeric_limits::%max())`. - static Vertex_index null_vertex() { + static Vertex_index null_vertex() + { return vertex_index((std::numeric_limits::max)()); } /// returns `Edge_index(std::numeric_limits::%max())`. - static Edge_index null_edge() { + static Edge_index null_edge() + { return edge_index((std::numeric_limits::max)()); } - /// returns `Halfedge_index(std::numeric_limits::%max())`. - static Halfedge_index null_halfedge() { + static Halfedge_index null_halfedge() + { return halfedge_index((std::numeric_limits::max)()); } - /// returns `Face_index(std::numeric_limits::%max())`. - static Face_index null_face() { + static Face_index null_face() + { return face_index((std::numeric_limits::max)()); } /// @} #if defined(CGAL_SURFACE_MESH_TEST_SUITE) - - std::vector vertex_freelist() const { + std::vector vertex_freelist() const + { return vprops_.inactive_list(); } - std::vector face_freelist() const { + std::vector face_freelist() const + { return fprops_.inactive_list(); } - std::vector edge_freelist() const { + std::vector edge_freelist() const + { return eprops_.inactive_list(); } - #endif private: //--------------------------------------------------- helper functions - /// make sure that the incoming halfedge of vertex v is a border halfedge - /// if `v` is a border vertex. - void adjust_incoming_halfedge(Vertex_index v); + /// make sure that the incoming halfedge of vertex v is a border halfedge + /// if `v` is a border vertex. + void adjust_incoming_halfedge(Vertex_index v); private: //------------------------------------------------------- private data - Property_container vprops_; - Property_container hprops_; - Property_container eprops_; - Property_container fprops_; + Property_container vprops_; + Property_container hprops_; + Property_container eprops_; + Property_container fprops_; - Property_array& vconn_; - Property_array& hconn_; - Property_array& fconn_; + Property_array& vconn_; + Property_array& hconn_; + Property_array& fconn_; - Property_array& vpoint_; + Property_array& vpoint_; - size_type vertices_freelist_; - size_type edges_freelist_; - size_type faces_freelist_; - bool recycle_ = true; + size_type vertices_freelist_; + size_type edges_freelist_; + size_type faces_freelist_; + bool recycle_ = true; - size_type anonymous_property_; + size_type anonymous_property_; }; -/*! \addtogroup PkgSurface_mesh - * - * @{ - */ - -/// \relates Surface_mesh -/// Inserts `other` into `sm`. -/// Shifts the indices of vertices of `other` by `sm.number_of_vertices() + sm.number_of_removed_vertices()` -/// and analogously for halfedges, edges, and faces. -/// Copies entries of all property maps which have the same name in `sm` and `other`. -/// that is, property maps which are only in `other` are ignored. -/// Also copies elements which are marked as removed, and concatenates the freelists of `sm` and `other`. - -template -Surface_mesh

& operator+=(Surface_mesh

& sm, const Surface_mesh

& other) { - sm.join(other); - return sm; -} + /*! \addtogroup PkgSurface_mesh + * + * @{ + */ + + /// \relates Surface_mesh + /// Inserts `other` into `sm`. + /// Shifts the indices of vertices of `other` by `sm.number_of_vertices() + sm.number_of_removed_vertices()` + /// and analogously for halfedges, edges, and faces. + /// Copies entries of all property maps which have the same name in `sm` and `other`. + /// that is, property maps which are only in `other` are ignored. + /// Also copies elements which are marked as removed, and concatenates the freelists of `sm` and `other`. + + template + Surface_mesh

& operator+=(Surface_mesh

& sm, const Surface_mesh

& other) + { + sm.join(other); + return sm; + } -/// \relates Surface_mesh -/// -/// This operator calls `write_OFF(std::ostream& os, const CGAL::Surface_mesh& sm)`. -template -std::ostream& operator<<(std::ostream& os, const Surface_mesh

& sm) { - IO::write_OFF(os, sm); - return os; -} + /// \relates Surface_mesh + /// + /// This operator calls `write_OFF(std::ostream& os, const CGAL::Surface_mesh& sm)`. + template + std::ostream& operator<<(std::ostream& os, const Surface_mesh

& sm) + { + IO::write_OFF(os, sm); + return os; + } -/// \relates Surface_mesh -/// Extracts the surface mesh from an input stream in OFF -/// and appends it to the surface mesh `sm`. -/// -/// This operator calls `read_OFF(std::istream& is, CGAL::Surface_mesh& sm)`. -template -std::istream& operator>>(std::istream& is, Surface_mesh

& sm) { - IO::read_OFF(is, sm); - return is; -} + /// \relates Surface_mesh + /// Extracts the surface mesh from an input stream in OFF + /// and appends it to the surface mesh `sm`. + /// + /// This operator calls `read_OFF(std::istream& is, CGAL::Surface_mesh& sm)`. + template + std::istream& operator>>(std::istream& is, Surface_mesh

& sm) + { + IO::read_OFF(is, sm); + return is; + } -/*! @} */ + /*! @} */ //----------------------------------------------------------------------------- template Surface_mesh

& Surface_mesh

:: -operator=(const Surface_mesh

& rhs) { - if (this != &rhs) { - - // Deep copy of properties - vprops_ = rhs.vprops_; - hprops_ = rhs.hprops_; - eprops_ = rhs.eprops_; - fprops_ = rhs.fprops_; - - // Property array refs don't need to be reassigned, - // because the deep copy updated the values they point to - - - // how many elements are removed? - vertices_freelist_ = rhs.vertices_freelist_; - edges_freelist_ = rhs.edges_freelist_; - faces_freelist_ = rhs.faces_freelist_; - recycle_ = rhs.recycle_; - anonymous_property_ = rhs.anonymous_property_; - } +operator=(const Surface_mesh

& rhs) +{ + if (this != &rhs) + { + // deep copy of property containers + vprops_ = rhs.vprops_; + hprops_ = rhs.hprops_; + eprops_ = rhs.eprops_; + fprops_ = rhs.fprops_; + + // Property array refs don't need to be reassigned, + // because the deep copy updated the values they point to + + + // how many elements are removed? + vertices_freelist_ = rhs.vertices_freelist_; + edges_freelist_ = rhs.edges_freelist_; + faces_freelist_ = rhs.faces_freelist_; + recycle_ = rhs.recycle_; + anonymous_property_ = rhs.anonymous_property_; + } - return *this; + return *this; } //----------------------------------------------------------------------------- @@ -2100,28 +2176,29 @@ operator=(const Surface_mesh

& rhs) { template void Surface_mesh

:: -property_stats(std::ostream& out) const { - std::vector props; - - out << "vertex properties:\n"; - props = properties(); - for (unsigned int i = 0; i < props.size(); ++i) - out << "\t" << props[i] << std::endl; - - out << "halfedge properties:\n"; - props = properties(); - for (unsigned int i = 0; i < props.size(); ++i) - out << "\t" << props[i] << std::endl; - - out << "edge properties:\n"; - props = properties(); - for (unsigned int i = 0; i < props.size(); ++i) - out << "\t" << props[i] << std::endl; - - out << "face properties:\n"; - props = properties(); - for (unsigned int i = 0; i < props.size(); ++i) - out << "\t" << props[i] << std::endl; +property_stats(std::ostream& out) const +{ + std::vector props; + + out << "vertex properties:\n"; + props = properties(); + for (unsigned int i=0; i(); + for (unsigned int i=0; i(); + for (unsigned int i=0; i(); + for (unsigned int i=0; i typename Surface_mesh

::Halfedge_index Surface_mesh

:: -halfedge(Vertex_index source, Vertex_index target) const { - CGAL_assertion(has_valid_index(source) && has_valid_index(target)); +halfedge(Vertex_index source, Vertex_index target) const +{ + CGAL_assertion(has_valid_index(source) && has_valid_index(target)); - Halfedge_index h = halfedge(target); - const Halfedge_index hh = h; + Halfedge_index h = halfedge(target); + const Halfedge_index hh = h; - if (h.is_valid()) { - do { - if (this->source(h) == source) - return h; - h = next_around_target(h); - } while (h != hh); - } + if (h.is_valid()) + { + do + { + if (this->source(h) == source) + return h; + h = next_around_target(h); + } + while (h != hh); + } - return Halfedge_index(); + return Halfedge_index(); } @@ -2151,59 +2232,67 @@ halfedge(Vertex_index source, Vertex_index target) const { template void Surface_mesh

:: -adjust_incoming_halfedge(Vertex_index v) { - Halfedge_index h = halfedge(v); - Halfedge_index hh = h; +adjust_incoming_halfedge(Vertex_index v) +{ + Halfedge_index h = halfedge(v); + Halfedge_index hh = h; - if (h.is_valid()) { - if (target(h) != v) { - // wrong target, flip - h = opposite(h); - hh = h; - set_halfedge(v, h); - } + if (h.is_valid()) + { + if (target(h) != v) + { + // wrong target, flip + h = opposite(h); + hh = h; + set_halfedge(v, h); + } - do { - if (is_border(h)) { - set_halfedge(v, h); - return; - } - h = next_around_target(h); - } while (h != hh); - } + do + { + if (is_border(h)) + { + set_halfedge(v, h); + return; + } + h = next_around_target(h); + } + while (h != hh); + } } //----------------------------------------------------------------------------- -/// @cond CGAL_DOCUMENT_INTERNALS + /// @cond CGAL_DOCUMENT_INTERNALS template template typename Surface_mesh

::Face_index -Surface_mesh

::add_face(const Range& r) { +Surface_mesh

::add_face(const Range& r) +{ return CGAL::Euler::add_face(r, *this); } -/// @endcond + /// @endcond //----------------------------------------------------------------------------- template typename Surface_mesh

::size_type Surface_mesh

:: -degree(Vertex_index v) const { - Halfedge_index h = halfedge(v); +degree(Vertex_index v) const +{ + Halfedge_index h = halfedge(v); - if (h == null_halfedge()) { - return 0; - } - size_type count(0); - Halfedge_index done = h; - do { - ++count; - h = opposite(next(h)); - } while (h != done); - - return count; + if(h == null_halfedge()){ + return 0; + } + size_type count(0); + Halfedge_index done = h; + do { + ++count; + h = opposite(next(h)); + }while(h != done); + + return count; } @@ -2211,56 +2300,60 @@ degree(Vertex_index v) const { template typename Surface_mesh

::size_type Surface_mesh

:: -degree(Face_index f) const { - size_type count(0); - if (halfedge(f) == null_halfedge()) { - return 0; - } - Vertex_around_face_circulator fvit(halfedge(f), *this); - Vertex_around_face_circulator fvend = fvit; - if (fvit) - do { - ++count; +degree(Face_index f) const +{ + size_type count(0); + if(halfedge(f) == null_halfedge()){ + return 0; + } + Vertex_around_face_circulator fvit(halfedge(f),*this); + Vertex_around_face_circulator fvend = fvit; + if(fvit) do { + ++count; } while (++fvit != fvend); - return count; + return count; } -namespace internal { -namespace handle { -template <> -struct Hash_functor { - std::size_t - operator()(const SM_Vertex_index i) { - return i; - } -}; +namespace internal{ + namespace handle { + template <> + struct Hash_functor{ + std::size_t + operator()(const SM_Vertex_index i) + { + return i; + } + }; -template <> -struct Hash_functor { - std::size_t - operator()(const SM_Halfedge_index i) { - return i; - } -}; + template <> + struct Hash_functor{ + std::size_t + operator()(const SM_Halfedge_index i) + { + return i; + } + }; -template <> -struct Hash_functor { - std::size_t - operator()(const SM_Edge_index i) { - return i; - } -}; + template <> + struct Hash_functor{ + std::size_t + operator()(const SM_Edge_index i) + { + return i; + } + }; -template <> -struct Hash_functor { - std::size_t - operator()(const SM_Face_index i) { - return i; + template <> + struct Hash_functor{ + std::size_t + operator()(const SM_Face_index i) + { + return i; + } + }; } -}; -} } } // namespace CGAL @@ -2276,42 +2369,45 @@ namespace std { #ifndef CGAL_CFG_NO_STD_HASH -template <> -struct hash - : public CGAL::cpp98::unary_function { + template <> + struct hash + : public CGAL::cpp98::unary_function { - std::size_t operator()(const CGAL::SM_Halfedge_index& i) const { - return i; - } -}; - -template <> -struct hash - : public CGAL::cpp98::unary_function { + std::size_t operator()(const CGAL::SM_Halfedge_index& i) const + { + return i; + } + }; - std::size_t operator()(const CGAL::SM_Vertex_index& i) const { - return i; - } -}; + template <> + struct hash + : public CGAL::cpp98::unary_function { -template <> -struct hash - : public CGAL::cpp98::unary_function { + std::size_t operator()(const CGAL::SM_Vertex_index& i) const + { + return i; + } + }; - std::size_t operator()(const CGAL::SM_Face_index& i) const { - return i; - } -}; + template <> + struct hash + : public CGAL::cpp98::unary_function { -template <> -struct hash - : public CGAL::cpp98::unary_function { + std::size_t operator()(const CGAL::SM_Face_index& i) const + { + return i; + } + }; - std::size_t operator()(const CGAL::SM_Edge_index& i) const { - return i; - } -}; + template <> + struct hash + : public CGAL::cpp98::unary_function { + std::size_t operator()(const CGAL::SM_Edge_index& i) const + { + return i; + } + }; #endif // CGAL_CFG_NO_STD_HASH #if defined(BOOST_MSVC) @@ -2321,12 +2417,13 @@ struct hash } // namespace std namespace boost { -template <> -struct hash { - std::size_t operator()(const CGAL::SM_Vertex_index& i) const { - return i; - } -}; + template <> + struct hash { + std::size_t operator()(const CGAL::SM_Vertex_index& i) const + { + return i; + } + }; } // namespace boost From 6963e7f973c09e0fcc4dd12e98b0796a7047ec09 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 5 Oct 2023 13:57:01 +0200 Subject: [PATCH 262/520] fix emplace_group on MSVC --- Property_map/include/CGAL/Property_container.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 35887a1ee435..83d6e81cde2d 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -106,7 +106,7 @@ class Property_array : public Property_array_base { } virtual void move(Property_array_base&& other_base) override { - auto&& other = dynamic_cast&&>(other_base); + auto&& other = static_cast&&>(other_base); m_data = std::move(other.m_data); CGAL_precondition(m_active_indices.size() == m_data.size()); } @@ -489,10 +489,13 @@ class Property_container { [](bool used) { return !used; } ); + auto unused_end = unused_begin; + // Determine if the group fits - auto unused_end = std::find_if( - unused_begin, std::min(unused_begin + n, m_active_indices.end()), - [](bool used) { return used; } + if (std::distance(unused_begin, m_active_indices.end()) >= n) + unused_end = std::find_if( + unused_begin, std::min(unused_begin + n, m_active_indices.end()), + [](bool used) { return used; } ); // If the discovered range was large enough From f797ae714f09d0053c7c6edca89632e1105c1cf1 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 5 Oct 2023 13:57:18 +0200 Subject: [PATCH 263/520] fix compilation on MSVC --- Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp | 2 +- Orthtree/examples/Orthtree/octree_grade.cpp | 2 +- Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp index 754fcaa3553b..1081e155df30 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_vector.cpp @@ -23,7 +23,7 @@ int main() { points.emplace_back(-1, 1, 1); // Create an octree from the points - Octree octree({points}); + Octree octree(points); // Build the octree octree.refine(10, 1); diff --git a/Orthtree/examples/Orthtree/octree_grade.cpp b/Orthtree/examples/Orthtree/octree_grade.cpp index ae41079ea87e..5bb410d7e336 100644 --- a/Orthtree/examples/Orthtree/octree_grade.cpp +++ b/Orthtree/examples/Orthtree/octree_grade.cpp @@ -30,7 +30,7 @@ int main() { points.emplace_back(-1.0001, 1, 1); // Create an octree from the points - Octree octree({points}); + Octree octree(points); // Build the octree with a small bucket size, so we get a deep node octree.refine(10, 2); diff --git a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp index 45a9b2a80f50..bd40ace04232 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_from_point_vector.cpp @@ -18,7 +18,7 @@ int main() points_2d.emplace_back(r.get_double(-1., 1.), r.get_double(-1., 1.)); - Quadtree quadtree({points_2d}); + Quadtree quadtree(points_2d); quadtree.refine(10, 5); return EXIT_SUCCESS; From 52e91967d8a67d3491871d38c996c85da8f5ff4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 6 Oct 2023 11:11:40 +0200 Subject: [PATCH 264/520] use new macros --- Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h | 4 +++- Polygon/include/CGAL/Multipolygon_with_holes_2.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index 1c315df5a6bb..8725c35a233a 100644 --- a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -7,7 +7,9 @@ * The concept requires the ability to store the polygons with holes * that the multipolygon is composed of. * - * \cgalHasModel `CGAL::Multipolygon_with_holes_2` + * \cgalHasModelsBegin + * \cgalHasModel{CGAL::Multipolygon_with_holes_2} + * \cgalHasModelsEnd */ class MultipolygonWithHoles_2 { diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index d2744339c7ef..ede241a6779e 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -28,7 +28,7 @@ namespace CGAL { * the types `Polygon_2` and `Polygon_with_holes_2`. * The latter is used to represent each polygon with holes. The former is converted to the latter. * - * \cgalModels `MultipolygonWithHoles_2` + * \cgalModels{MultipolygonWithHoles_2} */ template > From be2ccc2ee2a987c461ff303a72a2669ff3d60cbf Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 6 Oct 2023 10:59:46 +0100 Subject: [PATCH 265/520] typo --- Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index 8725c35a233a..c39c76680da3 100644 --- a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -8,7 +8,7 @@ * that the multipolygon is composed of. * * \cgalHasModelsBegin - * \cgalHasModel{CGAL::Multipolygon_with_holes_2} + * \cgalHasModels{CGAL::Multipolygon_with_holes_2} * \cgalHasModelsEnd */ From 1416ddfd2d67574ae592bf0da63d7af221ef8178 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 6 Oct 2023 13:40:32 +0100 Subject: [PATCH 266/520] No --- .../doc/Polygon_repair/Polygon_repair.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 85a71fdc0767..4a875571280e 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -25,16 +25,16 @@ from those of inner boundaries. \section SectionPolygonRepair_Definitions Definitions -- A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ +- A valid polygon (without holes) is a point set in \f$ \mathbb{R}^2\f$ that is bounded by a cycle of linear edges, which is known as its -outer boundary. This outer boundary should be simple, +outer boundary. This outer boundary should be simple, meaning that the interiors of its edges are pairwise disjoint and all of its vertices have a degree of two. It is thus topologically equivalent to a disk and is represented internally as the sequence of points at the common end points of the edges around its outer boundary. -- A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ -that is bounded by one outer boundary and zero or more inner boundaries, +- A valid polygon with holes is a point set in \f$ \mathbb{R}^2\f$ +that is bounded by one outer boundary and zero or more inner boundaries, where each inner boundary represents a hole in the polygon. Considered independently, each boundary should be simple. The different boundaries of a polygon are allowed to intersect tangentially at their common vertices (with no common @@ -43,7 +43,7 @@ The interior of a polygon with holes should form a connected point set. Note that a valid polygon can also be represented as a valid polygon with holes (where the number of holes is zero). -- A valid multipolygon with holes is a point set in \f$ \mathbb{R}^2\f$ +- A valid multipolygon with holes is a point set in \f$ \mathbb{R}^2\f$ that is represented by a set of zero or more valid polygons with holes. The interiors of the polygons with holes should be pairwise disjoint, but they are allowed to intersect tangentially at their common vertices. Note that @@ -102,7 +102,7 @@ Examples of polygons with holes (a-d) and multipolygons with holes For the purposes of the repair operation, the input polygon, polygon with holes or multipolygon is merely used as a container of input line segments. These line segments are added to the arrangement as edges. Internally, this is done using -a constrained triangulation where they added as constraints. +a constrained triangulation where they are added as constraints. With the odd-even heuristic, only the edges that are present an odd number of times in the input will be edges in the final arrangement. @@ -149,9 +149,9 @@ with holes has zero holes and extract these if needed. \subsection SubsectionPolygonRepair_Repair Repairing a (Multi)polygon -It's possible to repair a polygon, polygon with holes or multipolygon with holes -using the odd-even rule by calling the `repair_odd_even` function as shown in the -following example. This function returns a repaired multipolygon with holes. +It is possible to repair a polygon, polygon with holes or multipolygon with holes +using the odd-even rule by calling the `Polygon_repair::repair_odd_even()` function +as shown in the following example. This function returns a repaired multipolygon with holes. \cgalExample{Polygon_repair/repair_polygon_2.cpp} From 455c6324b6d3adbf438de37431df8d426168f8bb Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 6 Oct 2023 13:46:59 +0100 Subject: [PATCH 267/520] labelled -> labeled --- .../include/CGAL/Polygon_repair/Polygon_repair.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index aa49e1857b05..a11ba0f35647 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -129,7 +129,7 @@ bool is_valid(const Polygon_with_holes_2& polygon) { CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior int regions = 0, holes = 0; while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; @@ -172,7 +172,7 @@ bool is_valid(const Polygon_with_holes_2& polygon) { regions = 0; holes = 0; while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; @@ -215,7 +215,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior int regions = 0, holes = 0; while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; @@ -227,7 +227,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo to_check_added_by.pop_front(); } - // Test vertices in labelled triangulation + // Test vertices in labeled triangulation for (auto const& vertex: polygon.outer_boundary().vertices()) { typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { @@ -491,7 +491,7 @@ class Polygon_repair { while (!to_check_in_region.empty()) { for (int neighbour = 0; neighbour < 3; ++neighbour) { if (!tt.is_constrained(typename Triangulation::Edge(to_check_in_region.front(), neighbour))) { - if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { // unlabelled + if (to_check_in_region.front()->neighbor(neighbour)->label() == 0) { // unlabeled to_check_in_region.front()->neighbor(neighbour)->label() = label; to_check_in_region.push_back(to_check_in_region.front()->neighbor(neighbour)); to_check_in_region.front()->neighbor(neighbour)->processed() = true; @@ -550,7 +550,7 @@ class Polygon_repair { // Label region of front element to_check list while (!to_check.empty()) { - if (to_check.front()->label() == 0) { // label = 0 means not labelled yet + if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { label_region(t, to_check.front(), number_of_polygons+1, to_check, to_check_added_by); ++number_of_polygons; From 4f9dd85aaa1a328ed536c607f733c5b430b61d72 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 6 Oct 2023 14:04:02 +0100 Subject: [PATCH 268/520] PolygonContainer -> Container --- .../CGAL/Polygon_repair/Polygon_repair.h | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index a11ba0f35647..5264913eb03f 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -27,14 +27,14 @@ namespace CGAL { namespace Polygon_repair { -template +template class Polygon_repair; /// \ingroup PkgPolygonRepairFunctions /// Repair a polygon without holes using the odd-even heuristic -template -Multipolygon_with_holes_2 repair_odd_even(const Polygon_2& p) { - CGAL::Polygon_repair::Polygon_repair pr; +template +Multipolygon_with_holes_2 repair_odd_even(const Polygon_2& p) { + CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_odd_even(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_odd_even(); @@ -44,9 +44,9 @@ Multipolygon_with_holes_2 repair_odd_even(const Polygo /// \ingroup PkgPolygonRepairFunctions /// Repair a polygon with holes using the odd-even heuristic -template -Multipolygon_with_holes_2 repair_odd_even(const Polygon_with_holes_2& p) { - CGAL::Polygon_repair::Polygon_repair pr; +template +Multipolygon_with_holes_2 repair_odd_even(const Polygon_with_holes_2& p) { + CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_odd_even(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_odd_even(); @@ -56,9 +56,9 @@ Multipolygon_with_holes_2 repair_odd_even(const Polygo /// \ingroup PkgPolygonRepairFunctions /// Repair a multipolygon with holes using the odd-even heuristic -template -Multipolygon_with_holes_2 repair_odd_even(const Multipolygon_with_holes_2& mp) { - CGAL::Polygon_repair::Polygon_repair pr; +template +Multipolygon_with_holes_2 repair_odd_even(const Multipolygon_with_holes_2& mp) { + CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_odd_even(mp); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_odd_even(); @@ -66,8 +66,8 @@ Multipolygon_with_holes_2 repair_odd_even(const Multip } return pr.multipolygon(); } -template -bool is_valid(const Polygon_2& polygon) { +template +bool is_valid(const Polygon_2& polygon) { if (polygon.vertices().size() < 3) { std::cout << "Invalid: less than 3 vertices" << std::endl; return false; @@ -82,8 +82,8 @@ bool is_valid(const Polygon_2& polygon) { } return true; } -template -bool is_valid(const Polygon_with_holes_2& polygon) { +template +bool is_valid(const Polygon_with_holes_2& polygon) { // Validate outer boundary for (auto const& edge: polygon.outer_boundary().edges()) { @@ -110,11 +110,11 @@ bool is_valid(const Polygon_with_holes_2& polygon) { } // Create triangulation of outer boundary - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; for (auto const& edge: polygon.outer_boundary().edges()) { try { vt.insert_constraint(edge.source(), edge.target()); - } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { std::cout << "Invalid: intersection in outer boundary" << std::endl; return false; } @@ -124,17 +124,17 @@ bool is_valid(const Polygon_with_holes_2& polygon) { } for (auto const face: vt.all_face_handles()) { face->label() = 0; face->processed() = false; - } std::list::Validation_triangulation::Face_handle> to_check; + } std::list::Validation_triangulation::Face_handle> to_check; std::list to_check_added_by; - CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior int regions = 0, holes = 0; while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { - CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; } else { - CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); ++holes; } } to_check.pop_front(); @@ -144,10 +144,10 @@ bool is_valid(const Polygon_with_holes_2& polygon) { // Hole nesting for (auto const& hole: polygon.holes()) { for (auto const& vertex: hole.vertices()) { - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; int li; - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != 1) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() != 1) { std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; return false; } @@ -155,7 +155,7 @@ bool is_valid(const Polygon_with_holes_2& polygon) { for (auto const& edge: hole.edges()) { try { vt.insert_constraint(edge.source(), edge.target()); - } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { std::cout << "Invalid: hole (partly) outside outer boundary" << std::endl; return false; } @@ -168,16 +168,16 @@ bool is_valid(const Polygon_with_holes_2& polygon) { face->processed() = false; } to_check.clear(); to_check_added_by.clear(); - CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior regions = 0; holes = 0; while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { - CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; } else { - CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); ++holes; } } to_check.pop_front(); @@ -190,16 +190,16 @@ bool is_valid(const Polygon_with_holes_2& polygon) { return true; } -template -bool is_valid(const Multipolygon_with_holes_2& multipolygon) { +template +bool is_valid(const Multipolygon_with_holes_2& multipolygon) { // Validate polygons for (auto const& polygon: multipolygon.polygons()) { if (!is_valid(polygon)) return false; } - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; int li; for (auto const& polygon: multipolygon.polygons()) { @@ -210,17 +210,17 @@ bool is_valid(const Multipolygon_with_holes_2& multipo for (auto const face: vt.all_face_handles()) { face->label() = 0; face->processed() = false; - } std::list::Validation_triangulation::Face_handle> to_check; + } std::list::Validation_triangulation::Face_handle> to_check; std::list to_check_added_by; - CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior + CGAL::Polygon_repair::Polygon_repair::label_region(vt, vt.infinite_face(), -1, to_check, to_check_added_by); // exterior int regions = 0, holes = 0; while (!to_check.empty()) { if (to_check.front()->label() == 0) { // label = 0 means not labeled yet if (to_check_added_by.front() < 0) { - CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), regions+1, to_check, to_check_added_by); ++regions; } else { - CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); + CGAL::Polygon_repair::Polygon_repair::label_region(vt, to_check.front(), -(holes+2), to_check, to_check_added_by); ++holes; } } to_check.pop_front(); @@ -229,16 +229,16 @@ bool is_valid(const Multipolygon_with_holes_2& multipo // Test vertices in labeled triangulation for (auto const& vertex: polygon.outer_boundary().vertices()) { - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } } for (auto const& hole: polygon.holes()) { for (auto const& vertex: hole.vertices()) { - typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); - if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { + typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Face_handle f = vt.locate(vertex, lt, li); + if (lt == CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type::FACE && f->label() > 0) { std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } @@ -251,7 +251,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo for (auto const& edge: polygon.outer_boundary().edges()) { try { vt.insert_constraint(edge.source(), edge.target()); - } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } @@ -260,7 +260,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo for (auto const& edge: hole.edges()) { try { vt.insert_constraint(edge.source(), edge.target()); - } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { + } catch (typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Intersection_of_constraints_exception ice) { std::cout << "Invalid: (partly) overlapping polygons" << std::endl; return false; } @@ -271,7 +271,7 @@ bool is_valid(const Multipolygon_with_holes_2& multipo return true; } -template > +template > class Polygon_repair { public: using Vertex_base = CGAL::Triangulation_vertex_base_2; @@ -296,7 +296,7 @@ class Polygon_repair { using Validation_triangulation = CGAL::Constrained_triangulation_2; struct Polygon_less { - using Polygon_2 = CGAL::Polygon_2; + using Polygon_2 = CGAL::Polygon_2; bool operator()(const Polygon_2& pa, const Polygon_2& pb) const { typename Polygon_2::Vertex_iterator va = pa.vertices_begin(); typename Polygon_2::Vertex_iterator vb = pb.vertices_begin(); @@ -311,7 +311,7 @@ class Polygon_repair { }; struct Polygon_with_holes_less { - using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; + using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; Polygon_less pl; bool operator()(const Polygon_with_holes_2& pa, const Polygon_with_holes_2& pb) const { if (pl(pa.outer_boundary(), pb.outer_boundary())) return true; @@ -334,7 +334,7 @@ class Polygon_repair { /// @{ // Add edges of the polygon to the triangulation - void add_to_triangulation_odd_even(const Polygon_2& polygon) { + void add_to_triangulation_odd_even(const Polygon_2& polygon) { // Get unique edges for (auto const& edge: polygon.edges()) { @@ -375,7 +375,7 @@ class Polygon_repair { } // Add edges of the polygon to the triangulation - void add_to_triangulation_odd_even(const Polygon_with_holes_2& polygon) { + void add_to_triangulation_odd_even(const Polygon_with_holes_2& polygon) { // Get unique edges for (auto const& edge: polygon.outer_boundary().edges()) { @@ -425,7 +425,7 @@ class Polygon_repair { } // Add edges of the polygon to the triangulation - void add_to_triangulation_odd_even(const Multipolygon_with_holes_2& multipolygon) { + void add_to_triangulation_odd_even(const Multipolygon_with_holes_2& multipolygon) { // Get unique edges for (auto const& polygon: multipolygon.polygons()) { @@ -602,8 +602,8 @@ class Polygon_repair { // Reconstruct multipolygon based on the triangles labeled as inside the polygon void reconstruct_multipolygon() { mp.clear(); - std::vector> polygons; // outer boundaries - std::vector, Polygon_less>> holes; // holes are ordered (per polygon) + std::vector> polygons; // outer boundaries + std::vector, Polygon_less>> holes; // holes are ordered (per polygon) polygons.resize(number_of_polygons); holes.resize(number_of_polygons); @@ -621,7 +621,7 @@ class Polygon_repair { reconstruct_ring(ring, face, opposite_vertex); // Put ring in polygons - Polygon_2 polygon(ring.begin(), ring.end()); + Polygon_2 polygon(ring.begin(), ring.end()); // std::cout << "Reconstructed ring for polygon " << face->label() << " with ccw? " << (polygon.orientation() == CGAL::COUNTERCLOCKWISE) << std::endl; if (polygon.orientation() == CGAL::COUNTERCLOCKWISE) { polygons[face->label()-1] = polygon; @@ -632,9 +632,9 @@ class Polygon_repair { } // Create polygons with holes and put in multipolygon - std::set, Polygon_with_holes_less> ordered_polygons; + std::set, Polygon_with_holes_less> ordered_polygons; for (int i = 0; i < polygons.size(); ++i) { - ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); + ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); } for (auto const& polygon: ordered_polygons) { // std::cout << "Adding polygon " << polygon << std::endl; @@ -661,7 +661,7 @@ class Polygon_repair { return t; } - Multipolygon_with_holes_2 multipolygon() { + Multipolygon_with_holes_2 multipolygon() { return mp; } @@ -671,7 +671,7 @@ class Polygon_repair { protected: Triangulation t; Edge_map unique_edges; - Multipolygon_with_holes_2 mp; + Multipolygon_with_holes_2 mp; int number_of_polygons, number_of_holes; typename Triangulation::Face_handle search_start; }; From 55c564558f8eb1cb7c82d2bb0bc07619b01e24e3 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 6 Oct 2023 14:15:02 +0100 Subject: [PATCH 269/520] Move code to internal folder/namespace --- .../include/CGAL/Polygon_repair/Polygon_repair.h | 8 ++++---- .../Triangulation_face_base_with_repair_info_2.h | 4 ++++ .../Triangulation_with_odd_even_constraints_2.h | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) rename Polygon_repair/include/CGAL/Polygon_repair/{ => internal}/Triangulation_face_base_with_repair_info_2.h (94%) rename Polygon_repair/include/CGAL/Polygon_repair/{ => internal}/Triangulation_with_odd_even_constraints_2.h (97%) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 5264913eb03f..e703ce7e842b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -20,8 +20,8 @@ #include #include -#include -#include +#include +#include namespace CGAL { @@ -276,13 +276,13 @@ class Polygon_repair { public: using Vertex_base = CGAL::Triangulation_vertex_base_2; using Face_base = CGAL::Constrained_triangulation_face_base_2; - using Face_base_with_repair_info = CGAL::Triangulation_face_base_with_repair_info_2; + using Face_base_with_repair_info = internal::Triangulation_face_base_with_repair_info_2; using Triangulation_data_structure = CGAL::Triangulation_data_structure_2; using Tag = typename std::conditional::value, CGAL::Exact_predicates_tag, CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; - using Triangulation = Triangulation_with_odd_even_constraints_2; + using Triangulation = internal::Triangulation_with_odd_even_constraints_2; // TODO: Edge_map and Vertex_map use std::set and set::map with exact kernels since Point_2 can't be hashed otherwise using Edge_map = typename std::conditional::value, std::unordered_set, diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_face_base_with_repair_info_2.h b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_face_base_with_repair_info_2.h similarity index 94% rename from Polygon_repair/include/CGAL/Polygon_repair/Triangulation_face_base_with_repair_info_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_face_base_with_repair_info_2.h index 61389d4e33ee..0f1482795486 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_face_base_with_repair_info_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_face_base_with_repair_info_2.h @@ -17,6 +17,8 @@ #include namespace CGAL { +namespace Polygon_repair { +namespace internal { template > class Triangulation_face_base_with_repair_info_2 : public FaceBase { @@ -47,6 +49,8 @@ class Triangulation_face_base_with_repair_info_2 : public FaceBase { int& label() { return _label; } }; +} // namespace internal +} // namespace Polygon_repair } //namespace CGAL #endif // CGAL_TRIANGULATION_WITH_REPAIR_INFO_2_H diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_odd_even_constraints_2.h similarity index 97% rename from Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_odd_even_constraints_2.h index 3d5dd2a77e47..563527a48fb1 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_odd_even_constraints_2.h @@ -16,6 +16,8 @@ #include namespace CGAL { +namespace Polygon_repair { +namespace internal { template class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { @@ -122,6 +124,8 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { } }; +} // namespace internal +} // namespace Polygon_repair } // namespace CGAL #endif // CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H From 2f9535d769b99df77a2834276d144c9d6efa0fa4 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 6 Oct 2023 14:40:32 +0100 Subject: [PATCH 270/520] no concepts, export->insert in a stream, capitalize --- Polygon/doc/Polygon/Polygon.txt | 2 +- Polygon/include/CGAL/Multipolygon_with_holes_2.h | 10 +++++----- .../doc/Polygon_repair/PackageDescription.txt | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Polygon/doc/Polygon/Polygon.txt b/Polygon/doc/Polygon/Polygon.txt index 284157f95ef7..6383cdad10cc 100644 --- a/Polygon/doc/Polygon/Polygon.txt +++ b/Polygon/doc/Polygon/Polygon.txt @@ -59,7 +59,7 @@ some member functions. \cgalExample{Polygon/Polygon.cpp} -\subsection SubsectionPolygonRepair_Multipolygon The multipolygon with holes class +\subsection SubsectionPolygonRepair_Multipolygon The Multipolygon with Holes Class The following example shows the creation of a multipolygon with holes and the traversal of the polygons in it. diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index ede241a6779e..55162b50df16 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -92,7 +92,7 @@ class Multipolygon_with_holes_2 { }; /*! -exports a multipolygon with holes to the output stream `os`. +inserts a multipolygon with holes to the output stream `os`. An \ascii and a binary format exist. The format can be selected with the \cgal modifiers for streams, `set_ascii_mode()` and `set_binary_mode()`, @@ -100,11 +100,11 @@ respectively. The modifier `set_pretty_mode()` can be used to allow for (a few) structuring comments in the output. Otherwise, the output would be free of comments. The default for writing is \ascii without comments. -The number of polygons is exported followed by the polygons. For each polygon, -the number of points of the outer boundary is exported followed by the +The number of polygons is written followed by the polygons. For each polygon, +the number of points of the outer boundary is written followed by the points themselves in counterclockwise order. Then, the number of holes -is exported, and for each hole, the number of points on its outer -boundary is exported followed by the points themselves in clockwise +is written, and for each hole, the number of points on its outer +boundary is written followed by the points themselves in clockwise order. \relates Multipolygon_with_holes_2 diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index ce5b791ff5c3..e622d0f23b25 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -2,8 +2,8 @@ /// \defgroup PkgPolygonRepairRef 2D Polygon Repair Reference -/// \defgroup PkgPolygonRepairConcepts Concepts -/// \ingroup PkgPolygonRepairRef +// \defgroup PkgPolygonRepairConcepts Concepts +// \ingroup PkgPolygonRepairRef /// \defgroup PkgPolygonRepairFunctions Functions /// \ingroup PkgPolygonRepairRef From 047a223db0bf6d4ca8e23392d70cd7e041de9682 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 8 Oct 2023 15:05:24 +0200 Subject: [PATCH 271/520] Revert Surface_mesh to the old memory management scheme The new property container system is still used. Beneficial changes to surface mesh and additions to unit tests are preserved. --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- .../include/CGAL/Surface_mesh/Surface_mesh.h | 542 ++++++++++++++---- .../test/Surface_mesh/sm_circulator_test.cpp | 8 +- .../test/Surface_mesh/sm_join_test.cpp | 31 +- Surface_mesh/test/Surface_mesh/sm_remove.cpp | 43 +- 5 files changed, 474 insertions(+), 152 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 1f40d39d5c52..6abe414ea5f3 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -293,4 +293,4 @@ provided kind help and advice all the way through. */ -} +} \ No newline at end of file diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 86713e830d11..7411c8889eec 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -690,7 +690,7 @@ class Surface_mesh /// End iterator for vertices. Vertex_iterator vertices_end() const { - return Vertex_iterator(Vertex_index(number_of_vertices()), this); + return Vertex_iterator(Vertex_index(num_vertices()), this); } /// @endcond @@ -711,7 +711,7 @@ class Surface_mesh /// End iterator for halfedges. Halfedge_iterator halfedges_end() const { - return Halfedge_iterator(Halfedge_index(number_of_halfedges()), this); + return Halfedge_iterator(Halfedge_index(num_halfedges()), this); } /// @endcond @@ -732,7 +732,7 @@ class Surface_mesh /// End iterator for edges. Edge_iterator edges_end() const { - return Edge_iterator(Edge_index(number_of_edges()), this); + return Edge_iterator(Edge_index(num_edges()), this); } /// @endcond @@ -754,7 +754,7 @@ class Surface_mesh /// End iterator for faces. Face_iterator faces_end() const { - return Face_iterator(Face_index(number_of_faces()), this); + return Face_iterator(Face_index(num_faces()), this); } /// @endcond @@ -908,31 +908,58 @@ class Surface_mesh hconn_(hprops_.add_property("h:connectivity")), fconn_(fprops_.add_property("f:connectivity")), vpoint_(vprops_.add_property("v:point")), + vremoved_(vprops_.add_property("v:removed")), + eremoved_(eprops_.add_property("e:removed")), + fremoved_(fprops_.add_property("f:removed")), anonymous_property_(0) {} /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. Surface_mesh(const Surface_mesh& rhs) : - vprops_(rhs.vprops_), - hprops_(rhs.hprops_), - fprops_(rhs.fprops_), - eprops_(rhs.eprops_), - vconn_(vprops_.get_property("v:connectivity")), - vpoint_(vprops_.get_property("v:point")), - hconn_(hprops_.get_property("h:connectivity")), - fconn_(fprops_.get_property("f:connectivity")), - anonymous_property_(0) {} + vprops_(rhs.vprops_) + , hprops_(rhs.hprops_) + , fprops_(rhs.fprops_) + , eprops_(rhs.eprops_) + , vpoint_(vprops_.get_property("v:point")) + , vconn_(vprops_.get_property("v:connectivity")) + , hconn_(hprops_.get_property("h:connectivity")) + , fconn_(fprops_.get_property("f:connectivity")) + , vremoved_(vprops_.get_property("v:removed")) + , eremoved_(eprops_.get_property("e:removed")) + , fremoved_(fprops_.get_property("f:removed")) + , removed_vertices_(rhs.removed_vertices_) + , removed_edges_(rhs.removed_edges_) + , removed_faces_(rhs.removed_faces_) + , vertices_freelist_(rhs.vertices_freelist_) + , edges_freelist_(rhs.edges_freelist_) + , faces_freelist_(rhs.faces_freelist_) + , garbage_(rhs.garbage_) + , recycle_(rhs.recycle_) + , anonymous_property_(rhs.anonymous_property_) + {} /// Move constructor. - Surface_mesh(Surface_mesh&& sm) : - vprops_(std::move(sm.vprops_)), - hprops_(std::move(sm.hprops_)), - eprops_(std::move(sm.eprops_)), - fprops_(std::move(sm.fprops_)), - vconn_(vprops_.get_property("v:connectivity")), - vpoint_(vprops_.get_property("v:point")), - hconn_(hprops_.get_property("h:connectivity")), - fconn_(fprops_.get_property("f:connectivity")), - anonymous_property_(0) {} + Surface_mesh(Surface_mesh&& sm) + : vprops_(std::move(sm.vprops_)) + , hprops_(std::move(sm.hprops_)) + , eprops_(std::move(sm.eprops_)) + , fprops_(std::move(sm.fprops_)) + , vpoint_(vprops_.get_property("v:point")) + , vconn_(vprops_.get_property("v:connectivity")) + , hconn_(hprops_.get_property("h:connectivity")) + , fconn_(fprops_.get_property("f:connectivity")) + , vremoved_(vprops_.get_property("v:removed")) + , eremoved_(eprops_.get_property("e:removed")) + , fremoved_(fprops_.get_property("f:removed")) + , removed_vertices_(std::exchange(sm.removed_vertices_, 0)) + , removed_edges_(std::exchange(sm.removed_edges_, 0)) + , removed_faces_(std::exchange(sm.removed_faces_, 0)) + , vertices_freelist_(std::exchange(sm.vertices_freelist_,(std::numeric_limits::max)())) + , edges_freelist_(std::exchange(sm.edges_freelist_,(std::numeric_limits::max)())) + , faces_freelist_(std::exchange(sm.faces_freelist_,(std::numeric_limits::max)())) + , garbage_(std::exchange(sm.garbage_, false)) + , recycle_(std::exchange(sm.recycle_, true)) + , anonymous_property_(std::exchange(sm.anonymous_property_, 0)) + {} /// assigns `rhs` to `*this`. Performs a deep copy of all properties. Surface_mesh& operator=(const Surface_mesh& rhs); @@ -940,6 +967,7 @@ class Surface_mesh /// move assignment Surface_mesh& operator=(Surface_mesh&& sm) { + // Moving properties also moves their contents without invalidating references vprops_ = std::move(sm.vprops_); hprops_ = std::move(sm.hprops_); eprops_ = std::move(sm.eprops_); @@ -957,10 +985,17 @@ class Surface_mesh /// adds a new vertex, and resizes vertex properties if necessary. Vertex_index add_vertex() { - if(recycle_) - return vprops_.emplace(); - else - return vprops_.emplace_back(); + size_type inf = (std::numeric_limits::max)(); + if(recycle_ && (vertices_freelist_ != inf)){ + size_type idx = vertices_freelist_; + vertices_freelist_ = (size_type)vconn_[Vertex_index(vertices_freelist_)].halfedge_; + --removed_vertices_; + vremoved_[Vertex_index(idx)] = false; + vprops_.reset(Vertex_index(idx)); + return Vertex_index(idx); + } else { + return Vertex_index(vprops_.emplace_back()); + } } /// adds a new vertex, resizes vertex properties if necessary, @@ -980,19 +1015,21 @@ class Surface_mesh /// adds a new edge, and resizes edge and halfedge properties if necessary. Halfedge_index add_edge() { - - // Add properties for a new edge - if (recycle_) - eprops_.emplace(); - else + Halfedge_index h0, h1; + size_type inf = (std::numeric_limits::max)(); + if(recycle_ && (edges_freelist_ != inf)){ + size_type idx = edges_freelist_; + edges_freelist_ = (size_type)hconn_[Halfedge_index(edges_freelist_)].next_halfedge_; + --removed_edges_; + eremoved_[Edge_index(Halfedge_index(idx))] = false; + hprops_.reset(Halfedge_index(idx)); + hprops_.reset(opposite(Halfedge_index(idx))); + eprops_.reset(Edge_index(Halfedge_index(idx))); + return Halfedge_index(idx); + } else { eprops_.emplace_back(); - - // Add properties for a pair of new half-edges - // The new half-edges are placed adjacently, and we return the index of the first - if (recycle_) - return hprops_.emplace_group(2); - else - return hprops_.emplace_group_back(2); + return Halfedge_index(hprops_.emplace_group_back(2)); + } } /// adds two opposite halfedges, and resizes edge and halfedge properties if necessary. @@ -1015,10 +1052,17 @@ class Surface_mesh /// adds a new face, and resizes face properties if necessary. Face_index add_face() { - if(recycle_) - return fprops_.emplace(); - else - return fprops_.emplace_back(); + size_type inf = (std::numeric_limits::max)(); + if(recycle_ && (faces_freelist_ != inf)){ + size_type idx = faces_freelist_; + faces_freelist_ = (size_type)fconn_[Face_index(faces_freelist_)].halfedge_; + --removed_faces_; + fprops_.reset(Face_index(idx)); + fremoved_[Face_index(idx)] = false; + return Face_index(idx); + } else { + return Face_index(fprops_.emplace_back()); + } } /// if possible, adds a new face with vertices from a range with value type `Vertex_index`. @@ -1068,16 +1112,18 @@ class Surface_mesh /// adjusting anything. void remove_vertex(Vertex_index v) { - // todo: confirm this behaves correctly - vprops_.erase(v); + vremoved_[v] = true; ++removed_vertices_; garbage_ = true; + vconn_[v].halfedge_ = Halfedge_index(vertices_freelist_); + vertices_freelist_ = (size_type)v; } /// removes the two halfedges corresponding to `e` from the halfedge data structure without /// adjusting anything. void remove_edge(Edge_index e) { - // todo: confirm this behaves correctly - eprops_.erase(e); + eremoved_[e] = true; ++removed_edges_; garbage_ = true; + hconn_[Halfedge_index((size_type)e << 1)].next_halfedge_ = Halfedge_index(edges_freelist_ ); + edges_freelist_ = ((size_type)e << 1); } /// removes face `f` from the halfedge data structure without @@ -1085,8 +1131,9 @@ class Surface_mesh void remove_face(Face_index f) { - // todo: confirm this behaves correctly - fprops_.erase(f); + fremoved_[f] = true; ++removed_faces_; garbage_ = true; + fconn_[f].halfedge_ = Halfedge_index(faces_freelist_); + faces_freelist_ = (size_type)f; } @@ -1099,51 +1146,45 @@ class Surface_mesh /// allocated for elements, and to clear the structure. ///@{ -#ifndef DOXYGEN_RUNNING - /// returns the number of used and removed vertices in the mesh. - size_type num_vertices() const { return (size_type) vprops_.size(); } - - /// returns the number of used and removed halfedges in the mesh. - size_type num_halfedges() const { return (size_type) hprops_.size(); } - - /// returns the number of used and removed edges in the mesh. - size_type num_edges() const { return (size_type) eprops_.size(); } - - /// returns the number of used and removed faces in the mesh. - size_type num_faces() const { return (size_type) fprops_.size(); } - -#endif - /// returns the number of vertices in the mesh. size_type number_of_vertices() const { - return vprops_.size(); + return num_vertices() - number_of_removed_vertices(); } /// returns the number of halfedges in the mesh. size_type number_of_halfedges() const { - return hprops_.size(); + return num_halfedges() - number_of_removed_halfedges(); } /// returns the number of edges in the mesh. size_type number_of_edges() const { - return eprops_.size(); + return num_edges() - number_of_removed_edges(); } /// returns the number of faces in the mesh. size_type number_of_faces() const { - return fprops_.size(); + return num_faces() - number_of_removed_faces(); } /// returns `true` iff the mesh is empty, i.e., has no vertices, halfedges and faces. bool is_empty() const { - return (vprops_.size() == 0 - && hprops_.size() == 0 - && fprops_.size() == 0); + return ( num_vertices() == number_of_removed_vertices() + && num_halfedges() == number_of_removed_halfedges() + && num_faces() == number_of_removed_faces()); + } + + /// removes all vertices, halfedge, edges and faces. Collects garbage but keeps all property maps. + void clear_without_removing_property_maps() + { + vprops_.reserve(0); + hprops_.reserve(0); + eprops_.reserve(0); + fprops_.reserve(0); } /// removes all vertices, halfedge, edges and faces. Collects garbage and removes all property maps added by a call to `add_property_map()` for all simplex types. @@ -1158,14 +1199,6 @@ class Surface_mesh eprops_.remove_all_properties_except({}); } - void clear_without_removing_property_maps() - { - vprops_.reserve(0); - hprops_.reserve(0); - eprops_.reserve(0); - fprops_.reserve(0); - } - /// reserves space for vertices, halfedges, edges, faces, and their currently /// associated properties. @@ -1179,6 +1212,16 @@ class Surface_mesh fprops_.reserve(nfaces); } + void resize(size_type nvertices, + size_type nedges, + size_type nfaces ) + { + vprops_.resize(nvertices); + hprops_.resize(2*nedges); + eprops_.resize(nedges); + fprops_.resize(nfaces); + } + /// copies the simplices from `other`, and copies values of /// properties that already exist under the same name in `*this`. /// In case `*this` has a property that does not exist in `other` @@ -1195,24 +1238,22 @@ class Surface_mesh fprops_.append(other.fprops_); eprops_.append(other.eprops_); - // todo: the below code assumes no gaps were present in the properties! That might be okay for this situation. - // translate halfedge index in vertex -> halfedge - for(size_type i = nv; i < nv+other.number_of_vertices(); i++){ + for(size_type i = nv; i < nv+other.num_vertices(); i++){ Vertex_index vi(i); if(vconn_[vi].halfedge_ != null_halfedge()){ vconn_[vi].halfedge_ = Halfedge_index(size_type(vconn_[vi].halfedge_)+nh); } } // translate halfedge index in face -> halfedge - for(size_type i = nf; i < nf+other.number_of_faces(); i++){ + for(size_type i = nf; i < nf+other.num_faces(); i++){ Face_index fi(i); if(fconn_[fi].halfedge_ != null_halfedge()){ fconn_[fi].halfedge_ = Halfedge_index(size_type(fconn_[fi].halfedge_)+nh); } } // translate indices in halfedge -> face, halfedge -> target, halfedge -> prev, and halfedge -> next - for(size_type i = nh; i < nh+other.number_of_halfedges(); i++){ + for(size_type i = nh; i < nh+other.num_halfedges(); i++){ Halfedge_index hi(i); if(hconn_[hi].face_ != null_face()){ hconn_[hi].face_ = Face_index(size_type(hconn_[hi].face_)+nf); @@ -1227,6 +1268,55 @@ class Surface_mesh hconn_[hi].prev_halfedge_ = Halfedge_index(size_type(hconn_[hi].prev_halfedge_)+nh); } } + size_type inf_value = (std::numeric_limits::max)(); + + // merge vertex free list + if(other.vertices_freelist_ != inf_value){ + Vertex_index vi(nv+other.vertices_freelist_); + Halfedge_index inf((std::numeric_limits::max)()); + // correct the indices in the linked list of free vertices copied (due to vconn_ translation) + while(vconn_[vi].halfedge_ != inf){ + Vertex_index corrected_vi = Vertex_index(size_type(vconn_[vi].halfedge_)+nv-nh); + vconn_[vi].halfedge_ = Halfedge_index(corrected_vi); + vi = corrected_vi; + } + // append the vertex free linked list of `this` to the copy of `other` + vconn_[vi].halfedge_ = Halfedge_index(vertices_freelist_); + // update the begin of the vertex free linked list + vertices_freelist_ = nv + other.vertices_freelist_; + } + // merge face free list + if(other.faces_freelist_ != inf_value){ + Face_index fi(nf+other.faces_freelist_); + Halfedge_index inf((std::numeric_limits::max)()); + // correct the indices in the linked list of free faces copied (due to fconn_ translation) + while(fconn_[fi].halfedge_ != inf){ + Face_index corrected_fi = Face_index(size_type(fconn_[fi].halfedge_)+nf-nh); + fconn_[fi].halfedge_ = Halfedge_index(corrected_fi); + fi = corrected_fi; + } + // append the face free linked list of `this` to the copy of `other` + fconn_[fi].halfedge_ = Halfedge_index(faces_freelist_); + // update the begin of the face free linked list + faces_freelist_ = nf + other.faces_freelist_; + } + // merge edge free list + if(other.edges_freelist_ != inf_value){ + Halfedge_index hi(nh+other.edges_freelist_); + Halfedge_index inf((std::numeric_limits::max)()); + while(hconn_[hi].next_halfedge_ != inf){ + hi = hconn_[hi].next_halfedge_; + } + // append the halfedge free linked list of `this` to the copy of `other` + hconn_[hi].next_halfedge_ = Halfedge_index(edges_freelist_); + // update the begin of the halfedge free linked list + edges_freelist_ = nh + other.edges_freelist_; + } + // update garbage infos + garbage_ = garbage_ || other.garbage_; + removed_vertices_ += other.removed_vertices_; + removed_edges_ += other.removed_edges_; + removed_faces_ += other.removed_faces_; return true; } @@ -1252,49 +1342,63 @@ class Surface_mesh /// are recycled. ///@{ +#ifndef DOXYGEN_RUNNING + /// returns the number of used and removed vertices in the mesh. + size_type num_vertices() const { return (size_type) vprops_.size(); } + + /// returns the number of used and removed halfedges in the mesh. + size_type num_halfedges() const { return (size_type) hprops_.size(); } + + /// returns the number of used and removed edges in the mesh. + size_type num_edges() const { return (size_type) eprops_.size(); } + + /// returns the number of used and removed faces in the mesh. + size_type num_faces() const { return (size_type) fprops_.size(); } + +#endif /// returns the number of vertices in the mesh which are marked removed. - size_type number_of_removed_vertices() const { return vprops_.capacity() - vprops_.size(); } + size_type number_of_removed_vertices() const { return removed_vertices_; } /// returns the number of halfedges in the mesh which are marked removed. - size_type number_of_removed_halfedges() const { return hprops_.capacity() - hprops_.size(); } + size_type number_of_removed_halfedges() const { return 2*removed_edges_; } /// returns the number of edges in the mesh which are marked removed. - size_type number_of_removed_edges() const { return eprops_.capacity() - eprops_.size(); } + size_type number_of_removed_edges() const { return removed_edges_; } /// returns the number offaces in the mesh which are marked removed. - size_type number_of_removed_faces() const { return fprops_.capacity() - fprops_.size(); } + size_type number_of_removed_faces() const { return removed_faces_; } + /// returns whether vertex `v` is marked removed. + /// \sa `collect_garbage()` bool is_removed(Vertex_index v) const { - return vprops_.is_erased(v); + return vremoved_[v]; } /// returns whether halfedge `h` is marked removed. + /// \sa `collect_garbage()` bool is_removed(Halfedge_index h) const { - return hprops_.is_erased(h); + return eremoved_[edge(h)]; } /// returns whether edge `e` is marked removed. + /// \sa `collect_garbage()` bool is_removed(Edge_index e) const { - return eprops_.is_erased(e); + return eremoved_[e]; } /// returns whether face `f` is marked removed. + /// \sa `collect_garbage()` bool is_removed(Face_index f) const { - return fprops_.is_erased(f); + return fremoved_[f]; } /// checks if any vertices, halfedges, edges, or faces are marked as removed. /// \sa collect_garbage - bool has_garbage() const { - return number_of_removed_vertices() != 0 || - number_of_removed_edges() != 0 || - number_of_removed_halfedges() != 0 || - number_of_removed_faces() != 0; - } + bool has_garbage() const { return garbage_; } /// really removes vertices, halfedges, edges, and faces which are marked removed. /// \sa `has_garbage()` @@ -1302,15 +1406,11 @@ class Surface_mesh /// In case you store indices in an auxiliary data structure /// or in a property these indices are potentially no longer /// referring to the right elements. - void collect_garbage() { - // todo: this should compress the array - } + void collect_garbage(); //undocumented convenience function that allows to get old-index->new-index information template - void collect_garbage(Visitor& visitor) { - // todo: this should compress the array and remap indices - } + void collect_garbage(Visitor& visitor); /// controls the recycling or not of simplices previously marked as removed /// upon addition of new elements. @@ -1326,6 +1426,13 @@ class Surface_mesh /// of all properties to the minimal required size. /// \attention Invalidates all existing references to properties. + void shrink_to_fit() + { + vprops_.shrink_to_fit(); + hprops_.shrink_to_fit(); + eprops_.shrink_to_fit(); + fprops_.shrink_to_fit(); + } /// @endcond ///@} @@ -1342,23 +1449,23 @@ class Surface_mesh /// returns whether the index of vertex `v` is valid, that is within the current array bounds. bool has_valid_index(Vertex_index v) const { - return ((size_type)v < number_of_vertices()); + return ((size_type)v < num_vertices()); } /// returns whether the index of halfedge `h` is valid, that is within the current array bounds. bool has_valid_index(Halfedge_index h) const { - return ((size_type)h < number_of_halfedges()); + return ((size_type)h < num_halfedges()); } /// returns whether the index of edge `e` is valid, that is within the current array bounds. bool has_valid_index(Edge_index e) const { - return ((size_type)e < number_of_edges()); + return ((size_type)e < num_edges()); } /// returns whether the index of face `f` is valid, that is within the current array bounds. bool has_valid_index(Face_index f) const { - return ((size_type)f < number_of_faces()); + return ((size_type)f < num_faces()); } /// @} @@ -1456,6 +1563,32 @@ class Surface_mesh std::cerr << "#faces: iterated: " << fcount << " vs number_of_faces(): " << number_of_faces()<< std::endl; } + size_type inf = (std::numeric_limits::max)(); + size_type vfl = vertices_freelist_; + size_type rv = 0; + while(vfl != inf){ + vfl = (size_type)vconn_[Vertex_index(vfl)].halfedge_; + rv++; + } + valid = valid && ( rv == removed_vertices_ ); + + + size_type efl = edges_freelist_; + size_type re = 0; + while(efl != inf){ + efl = (size_type)hconn_[Halfedge_index(efl)].next_halfedge_; + re++; + } + valid = valid && ( re == removed_edges_ ); + + size_type ffl = faces_freelist_; + size_type rf = 0; + while(ffl != inf){ + ffl = (size_type)fconn_[Face_index(ffl)].halfedge_; + rf++; + } + valid = valid && ( rf == removed_faces_ ); + return valid; } @@ -1919,8 +2052,6 @@ class Surface_mesh #endif - // todo: I can't see a good reason for these two functions to exist separately, but do almost the same thing - /// adds a property map named `name` with value type `T` and default `t` /// for index type `I`. Returns the property map together with a Boolean /// that is `true` if a new map was created. In case it already exists @@ -1934,7 +2065,6 @@ class Surface_mesh oss << "anonymous-property-" << anonymous_property_++; name = std::string(oss.str()); } - // todo: double check this is working auto [array, created] = const_cast*>(this)->get_property_container().template get_or_add_property(name, t); return {{array.get()}, created}; @@ -2053,19 +2183,19 @@ class Surface_mesh /// @} #if defined(CGAL_SURFACE_MESH_TEST_SUITE) - std::vector vertex_freelist() const + Vertex_index vertex_freelist() const { - return vprops_.inactive_list(); + return Vertex_index(vertices_freelist_); } - std::vector face_freelist() const + Face_index face_freelist() const { - return fprops_.inactive_list(); + return Face_index(faces_freelist_); } - std::vector edge_freelist() const + Edge_index edge_freelist() const { - return eprops_.inactive_list(); + return Edge_index(edges_freelist_>>1); } #endif @@ -2087,11 +2217,20 @@ class Surface_mesh Property_array& hconn_; Property_array& fconn_; + Property_array &vremoved_; + Property_array &eremoved_; + Property_array &fremoved_; + Property_array& vpoint_; - size_type vertices_freelist_; - size_type edges_freelist_; - size_type faces_freelist_; + size_type removed_vertices_ = 0; + size_type removed_edges_ = 0; + size_type removed_faces_ = 0; + + size_type vertices_freelist_ = std::numeric_limits::max(); + size_type edges_freelist_ = std::numeric_limits::max(); + size_type faces_freelist_ = std::numeric_limits::max(); + bool garbage_ = false; bool recycle_ = true; size_type anonymous_property_; @@ -2315,6 +2454,169 @@ degree(Face_index f) const return count; } +template template< typename Visitor> +void +Surface_mesh

:: +collect_garbage(Visitor &visitor) +{ + if (!has_garbage()) + { + return; + } + + std::uint32_t i, i0, i1, + nV(num_vertices()), + nE(num_edges()), + nH(num_halfedges()), + nF(num_faces()); + + Vertex_index v; + Halfedge_index h; + Face_index f; + + + // setup index mapping% + Property_map vmap = add_property_map("v:garbage-collection").first; + Property_map hmap = add_property_map("h:garbage-collection").first; + Property_map fmap = add_property_map("f:garbage-collection").first; + for (i=0; i 0) + { + i0=0; i1=nV-1; + + while (1) + { + // find first removed and last un-removed + while (!vremoved_[Vertex_index(i0)] && i0 < i1) ++i0; + while ( vremoved_[Vertex_index(i1)] && i0 < i1) --i1; + if (i0 >= i1) break; + + // swap + vprops_.swap(i0, i1); + }; + + // remember new size + nV = vremoved_[Vertex_index(i0)] ? i0 : i0+1; + } + + // really remove edges + if (nE > 0) + { + i0=0; i1=nE-1; + + while (1) + { + // find first removed and last un-removed + while (!eremoved_[Edge_index(i0)] && i0 < i1) ++i0; + while ( eremoved_[Edge_index(i1)] && i0 < i1) --i1; + if (i0 >= i1) break; + + // swap + eprops_.swap(SM_Edge_index{i0}, SM_Edge_index{i1}); + hprops_.swap(SM_Halfedge_index{2*i0}, SM_Halfedge_index{2*i1}); + hprops_.swap(SM_Halfedge_index{2*i0+1}, SM_Halfedge_index{2*i1+1}); + }; + + // remember new size + nE = eremoved_[Edge_index(i0)] ? i0 : i0+1; + nH = 2*nE; + } + + + // really remove faces + if (nF > 0) + { + i0=0; i1=nF-1; + + while (1) + { + // find 1st removed and last un-removed + while (!fremoved_[Face_index(i0)] && i0 < i1) ++i0; + while ( fremoved_[Face_index(i1)] && i0 < i1) --i1; + if (i0 >= i1) break; + + // swap + fprops_.swap(SM_Face_index{i0}, SM_Face_index{i1}); + }; + + // remember new size + nF = fremoved_[Face_index(i0)] ? i0 : i0+1; + } + + + // update vertex connectivity + for (i=0; i(vmap); + remove_property_map(hmap); + remove_property_map(fmap); + + // finally resize arrays + vprops_.resize(nV); vprops_.shrink_to_fit(); + hprops_.resize(nH); hprops_.shrink_to_fit(); + eprops_.resize(nE); eprops_.shrink_to_fit(); + fprops_.resize(nF); fprops_.shrink_to_fit(); + + removed_vertices_ = removed_edges_ = removed_faces_ = 0; + vertices_freelist_ = edges_freelist_ = faces_freelist_ = -1; + garbage_ = false; +} + +#ifndef DOXYGEN_RUNNING +namespace collect_garbage_internal { +struct Dummy_visitor{ + template + void operator()(const A&, const B&, const C&) + {} +}; + +} +#endif + +template +void +Surface_mesh

:: +collect_garbage() +{ + collect_garbage_internal::Dummy_visitor visitor; + collect_garbage(visitor); +} namespace internal{ namespace handle { diff --git a/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp b/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp index 5e932869a440..bf446b309597 100644 --- a/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp @@ -64,20 +64,20 @@ struct test_emptiness : public Surface_fixture assert(m.is_isolated(iv)); Sm::Vertex_around_target_range vr = m.vertices_around_target(m.halfedge(iv)); - assert(is_empty_range(std::begin(vr), std::end(vr))); + assert(is_empty_range(boost::begin(vr), boost::end(vr))); Sm::Face_around_target_range fr = m.faces_around_target(m.halfedge(iv)); - assert(is_empty_range(std::begin(fr), std::end(fr))); + assert(is_empty_range(boost::begin(fr), boost::end(fr))); Sm::Halfedge_around_target_range hr = m.halfedges_around_target(m.halfedge(iv)); - assert(is_empty_range(std::begin(hr), std::end(hr))); + assert(is_empty_range(boost::begin(hr), boost::end(hr))); // not true for everything else m.remove_vertex(iv); assert(m.is_removed(iv)); Sm::Vertex_iterator vb, ve; for(boost::tie(vb, ve) = m.vertices(); vb != ve; ++vb) { Sm::Vertex_around_target_range vr = m.vertices_around_target(m.halfedge(*vb)); - assert(!is_empty_range(std::begin(vr), std::end(vr))); + assert(!is_empty_range(boost::begin(vr), boost::end(vr))); } } }; diff --git a/Surface_mesh/test/Surface_mesh/sm_join_test.cpp b/Surface_mesh/test/Surface_mesh/sm_join_test.cpp index 65b83c9fef6a..4a71243b8e4e 100644 --- a/Surface_mesh/test/Surface_mesh/sm_join_test.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_join_test.cpp @@ -19,24 +19,35 @@ typedef boost::graph_traits::face_descriptor face_descriptor; void freelist(const Sm& sm, int vc, int fc, int ec) { - // vc should be the number of in-active vertex indices std::cout << "vertex freelist" << std::endl; - auto unused_vertices = sm.vertex_freelist(); - for (auto vd: unused_vertices) + vertex_descriptor vd = sm.vertex_freelist(); + while(vd != sm.null_vertex()){ + --vc; std::cout << vd << std::endl; - assert(vc == unused_vertices.size()); + halfedge_descriptor hd = halfedge(vd,sm); + vd = vertex_descriptor((Sm::size_type)hd); + } + assert(vc == 0); std::cout << "face freelist" << std::endl; - auto unused_faces = sm.face_freelist(); - for (auto fd: unused_faces) + face_descriptor fd = sm.face_freelist(); + while(fd != sm.null_face()){ + --fc; std::cout << fd << std::endl; - assert(fc == unused_faces.size()); + halfedge_descriptor hd = halfedge(fd,sm); + fd = face_descriptor((Sm::size_type)hd); + } + assert(fc == 0); std::cout << "edge freelist" << std::endl; - auto unused_edges = sm.edge_freelist(); - for (auto ed: unused_edges) + edge_descriptor ed = sm.edge_freelist(); + while(ed != sm.null_edge()){ + --ec; std::cout << ed << std::endl; - assert(ec == unused_edges.size()); + halfedge_descriptor hd = next(halfedge(ed,sm),sm); + ed = edge(hd,sm); + } + assert(ec == 0); } diff --git a/Surface_mesh/test/Surface_mesh/sm_remove.cpp b/Surface_mesh/test/Surface_mesh/sm_remove.cpp index 6744fbf77b2f..5aadc4a132e5 100644 --- a/Surface_mesh/test/Surface_mesh/sm_remove.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_remove.cpp @@ -19,13 +19,13 @@ int main() Sm m; Sm::vertex_index u; - assert(m.number_of_vertices() == 0); + assert(m.num_vertices() == 0); assert(m.number_of_removed_vertices() == 0); for(int i=0; i < 10; i++){ u = m.add_vertex(Point_3(0,0,0)); m.remove_vertex(u); } - assert(m.number_of_vertices() == 0); + assert(m.num_vertices() == 1); assert(m.number_of_removed_vertices() == 1); @@ -34,23 +34,26 @@ int main() assert(! m.does_recycle_garbage()); m.add_vertex(Point_3(0,0,0)); - assert(m.number_of_vertices() == 1); + assert(m.num_vertices() == 2); assert(m.number_of_removed_vertices() == 1); m.set_recycle_garbage(true); m.add_vertex(Point_3(0,0,0)); - assert(m.number_of_vertices() == 2); + assert(m.num_vertices() == 2); assert(m.number_of_removed_vertices() == 0); - std::cout << m.number_of_vertices() << " " << m.number_of_removed_vertices() << std::endl; + std::cout << m.num_vertices() << " " << m.number_of_removed_vertices() << std::endl; // make sure all is OK when clearing the mesh - auto vconn = m.add_property_map("v:connectivity").first; - auto hconn = m.add_property_map("h:connectivity").first; - auto fconn = m.add_property_map("f:connectivity").first; - auto vpoint = m.add_property_map("v:point").first; + auto vconn = m.property_map("v:connectivity").first; + auto hconn = m.property_map("h:connectivity").first; + auto fconn = m.property_map("f:connectivity").first; + auto vpoint = m.property_map("v:point").first; + auto vremoved = m.property_map("v:removed").first; + auto eremoved = m.property_map("e:removed").first; + auto fremoved = m.property_map("f:removed").first; // first call to squat the first available position m.add_property_map("vprop_dummy"); @@ -71,10 +74,13 @@ int main() auto l_fprop = m.add_property_map("fprop").first; auto l_eprop = m.add_property_map("eprop").first; - auto l_vconn = m.add_property_map("v:connectivity").first; - auto l_hconn = m.add_property_map("h:connectivity").first; - auto l_fconn = m.add_property_map("f:connectivity").first; - auto l_vpoint = m.add_property_map("v:point").first; + auto l_vconn = m.property_map("v:connectivity").first; + auto l_hconn = m.property_map("h:connectivity").first; + auto l_fconn = m.property_map("f:connectivity").first; + auto l_vpoint = m.property_map("v:point").first; + auto l_vremoved = m.property_map("v:removed").first; + auto l_eremoved = m.property_map("e:removed").first; + auto l_fremoved = m.property_map("f:removed").first; assert( vconn == l_vconn ); assert( hconn == l_hconn ); @@ -89,10 +95,13 @@ int main() { m.clear(); - auto l_vconn = m.add_property_map("v:connectivity").first; - auto l_hconn = m.add_property_map("h:connectivity").first; - auto l_fconn = m.add_property_map("f:connectivity").first; - auto l_vpoint = m.add_property_map("v:point").first; + auto l_vconn = m.property_map("v:connectivity").first; + auto l_hconn = m.property_map("h:connectivity").first; + auto l_fconn = m.property_map("f:connectivity").first; + auto l_vpoint = m.property_map("v:point").first; + auto l_vremoved = m.property_map("v:removed").first; + auto l_eremoved = m.property_map("e:removed").first; + auto l_fremoved = m.property_map("f:removed").first; assert( vconn == l_vconn ); assert( hconn == l_hconn ); From be6fa67a6b1f851e7401f2603418506b3ce0ef88 Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Tue, 10 Oct 2023 18:39:15 -0600 Subject: [PATCH 272/520] 120x120 logo From cc04b10659da0a37ea8f5e898894c3072af930da Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 09:02:43 +0100 Subject: [PATCH 273/520] remove non-author of one file --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index e703ce7e842b..6c67b602a43c 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -8,7 +8,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Ken Arroyo Ohori -// Guillaume Damiand #ifndef CGAL_POLYGON_REPAIR_H #define CGAL_POLYGON_REPAIR_H From 9f5a217200adf5c54551c3a00b8de474381e62cf Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 09:25:07 +0100 Subject: [PATCH 274/520] fix ingroup --- Polygon/include/CGAL/Multipolygon_with_holes_2.h | 2 +- Polygon/include/CGAL/Polygon_with_holes_2.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index 55162b50df16..7421f20c0048 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -21,7 +21,7 @@ namespace CGAL { -/*! \ingroup PkgPolygonRepairRef +/*! \ingroup PkgPolygon2Ref * * The class `Multipolygon_with_holes_2` models the concept `MultipolygonWithHoles_2`. * It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate diff --git a/Polygon/include/CGAL/Polygon_with_holes_2.h b/Polygon/include/CGAL/Polygon_with_holes_2.h index e41104c943a9..23eb826e9b2a 100644 --- a/Polygon/include/CGAL/Polygon_with_holes_2.h +++ b/Polygon/include/CGAL/Polygon_with_holes_2.h @@ -30,8 +30,8 @@ namespace CGAL { The class `Polygon_with_holes_2` models the concept `GeneralPolygonWithHoles_2`. It represents a linear polygon with holes. It is parameterized with two types (`Kernel` and `Container`) that are used to instantiate -the type `Polygon_2`. The latter is used to -represents the outer boundary and the boundary of the holes (if any exist). +the type `Polygon_2`. This poygon type is used to +represent the outer boundary and the boundary of the holes (if any exist). \cgalModels{GeneralPolygonWithHoles_2} From 4b9544dcc481f0db99e04612adbafab299e54848 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 10:25:02 +0100 Subject: [PATCH 275/520] Fix doc of the parameters of the multipolygon --- Polygon/doc/Polygon/PackageDescription.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon/doc/Polygon/PackageDescription.txt b/Polygon/doc/Polygon/PackageDescription.txt index 4e36dd32906c..8083eb8e15b5 100644 --- a/Polygon/doc/Polygon/PackageDescription.txt +++ b/Polygon/doc/Polygon/PackageDescription.txt @@ -54,7 +54,7 @@ - `CGAL::Polygon_2` - `CGAL::Polygon_with_holes_2` - `CGAL::General_polygon_with_holes_2` -- `CGAL::Multipolygon_with_holes_2` +- `CGAL::Multipolygon_with_holes_2` \cgalCRPSection{Global Functions} - `CGAL::area_2()` From 1fb664a471548af2ef96b37171bb96d7f4c526c6 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 11:36:26 +0100 Subject: [PATCH 276/520] Document Size --- Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h | 3 +++ Polygon/include/CGAL/Multipolygon_with_holes_2.h | 1 + 2 files changed, 4 insertions(+) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index c39c76680da3..6e5f4e4c9559 100644 --- a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -29,6 +29,9 @@ typedef unspecified_type Polygon_const_iterator; //! range type for iterating over holes. typedef unspecified_type Polygons_container; +//! size type +typedef unsigned int Size; + /// @} /// \name Creation diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index 7421f20c0048..1d154419bd7d 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -51,6 +51,7 @@ class Multipolygon_with_holes_2 { using Polygon_iterator = typename Polygons_container::iterator; using Polygon_const_iterator = typename Polygons_container::const_iterator; + /// the size type using Size = unsigned int; /*! %Default constructor. */ From f06f1024a67c92aaa65b4406570d5618801d7155 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 11:48:43 +0100 Subject: [PATCH 277/520] holes -> polygons --- Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h | 2 +- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index 6e5f4e4c9559..6d4d2d948cf0 100644 --- a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -26,7 +26,7 @@ typedef unspecified_type Polygon_with_holes_2; */ typedef unspecified_type Polygon_const_iterator; -//! range type for iterating over holes. +//! range type for iterating over the polygons. typedef unspecified_type Polygons_container; //! size type diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 4a875571280e..e2ded1de8745 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -20,7 +20,7 @@ Different arrangement and labelling heuristics are possible, but currently only the odd-even heuristic is implemented in the package. This heuristic results in areas that are alternately assigned as polygon interiors and exterior/holes each time that an input edge is passed. -It doesn't distinguish between edges that are part of outer boundaries +It does not distinguish between edges that are part of outer boundaries from those of inner boundaries. \section SectionPolygonRepair_Definitions Definitions From 950ed67a9b46096fe5c76ef57b9a6767ff6eb9a6 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 11:50:37 +0100 Subject: [PATCH 278/520] remove sentence about ability to store --- Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index 6d4d2d948cf0..687683d76ffa 100644 --- a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -4,8 +4,6 @@ * \cgalRefines{CopyConstructible,Assignable,DefaultConstructible} * * A model of this concept represents a multipolygon with holes. - * The concept requires the ability to store the polygons with holes - * that the multipolygon is composed of. * * \cgalHasModelsBegin * \cgalHasModels{CGAL::Multipolygon_with_holes_2} From d760c408a7567ce5656ff5209f7fea3c0a62a7d9 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 14:08:33 +0100 Subject: [PATCH 279/520] Be precise --- .../Concepts/MultiPolygonWithHoles_2.h | 34 ++++++++--------- .../CGAL/General_polygon_with_holes_2.h | 4 +- .../include/CGAL/Multipolygon_with_holes_2.h | 38 +++++++++---------- .../CGAL/draw_multipolygon_with_holes_2.h | 8 ++-- .../Polygon_repair/repair_polygon_2.cpp | 4 +- .../CGAL/Polygon_repair/Polygon_repair.h | 4 +- .../test/Polygon_repair/clipart.cpp | 8 ++-- .../Polygon_repair/repair_polygon_2_test.cpp | 2 +- .../write_repaired_polygons.cpp | 26 ++++++------- Stream_support/include/CGAL/IO/WKT.h | 4 +- 10 files changed, 66 insertions(+), 66 deletions(-) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h index 687683d76ffa..917e6f91052e 100644 --- a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h @@ -19,13 +19,13 @@ class MultipolygonWithHoles_2 { //! the polygon type used to represent each polygon with holes of the multipolygon. typedef unspecified_type Polygon_with_holes_2; -/*! a bidirectional iterator over the polygons. +/*! a bidirectional iterator over the polygons with holes. * Its value type is `Polygon_with_holes_2`. */ -typedef unspecified_type Polygon_const_iterator; +typedef unspecified_type Polygon_with_holes_const_iterator; -//! range type for iterating over the polygons. -typedef unspecified_type Polygons_container; +//! range type for iterating over polygons with holes. +typedef unspecified_type Polygon_with_holes_container; //! size type typedef unsigned int Size; @@ -35,7 +35,7 @@ typedef unsigned int Size; /// \name Creation /// @{ -/*! constructs a multipolygon using a range of polygons. +/*! constructs a multipolygon using a range of polygons with holes. */ template MultipolygonWithHoles_2(InputIterator begin, InputIterator end); @@ -45,41 +45,41 @@ MultipolygonWithHoles_2(InputIterator begin, InputIterator end); /// \name Predicates /// @{ -/*! returns the number of polygons. +/*! returns the number of polygons with holes. */ -Size number_of_polygons(); +Size number_of_polygons_wih_holes(); /// @} /// \name Access Functions /// @{ -/*! returns the begin iterator of the polygons. +/*! returns the begin iterator of the polygons with holes. */ -Polygon_const_iterator polygons_begin() const; +Polygon_with_holes_const_iterator polygons_with_holes_begin() const; -/*! returns the past-the-end iterator of the polygons. +/*! returns the past-the-end iterator of the polygons with holes. */ -Polygon_const_iterator polygons_end() const; +Polygon_with_holes_const_iterator polygons_with_holes_end() const; -/*! returns the range of polygons. +/*! returns the range of polygons with holes. */ -const Polygons_container& polygons() const; +const Polygon_with_holes_container& polygons_with_holes() const; /// @} /// \name Modifiers /// @{ -/*! adds a given polygon to the multipolygon. +/*! adds a given polygon with holes to the multipolygon. */ -void add_polygon(const Polygon_with_holes_2& polygon); +void add_polygon_with_holes(const Polygon_with_holes_2& polygon); /*! erases the specified polygon. */ -void erase_polygon(Polygon_iterator pit); +void erase_polygon_with_holes(Polygon_iterator pit); -/*! removes all the polygons. +/*! removes all the polygons with holes. */ void clear(); diff --git a/Polygon/include/CGAL/General_polygon_with_holes_2.h b/Polygon/include/CGAL/General_polygon_with_holes_2.h index 42173fc2ad6f..bb19fcf99436 100644 --- a/Polygon/include/CGAL/General_polygon_with_holes_2.h +++ b/Polygon/include/CGAL/General_polygon_with_holes_2.h @@ -28,8 +28,8 @@ namespace CGAL { * * The class `General_polygon_with_holes_2` models the concept * `GeneralPolygonWithHoles_2`. It represents a general polygon with holes. - * It is parameterized with a type `Polygon` used to define the exposed - * type `Polygon_2`. This type represents the outer boundary of the general + * It is parameterized with a type `Polygon_` used to define the exposed + * type `%Polygon_2`. This type represents the outer boundary of the general * polygon and each hole. * * \tparam Polygon_ must have input and output operators. diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index 1d154419bd7d..3fb93f903be7 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -46,10 +46,10 @@ class Multipolygon_with_holes_2 { /// @} - using Polygons_container = std::deque; + using Polygon_with_holes_container = std::deque; - using Polygon_iterator = typename Polygons_container::iterator; - using Polygon_const_iterator = typename Polygons_container::const_iterator; + using Polygon_with_holes_iterator = typename Polygon_with_holes_container::iterator; + using Polygon_with_holes_const_iterator = typename Polygon_with_holes_container::const_iterator; /// the size type using Size = unsigned int; @@ -64,32 +64,32 @@ class Multipolygon_with_holes_2 { m_polygons(p_begin, p_end) {} - Polygons_container& polygons() { return m_polygons; } + Polygon_with_holes_container& polygons_with_holes() { return m_polygons; } - const Polygons_container& polygons() const { return m_polygons; } + const Polygon_with_holes_container& polygons_with_holes() const { return m_polygons; } - Polygon_iterator polygons_begin() { return m_polygons.begin(); } + Polygon_with_holes_iterator polygons_with_holes_begin() { return m_polygons.begin(); } - Polygon_iterator polygons_end() { return m_polygons.end(); } + Polygon_with_holes_iterator polygons_with_holes_end() { return m_polygons.end(); } - Polygon_const_iterator polygons_begin() const { return m_polygons.begin(); } + Polygon_with_holes_const_iterator polygons_with_holes_begin() const { return m_polygons.begin(); } - Polygon_const_iterator polygons_end() const { return m_polygons.end(); } + Polygon_with_holes_const_iterator polygons_with_holes_end() const { return m_polygons.end(); } void add_polygon(const Polygon_2& pgn) { m_polygons.push_back(Polygon_with_holes_2(pgn)); } - void add_polygon(const Polygon_with_holes_2& pgn) { m_polygons.push_back(pgn); } + void add_polygon_with_holes(const Polygon_with_holes_2& pgn) { m_polygons.push_back(pgn); } - void add_polygon(Polygon_with_holes_2&& pgn) { m_polygons.emplace_back(std::move(pgn)); } + void add_polygon_with_holes(Polygon_with_holes_2&& pgn) { m_polygons.emplace_back(std::move(pgn)); } - void erase_polygon(Polygon_iterator pit) { m_polygons.erase(pit); } + void erase_polygon_with_holes(Polygon_with_holes_iterator pit) { m_polygons.erase(pit); } void clear() { m_polygons.clear(); } - Size number_of_polygons() const { return static_cast(m_polygons.size()); } + Size number_of_polygons_with_holes() const { return static_cast(m_polygons.size()); } protected: - Polygons_container m_polygons; + Polygon_with_holes_container m_polygons; }; /*! @@ -113,26 +113,26 @@ order. template std::ostream& operator<<(std::ostream& os, const Multipolygon_with_holes_2& mp) { - typename Multipolygon_with_holes_2::Polygon_const_iterator i; + typename Multipolygon_with_holes_2::Polygon_with_holes_const_iterator i; switch(IO::get_mode(os)) { case IO::ASCII : os << mp.number_of_polygons() << ' '; - for (i = mp.polygons_begin(); i != mp.polygons_end(); ++i) { + for (i = mp.polygon_with_holes_begin(); i != mp.polygon_with_holes_end(); ++i) { os << *i << ' '; } return os; case IO::BINARY : - os << mp.number_of_polygons(); - for (i = mp.polygons_begin(); i != mp.polygons_end(); ++i) { + os << mp.number_of_polygons_with_holes(); + for (i = mp.polygons_with_holes_begin(); i != mp.polygons_with_holes_end(); ++i) { os << *i ; } return os; default: os << "Multipolygon_with_holes_2(" << std::endl; - for (i = mp.polygons_begin(); i != mp.polygons_end(); ++i) { + for (i = mp.polygons_with_holes_begin(); i != mp.polygons_with_holes_end(); ++i) { os << " " << *i << std::endl; } diff --git a/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h index 814723411c7a..d9e311b6af25 100644 --- a/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h @@ -69,7 +69,7 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { const char* title = "Basic Multipolygon_with_holes_2 Viewer") : Base(parent, title, true, true, true, false, false), m_mpwh(mpwh) { - if (mpwh.number_of_polygons() == 0) return; + if (mpwh.number_of_polygons_with_holes() == 0) return; // mimic the computation of Camera::pixelGLRatio() auto bbox = bounding_box(); @@ -99,8 +99,8 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { */ CGAL::Bbox_2 bounding_box() { Bbox_2 bbox; - if (m_mpwh.number_of_polygons() > 0) bbox = m_mpwh.polygons().front().outer_boundary().bbox(); - for (auto const& pwh: m_mpwh.polygons()) { + if (m_mpwh.number_of_polygons_with_holes() > 0) bbox = m_mpwh.polygons_with_holes().front().outer_boundary().bbox(); + for (auto const& pwh: m_mpwh.polygons_with_holes()) { bbox += pwh.outer_boundary().bbox(); } return bbox; @@ -111,7 +111,7 @@ class Mpwh_2_basic_viewer_qt : public Basic_viewer_qt { void add_elements() { clear(); - for (auto const& p: m_mpwh.polygons()) { + for (auto const& p: m_mpwh.polygons_with_holes()) { CGAL::IO::Color c(rand()%255,rand()%255,rand()%255); face_begin(c); diff --git a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp index c8a91fb0c1b7..ed48aabf5933 100644 --- a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -17,10 +17,10 @@ int main(int argc, char* argv[]) { CGAL::IO::read_polygon_WKT(in, pin); Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair_odd_even(pin); - if (mp.number_of_polygons() > 1) { + if (mp.number_of_polygons_with_holes() > 1) { CGAL::IO::write_multi_polygon_WKT(std::cout, mp); } else { - CGAL::IO::write_polygon_WKT(std::cout, mp.polygons()[0]); + CGAL::IO::write_polygon_WKT(std::cout, mp.polygons_with_holes()[0]); } return 0; diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 6c67b602a43c..81e1090af03b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -427,7 +427,7 @@ class Polygon_repair { void add_to_triangulation_odd_even(const Multipolygon_with_holes_2& multipolygon) { // Get unique edges - for (auto const& polygon: multipolygon.polygons()) { + for (auto const& polygon: multipolygon.polygons_with_holes()) { for (auto const& edge: polygon.outer_boundary().edges()) { if (edge.source() == edge.target()) continue; std::pair pair = (edge.source() < edge.target())? @@ -637,7 +637,7 @@ class Polygon_repair { } for (auto const& polygon: ordered_polygons) { // std::cout << "Adding polygon " << polygon << std::endl; - mp.add_polygon(polygon); + mp.add_polygon_with_holes(polygon); } } diff --git a/Polygon_repair/test/Polygon_repair/clipart.cpp b/Polygon_repair/test/Polygon_repair/clipart.cpp index cc184ee781ef..b5337840fd88 100644 --- a/Polygon_repair/test/Polygon_repair/clipart.cpp +++ b/Polygon_repair/test/Polygon_repair/clipart.cpp @@ -82,9 +82,9 @@ int main(int argc, char* argv[]) { pr.reconstruct_multipolygon(); } Multipolygon_with_holes_2 mp = pr.multipolygon(); - if (mp.number_of_polygons() > 0) { - CGAL::Bbox_2 bbox = mp.polygons().front().bbox(); - for (auto const& polygon: mp.polygons()) { + if (mp.number_of_polygons_with_holes() > 0) { + CGAL::Bbox_2 bbox = mp.polygons_with_holes().front().bbox(); + for (auto const& polygon: mp.polygons_with_holes()) { bbox += polygon.outer_boundary().bbox(); } Kernel::Vector_2 translate(-bbox.xmin(), -bbox.ymin()); double scale = desired_width/(bbox.xmax()-bbox.xmin()); @@ -93,7 +93,7 @@ int main(int argc, char* argv[]) { std::ofstream ofs(folder_out + "/" + std::string(file.path().stem()) + ".svg"); ofs << "" << std::endl; - for (auto const& polygon: mp.polygons()) { + for (auto const& polygon: mp.polygons_with_holes()) { // std::cout << polygon << std::endl; ofs << "\t 0) ofs << ","; + if (rmp.polygons_with_holes()[i].number_of_holes() > 0) ofs << ","; ofs << std::endl; - for (int j = 0; j < rmp.polygons()[i].holes().size(); ++j) { + for (int j = 0; j < rmp.polygons_with_holes()[i].holes().size(); ++j) { ofs << "\t\t\t[" << std::endl; - for (int k = 0; k < rmp.polygons()[i].holes()[j].size(); ++k) { - ofs << "\t\t\t\t[" << rmp.polygons()[i].holes()[j][k].x() << - ", " << rmp.polygons()[i].holes()[j][k].y() << "]"; - if (k < rmp.polygons()[i].holes()[j].size()-1) ofs << ","; + for (int k = 0; k < rmp.polygons_with_holes()[i].holes()[j].size(); ++k) { + ofs << "\t\t\t\t[" << rmp.polygons_with_holes()[i].holes()[j][k].x() << + ", " << rmp.polygons_with_holes()[i].holes()[j][k].y() << "]"; + if (k < rmp.polygons_with_holes()[i].holes()[j].size()-1) ofs << ","; ofs << std::endl; } ofs << "\t\t\t]"; - if (j < rmp.polygons()[i].holes().size()-1) ofs << ","; + if (j < rmp.polygons_with_holes()[i].holes().size()-1) ofs << ","; ofs << std::endl; } ofs << "\t\t]"; - if (i < rmp.polygons().size()-1) ofs << ","; + if (i < rmp.polygons_with_holes().size()-1) ofs << ","; ofs << std::endl; } ofs << "\t]" << std::endl; ofs << "}" << std::endl; diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index ef8c7dee1f0e..f558c90aea28 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -351,7 +351,7 @@ template bool read_multi_polygon_WKT(std::istream& in, Multipolygon_with_holes_2& mp) { - return read_multi_polygon_WKT(in, mp.polygons()); + return read_multi_polygon_WKT(in, mp.polygons_with_holes()); } @@ -359,7 +359,7 @@ template std::ostream& write_multi_polygon_WKT(std::ostream& out, Multipolygon_with_holes_2& mp) { - return write_multi_polygon_WKT(out, mp.polygons()); + return write_multi_polygon_WKT(out, mp.polygons_with_holes()); } //! \ingroup PkgStreamSupportIoFuncsWKT From 6d51db7d975fb9595ab931996e2f407fb5ff4252 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 14:10:12 +0100 Subject: [PATCH 280/520] Change filename --- .../{MultiPolygonWithHoles_2.h => MultipolygonWithHoles_2.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Polygon/doc/Polygon/Concepts/{MultiPolygonWithHoles_2.h => MultipolygonWithHoles_2.h} (100%) diff --git a/Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h similarity index 100% rename from Polygon/doc/Polygon/Concepts/MultiPolygonWithHoles_2.h rename to Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h From f1c14d5ad7f91d7b2282e31ad171055c642bd46d Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 17 Oct 2023 14:16:46 +0100 Subject: [PATCH 281/520] fix example --- Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp | 1 + Polygon/examples/Polygon/multipolygon.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp index 5db6ececb9ee..db7927c8ba5d 100644 --- a/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp +++ b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/Polygon/examples/Polygon/multipolygon.cpp b/Polygon/examples/Polygon/multipolygon.cpp index b98a6fda3cfd..9699f916e99f 100644 --- a/Polygon/examples/Polygon/multipolygon.cpp +++ b/Polygon/examples/Polygon/multipolygon.cpp @@ -20,12 +20,12 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p2(Polygon_2(p2_outer, p2_outer+4)); Multipolygon_with_holes_2 mp; - mp.add_polygon(p1); - mp.add_polygon(p2); + mp.add_polygon_with_holes(p1); + mp.add_polygon_with_holes(p2); for (auto const& p: mp.polygons()) { std::cout << p << std::endl; } return 0; -} \ No newline at end of file +} From 7ed78a0e15bac06222d0aff760375967038fd70d Mon Sep 17 00:00:00 2001 From: Ken Arroyo Ohori Date: Wed, 25 Oct 2023 10:07:49 -0700 Subject: [PATCH 282/520] mark holes with red edges From 6383bf9676e8fdca8433a839a68a0a8b900af35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 7 Nov 2023 08:30:37 +0100 Subject: [PATCH 283/520] revert changes to not block the integration of new Orthtree --- .../graph/IO/Generic_facegraph_builder.h | 8 +- BGL/include/CGAL/boost/graph/property_maps.h | 44 +- .../CGAL/Classification/Planimetric_grid.h | 6 +- .../Classification/Point_set_neighborhood.h | 6 +- .../CGAL/Classification/property_maps.h | 2 + Point_set_3/include/CGAL/Point_set_3.h | 262 ++++--- Point_set_3/include/CGAL/Point_set_3/IO/PLY.h | 82 ++- Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h | 2 +- Point_set_3/test/Point_set_3/CMakeLists.txt | 3 - .../Point_set_3/copy_construction_test.cpp | 36 - .../test/Point_set_3/file_load_test.cpp | 33 - .../test/Point_set_3/point_insertion_test.cpp | 39 - .../test/Point_set_3/point_set_test.cpp | 21 +- .../test/Point_set_3/point_set_test_join.cpp | 13 +- .../internal/hypothesis.h | 2 +- .../internal/point_set_with_planes.h | 2 +- .../Classification/Cluster_classification.cpp | 120 +-- .../Classification/Cluster_classification.h | 30 +- .../Point_set_item_classification.cpp | 102 +-- .../Point_set_item_classification.h | 71 +- .../Surface_mesh_item_classification.cpp | 26 +- .../Surface_mesh_item_classification.h | 5 +- .../Display/Display_property_plugin.cpp | 72 +- .../Plugins/Display/Heat_method_plugin.cpp | 8 +- .../Plugins/PMP/Engrave_text_plugin.cpp | 30 +- .../Point_set/Point_set_clustering_plugin.cpp | 16 +- .../Point_set_normal_estimation_plugin.cpp | 26 +- .../Point_set_shape_detection_plugin.cpp | 32 +- .../Point_set_to_mesh_distance_plugin.cpp | 43 +- ...ce_reconstruction_advancing_front_impl.cpp | 6 +- .../Surface_reconstruction_polygonal_impl.cpp | 6 +- .../Surface_mesh/Parameterization_plugin.cpp | 12 +- .../Scene_edit_polyhedron_item.cpp | 15 +- .../Scene_points_with_normal_item.cpp | 48 +- .../Polyhedron/Scene_surface_mesh_item.cpp | 68 +- .../Scene_textured_surface_mesh_item.cpp | 43 +- .../demo/Polyhedron/include/Point_set_3.h | 186 ++--- .../include/CGAL/Search_traits_adapter.h | 26 +- .../include/CGAL/Surface_mesh/IO/OFF.h | 118 ++- .../include/CGAL/Surface_mesh/IO/PLY.h | 90 ++- .../include/CGAL/Surface_mesh/Properties.h | 683 ++++++++++++++++++ .../include/CGAL/Surface_mesh/Surface_mesh.h | 471 +++++++----- .../boost/graph/graph_traits_Surface_mesh.h | 2 +- .../boost/graph/properties_Surface_mesh.h | 287 ++++---- .../test/Surface_mesh/sm_circulator_test.cpp | 8 +- .../test/Surface_mesh/sm_open_colored_off.cpp | 32 +- Surface_mesh/test/Surface_mesh/sm_ply_io.cpp | 2 - Surface_mesh/test/Surface_mesh/sm_remove.cpp | 30 +- .../test/Surface_mesh/surface_mesh_test.cpp | 10 +- .../include/CGAL/Weights/cotangent_weights.h | 14 +- 50 files changed, 2083 insertions(+), 1216 deletions(-) delete mode 100644 Point_set_3/test/Point_set_3/copy_construction_test.cpp delete mode 100644 Point_set_3/test/Point_set_3/file_load_test.cpp delete mode 100644 Point_set_3/test/Point_set_3/point_insertion_test.cpp create mode 100644 Surface_mesh/include/CGAL/Surface_mesh/Properties.h diff --git a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h index 720b1c769146..1dd6235a28cd 100644 --- a/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h +++ b/BGL/include/CGAL/boost/graph/IO/Generic_facegraph_builder.h @@ -102,10 +102,10 @@ class Generic_facegraph_builder // Construct the graph VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_property_map(CGAL::vertex_point, g)); - VNM vnm = choose_parameter(get_parameter(np, internal_np::vertex_normal_map)); - VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map)); - VTM vtm = choose_parameter(get_parameter(np, internal_np::vertex_texture_map)); - FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map)); + VNM vnm = choose_parameter(get_parameter(np, internal_np::vertex_normal_map), VNM()); + VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map), VCM()); + VTM vtm = choose_parameter(get_parameter(np, internal_np::vertex_texture_map), VTM()); + FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map), FCM()); const bool has_vertex_normals = (is_vnm_requested && !(vertex_normals.empty())); const bool has_vertex_colors = (is_vcm_requested && !(vertex_colors.empty())); diff --git a/BGL/include/CGAL/boost/graph/property_maps.h b/BGL/include/CGAL/boost/graph/property_maps.h index 9cbe9f2d7bfa..aab29d9b8811 100644 --- a/BGL/include/CGAL/boost/graph/property_maps.h +++ b/BGL/include/CGAL/boost/graph/property_maps.h @@ -24,10 +24,10 @@ template < class TriangleMesh, class VertexPointMap = typename boost::property_map::type > struct Triangle_from_face_descriptor_map{ typename std::remove_const_t* m_tm; - std::optional m_vpm; + VertexPointMap m_vpm; Triangle_from_face_descriptor_map() - : m_tm(nullptr), m_vpm() + : m_tm(nullptr) {} Triangle_from_face_descriptor_map(TriangleMesh const* tm) @@ -58,9 +58,9 @@ struct Triangle_from_face_descriptor_map{ std::remove_const_t& tm = *(pmap.m_tm); CGAL_precondition(halfedge(f,tm) == next(next(next(halfedge(f,tm),tm),tm),tm)); - return value_type( get(pmap.m_vpm.value(), target(halfedge(f,tm),tm)), - get(pmap.m_vpm.value(), target(next(halfedge(f,tm),tm),tm)), - get(pmap.m_vpm.value(), source(halfedge(f,tm),tm)) ); + return value_type( get(pmap.m_vpm, target(halfedge(f,tm),tm)), + get(pmap.m_vpm, target(next(halfedge(f,tm),tm),tm)), + get(pmap.m_vpm, source(halfedge(f,tm),tm)) ); } inline friend @@ -71,9 +71,9 @@ struct Triangle_from_face_descriptor_map{ std::remove_const_t & tm = *(pmap.m_tm); CGAL_precondition(halfedge(f.first,tm) == next(next(next(halfedge(f.first,tm),tm),tm),tm)); - return value_type( get(pmap.m_vpm.value(), target(halfedge(f.first,tm),tm)), - get(pmap.m_vpm.value(), target(next(halfedge(f.first,tm),tm),tm)), - get(pmap.m_vpm.value(), source(halfedge(f.first,tm),tm)) ); + return value_type( get(pmap.m_vpm, target(halfedge(f.first,tm),tm)), + get(pmap.m_vpm, target(next(halfedge(f.first,tm),tm),tm)), + get(pmap.m_vpm, source(halfedge(f.first,tm),tm)) ); } }; @@ -82,7 +82,7 @@ template < class PolygonMesh, struct Segment_from_edge_descriptor_map{ Segment_from_edge_descriptor_map() - : m_pm(nullptr), m_vpm() + : m_pm(nullptr) {} Segment_from_edge_descriptor_map(PolygonMesh const * pm) @@ -105,7 +105,7 @@ struct Segment_from_edge_descriptor_map{ typedef boost::readable_property_map_tag category; //data std::remove_const_t* m_pm; - std::optional m_vpm; + VertexPointMap m_vpm; //get function for property map inline friend @@ -113,8 +113,8 @@ struct Segment_from_edge_descriptor_map{ get(const Segment_from_edge_descriptor_map& pmap, key_type h) { - return value_type(get(pmap.m_vpm.value(), source(h, *pmap.m_pm) ), - get(pmap.m_vpm.value(), target(h, *pmap.m_pm) ) ); + return value_type(get(pmap.m_vpm, source(h, *pmap.m_pm) ), + get(pmap.m_vpm, target(h, *pmap.m_pm) ) ); } inline friend @@ -122,8 +122,8 @@ struct Segment_from_edge_descriptor_map{ get(const Segment_from_edge_descriptor_map& pmap, const std::pair& h) { - return value_type(get(pmap.m_vpm.value(), source(h.first, *pmap.m_pm) ), - get(pmap.m_vpm.value(), target(h.first, *pmap.m_pm) ) ); + return value_type(get(pmap.m_vpm, source(h.first, *pmap.m_pm) ), + get(pmap.m_vpm, target(h.first, *pmap.m_pm) ) ); } }; @@ -132,7 +132,7 @@ template ::type > struct One_point_from_face_descriptor_map{ One_point_from_face_descriptor_map() - : m_pm(nullptr), m_vpm() + : m_pm(nullptr) {} One_point_from_face_descriptor_map(PolygonMesh const * g) @@ -146,7 +146,7 @@ struct One_point_from_face_descriptor_map{ {} std::remove_const_t* m_pm; - std::optional m_vpm; + VertexPointMap m_vpm; //classical typedefs typedef typename boost::graph_traits::face_descriptor key_type; @@ -160,7 +160,7 @@ struct One_point_from_face_descriptor_map{ get(const One_point_from_face_descriptor_map& m, key_type f) { - return get(m.m_vpm.value(), target(halfedge(f, *m.m_pm), *m.m_pm)); + return get(m.m_vpm, target(halfedge(f, *m.m_pm), *m.m_pm)); } inline friend @@ -168,7 +168,7 @@ struct One_point_from_face_descriptor_map{ get(const One_point_from_face_descriptor_map& m, const std::pair& f) { - return get(m.m_vpm.value(), target(halfedge(f.first, *m.m_pm), *m.m_pm)); + return get(m.m_vpm, target(halfedge(f.first, *m.m_pm), *m.m_pm)); } }; @@ -176,7 +176,7 @@ struct One_point_from_face_descriptor_map{ template < class PolygonMesh, class VertexPointMap = typename boost::property_map::type > struct Source_point_from_edge_descriptor_map{ - Source_point_from_edge_descriptor_map() : m_pm(nullptr), m_vpm() + Source_point_from_edge_descriptor_map() : m_pm(nullptr) {} Source_point_from_edge_descriptor_map(PolygonMesh const * g) @@ -197,7 +197,7 @@ struct Source_point_from_edge_descriptor_map{ //data std::remove_const_t* m_pm; - std::optional m_vpm; + VertexPointMap m_vpm; //get function for property map inline friend @@ -205,7 +205,7 @@ struct Source_point_from_edge_descriptor_map{ get(const Source_point_from_edge_descriptor_map& pmap, key_type h) { - return get(pmap.m_vpm.value(), source(h, *pmap.m_pm) ); + return get(pmap.m_vpm, source(h, *pmap.m_pm) ); } inline friend @@ -213,7 +213,7 @@ struct Source_point_from_edge_descriptor_map{ get(const Source_point_from_edge_descriptor_map& pmap, const std::pair& h) { - return get(pmap.m_vpm.value(), source(h.first, *pmap.m_pm) ); + return get(pmap.m_vpm, source(h.first, *pmap.m_pm) ); } }; diff --git a/Classification/include/CGAL/Classification/Planimetric_grid.h b/Classification/include/CGAL/Classification/Planimetric_grid.h index 673727f2df9e..2fd1adf5773e 100644 --- a/Classification/include/CGAL/Classification/Planimetric_grid.h +++ b/Classification/include/CGAL/Classification/Planimetric_grid.h @@ -59,7 +59,7 @@ class Planimetric_grid using Image_bool = Image; const PointRange* m_points; - std::optional m_point_map; + PointMap m_point_map; Iso_cuboid_3 m_bbox; float m_resolution; @@ -342,7 +342,7 @@ class Planimetric_grid { if (m_lower_scale == nullptr) { - const Point_3& p = get(m_point_map.value(), *(m_points->begin()+index)); + const Point_3& p = get(m_point_map, *(m_points->begin()+index)); return (std::size_t)((p.x() - m_bbox.xmin()) / m_resolution); } @@ -356,7 +356,7 @@ class Planimetric_grid { if (m_lower_scale == nullptr) { - const Point_3& p = get(m_point_map.value(), *(m_points->begin()+index)); + const Point_3& p = get(m_point_map, *(m_points->begin()+index)); return (std::size_t)((p.y() - m_bbox.ymin()) / m_resolution); } diff --git a/Classification/include/CGAL/Classification/Point_set_neighborhood.h b/Classification/include/CGAL/Classification/Point_set_neighborhood.h index 4527ab53f05f..c27c874f0c23 100644 --- a/Classification/include/CGAL/Classification/Point_set_neighborhood.h +++ b/Classification/include/CGAL/Classification/Point_set_neighborhood.h @@ -69,7 +69,7 @@ class Point_set_neighborhood using key_type = std::uint32_t; using category = boost::readable_property_map_tag; - //My_point_property_map () { } + My_point_property_map () { } My_point_property_map (const PointRange *input, PointMap point_map) : input (input), point_map (point_map) { } @@ -88,7 +88,7 @@ class Point_set_neighborhood using Knn = Orthogonal_k_neighbor_search; std::shared_ptr m_tree; - std::optional m_distance; + Distance m_distance; public: @@ -300,7 +300,7 @@ class Point_set_neighborhood void k_neighbors (const Point& query, const unsigned int k, OutputIterator output) const { CGAL_assertion (m_tree != nullptr); - Knn search (*m_tree, query, k, 0, true, m_distance.value()); + Knn search (*m_tree, query, k, 0, true, m_distance); for (typename Knn::iterator it = search.begin(); it != search.end(); ++ it) *(output ++) = it->first; } diff --git a/Classification/include/CGAL/Classification/property_maps.h b/Classification/include/CGAL/Classification/property_maps.h index bad32f9d7ee7..f03e837d8067 100644 --- a/Classification/include/CGAL/Classification/property_maps.h +++ b/Classification/include/CGAL/Classification/property_maps.h @@ -59,6 +59,8 @@ class Face_descriptor_to_center_of_mass_map public: + Face_descriptor_to_center_of_mass_map () + : m_mesh (nullptr) { } Face_descriptor_to_center_of_mass_map (const FaceGraph* mesh) : m_mesh (mesh), m_vpm (get (vertex_point, *m_mesh)) { } Face_descriptor_to_center_of_mass_map (const FaceGraph* mesh, VertexPointMap vpm) diff --git a/Point_set_3/include/CGAL/Point_set_3.h b/Point_set_3/include/CGAL/Point_set_3.h index 62ac11172173..35a7afcad807 100644 --- a/Point_set_3/include/CGAL/Point_set_3.h +++ b/Point_set_3/include/CGAL/Point_set_3.h @@ -17,7 +17,7 @@ #include -#include +#include #include @@ -53,7 +53,11 @@ namespace internal { typedef CGAL::Point_set_3 Point_set_3; private: - + friend class CGAL::Point_set_3; + friend class Properties::Property_container; + template friend class Properties::Property_array; + template friend struct Property_map; + friend class std::vector; size_type value; public: @@ -126,14 +130,18 @@ class Point_set_3 using Index = internal::Point_set_3_index; - typedef typename Properties::Property_container Base; + typedef typename Properties::Property_container Base; template - using Property_map = Properties::Property_array_handle; - + struct Property_map + : public Properties::Property_map_base > + { + typedef Properties::Property_map_base > Base; + Property_map() : Base() {} + Property_map(const Base& pm): Base(pm) {} + }; typedef Property_map Index_map; - // todo: apparently unused? template struct Get_property_map { typedef Property_map type; @@ -163,7 +171,6 @@ class Point_set_3 typedef Property_map Vector_map; ///< Property map of vectors /// \cond SKIP_IN_MANUAL - // todo: CGAL should probably have a general "span" type template class Property_range { @@ -177,17 +184,18 @@ class Point_set_3 private: const_iterator m_begin; const_iterator m_end; - // todo: couldn't this be replaced by std::distance(begin, end)? std::size_t m_size; public: Property_range (const Property_map& pmap, typename Point_set::const_iterator begin, typename Point_set::const_iterator end, - std::size_t size) : - m_begin(boost::make_transform_iterator (begin, Unary_function(pmap))), - m_end(boost::make_transform_iterator (end, Unary_function(pmap))), - m_size(size) {} + std::size_t size) + { + m_begin = boost::make_transform_iterator (begin, Unary_function(pmap)); + m_end = boost::make_transform_iterator (end, Unary_function(pmap)); + m_size = size; + } const_iterator begin() const { return m_begin; } const_iterator end() const { return m_end; } @@ -202,17 +210,11 @@ class Point_set_3 protected: /// \cond SKIP_IN_MANUAL - // todo: this shouldn't be necessary - mutable Base m_base; + Base m_base; Index_map m_indices; Point_map m_points; - std::optional m_normals{}; - - // todo: this is redundant! - // Property_container has its own garbage management, but it's not contiguous - // It should eventually be replaced with something like a Contiguous_property_container - // which places deleted elements at the end - std::size_t m_nb_removed = 0; + Vector_map m_normals; + std::size_t m_nb_removed; /// \endcond public: @@ -228,11 +230,9 @@ class Point_set_3 added. If `false` (default value), the normal map can still be added later on (see `add_normal_map()`). */ - Point_set_3(bool with_normal_map = false) : - m_base(), - m_indices(m_base.template add_property("index", typename Index::size_type(-1))), - m_points(m_base.template add_property("point", CGAL::ORIGIN)) { - + Point_set_3 (bool with_normal_map = false) : m_base() + { + clear(); if (with_normal_map) add_normal_map(); } @@ -240,23 +240,25 @@ class Point_set_3 /*! \brief Assignment operator, all properties with their content are copied. */ - Point_set_3& operator=(const Point_set_3 &ps){ + Point_set_3& operator= (const Point_set_3& ps) + { m_base = ps.m_base; + m_indices = this->property_map ("index").first; + m_points = this->property_map ("point").first; + m_normals = this->property_map ("normal").first; m_nb_removed = ps.m_nb_removed; - if (has_normal_map()) - add_normal_map(); return *this; } /// \cond SKIP_IN_MANUAL // copy constructor (same as assignment) - Point_set_3(const Point_set_3& ps) : - m_base(ps.m_base), - m_indices(m_base.template get_property("index")), - m_points(m_base.template get_property("point")), - m_nb_removed(ps.m_nb_removed) { - if (has_normal_map()) - add_normal_map(); + Point_set_3 (const Point_set_3& ps) + { + m_base = ps.m_base; + m_indices = this->property_map ("index").first; + m_points = this->property_map ("point").first; + m_normals = this->property_map ("normal").first; + m_nb_removed = ps.m_nb_removed; } /// \endcond @@ -264,7 +266,6 @@ class Point_set_3 /// \cond SKIP_IN_MANUAL const Base& base() const { return m_base; } - Base& base() { return m_base; } /// \endcond @@ -294,9 +295,8 @@ class Point_set_3 \note The method `size()` is also available (see `Range`) and does the same thing. */ - std::size_t number_of_points () const { return m_base.capacity() - m_nb_removed; } + std::size_t number_of_points () const { return m_base.size() - m_nb_removed; } /// \cond SKIP_IN_MANUAL - // todo: why is this undocumented, but mentioned in the number_of_points documentation? std::size_t size () const { return number_of_points(); } /// \endcond @@ -310,8 +310,6 @@ class Point_set_3 the point set and `other`. Property maps which are only in `other` are ignored. - todo: this seems backwards to me, isn't it clearer to have an append() function? - \note If `copy_properties()` with `other` as argument is called before calling this method, then all the content of `other` will be copied and no property will be lost in the process. @@ -322,7 +320,8 @@ class Point_set_3 { collect_garbage(); other.collect_garbage(); - m_base.append(other.m_base); + resize (number_of_points() + other.number_of_points()); + m_base.transfer (other.m_base); // Reset indices for (std::size_t i = 0; i < this->m_base.size(); ++ i) @@ -340,8 +339,9 @@ class Point_set_3 */ void clear() { - m_base.resize(0); - m_base.remove_all_properties_except({"index", "point"}); + m_base.clear(); + m_indices = this->add_property_map("index", typename Index::size_type(-1)).first; + m_points = this->add_property_map("point", CGAL::ORIGIN).first; m_nb_removed = 0; } @@ -353,8 +353,14 @@ class Point_set_3 */ void clear_properties() { - // todo: The old version was pretty convoluted, but I'm pretty sure this is the intended behavior - m_base.remove_all_properties_except({"index", "point"}); + Base other; + other.template add("index", typename Index::size_type(-1)); + other.template add("point", CGAL::ORIGIN); + other.resize(m_base.size()); + other.transfer(m_base); + m_base.swap(other); + m_indices = this->property_map("index").first; + m_points = this->property_map("point").first; } /*! @@ -366,13 +372,7 @@ class Point_set_3 \note This method does not change the content of the point set and is only used for optimization. */ - void reserve (std::size_t s) { - std::size_t initial_size = m_base.size(); - m_base.resize(s); - m_nb_removed = m_base.size() - initial_size; - for (std::size_t i = initial_size; i < m_base.size(); ++ i) - m_indices[i] = i; - } + void reserve (std::size_t s) { m_base.reserve (s); } /*! \brief changes size of the point set. @@ -429,8 +429,7 @@ class Point_set_3 { if (m_nb_removed == 0) { - auto new_index = m_base.emplace_back(); - CGAL_assertion(std::size_t(new_index) == size() - 1); + m_base.push_back(); m_indices[size()-1] = size()-1; return m_indices.end() - 1; } @@ -464,7 +463,7 @@ class Point_set_3 iterator insert (const Point& p) { iterator out = insert(); - m_points[*out] = p; + m_points[size()-1] = p; return out; } @@ -504,8 +503,6 @@ class Point_set_3 method allows the user to easily copy one point (along with the values of all its properties) from one point set to another. - todo: this must have been terribly slow, and the new implementation isn't any better. - \param other Point set to which the point to copy belongs \param idx Index of the point to copy in `other` @@ -586,7 +583,7 @@ class Point_set_3 \note The elements are just marked as removed and are not erased from the memory. `collect_garbage()` should be called if the - memory needs to be deallocated. Elements can be recovered with + memory needs to be disallocated. Elements can be recovered with `cancel_removals()`. \note All iterators, pointers and references related to the @@ -607,7 +604,6 @@ class Point_set_3 while (source != last // All elements have been moved && dest != last - 1) // All elements are at the end of the container { - // todo: Why is this writing to cerr? std::cerr << "Swapping " << *source << " and " << *dest << std::endl; std::swap (*(source ++), *(dest --)); } @@ -634,7 +630,6 @@ class Point_set_3 */ void remove (iterator it) { - // todo: Maybe some form of erase-by-iterator should exist? std::iter_swap (it, (end() - 1)); ++ m_nb_removed; } @@ -733,7 +728,7 @@ class Point_set_3 // Sorting based on the indices reorders the point set correctly quick_sort_on_indices ((std::ptrdiff_t)0, (std::ptrdiff_t)(m_base.size() - 1)); - m_base.resize(size ()); + m_base.resize (size ()); m_base.shrink_to_fit (); m_nb_removed = 0; } @@ -787,8 +782,11 @@ class Point_set_3 \param name Name of the property. */ template - bool has_property_map (const std::string& name) const { - return m_base.template property_exists(name); + bool has_property_map (const std::string& name) const + { + std::pair, bool> + pm = m_base.template get (name); + return pm.second; } /*! @@ -808,8 +806,10 @@ class Point_set_3 std::pair, bool> add_property_map (const std::string& name, const T t=T()) { - auto [array, created] = m_base.template get_or_add_property(name, t); - return {{array.get()}, created}; + Property_map pm; + bool added = false; + std::tie (pm, added) = m_base.template add (name, t); + return std::make_pair (pm, added); } /*! @@ -819,26 +819,18 @@ class Point_set_3 \param name Name of the property. - \return Returns an optional containing: the specified property map - or an empty property map (if the property was not found). + \return Returns a pair containing: the specified property map and a + Boolean set to `true` or an empty property map and a Boolean set + to `false` (if the property was not found). */ template - std::optional> - property_map (const std::string& name) - { - auto maybe_property_map = m_base.template get_property_if_exists(name); - if (!maybe_property_map) return {}; - else return {{maybe_property_map.value()}}; - } - - // todo: The const version should return a Const_property_map type - template - std::optional> + std::pair,bool> property_map (const std::string& name) const { - auto maybe_property_map = m_base.template get_property_if_exists(name); - if (!maybe_property_map) return {}; - else return {{maybe_property_map.value()}}; + Property_map pm; + bool okay = false; + std::tie (pm, okay) = m_base.template get(name); + return std::make_pair (pm, okay); } /*! @@ -854,7 +846,7 @@ class Point_set_3 template bool remove_property_map (Property_map& prop) { - return m_base.remove_property(prop.array()); + return m_base.template remove (prop); } /*! @@ -865,7 +857,8 @@ class Point_set_3 */ bool has_normal_map() const { - return m_base.template property_exists("normal"); + std::pair pm = this->property_map ("normal"); + return pm.second; } /*! \brief Convenience method that adds a normal property. @@ -879,9 +872,9 @@ class Point_set_3 */ std::pair add_normal_map (const Vector& default_value = CGAL::NULL_VECTOR) { - auto pair = this->add_property_map ("normal", default_value); - m_normals = {pair.first}; - return pair; + bool out = false; + std::tie (m_normals, out) = this->add_property_map ("normal", default_value); + return std::make_pair (m_normals, out); } /*! \brief returns the property map of the normal property. @@ -894,7 +887,7 @@ class Point_set_3 { if (!m_normals) add_normal_map(); - return m_normals.value(); + return m_normals; } /*! \brief returns the property map of the normal property (constant version). @@ -902,10 +895,9 @@ class Point_set_3 \note The normal property must have been added to the point set before calling this method (see `add_normal_map()`). */ - const Vector_map normal_map () const // todo: This is not how const works + const Vector_map normal_map () const { - CGAL_precondition(m_normals.has_value()); - return m_normals.value(); + return m_normals; } /*! \brief Convenience method that removes the normal property. @@ -915,7 +907,7 @@ class Point_set_3 */ bool remove_normal_map() { - return m_base.remove_property("normal"); + return m_base.template remove (m_normals); } /*! \brief returns the property map of the point property. @@ -944,7 +936,7 @@ class Point_set_3 { m_base.copy_properties (other.base()); - m_normals = this->property_map ("normal"); // In case normal was added + m_normals = this->property_map ("normal").first; // In case normal was added } @@ -970,7 +962,7 @@ class Point_set_3 std::vector > out; out.reserve (prop.size()); for (std::size_t i = 0; i < prop.size(); ++ i) - out.push_back (std::make_pair (prop[i], std::type_index(m_base.property_type(prop[i])))); + out.push_back (std::make_pair (prop[i], std::type_index(m_base.get_type(prop[i])))); return out; } @@ -1018,7 +1010,7 @@ class Point_set_3 std::vector prop = m_base.properties(); for (std::size_t i = 0; i < prop.size(); ++ i) oss << " * \"" << prop[i] << "\" property of type " - << CGAL::demangle(m_base.property_type(prop[i]).name()) << std::endl; + << CGAL::demangle(m_base.get_type(prop[i]).name()) << std::endl; return oss.str(); } @@ -1093,82 +1085,71 @@ class Point_set_3 #endif /// \cond SKIP_IN_MANUAL - // todo: these should probably be reconsidered. template class Property_back_inserter { public: typedef std::output_iterator_tag iterator_category; - typedef Property value_type; + typedef typename Property::value_type value_type; typedef std::ptrdiff_t difference_type; typedef void pointer; typedef void reference; private: - Point_set& ps; - Property_map map; - Index index; + Point_set* ps; + Property* prop; + Index ind; public: - Property_back_inserter( - Point_set& ps, - Properties::Property_array& prop, - Index ind = Index{0} - ) : ps(ps), map(prop), index(ind) {} + Property_back_inserter(Point_set* ps, Property* prop, Index ind=Index()) + : ps(ps), prop (prop), ind(ind) {} Property_back_inserter& operator++() { return *this; } Property_back_inserter& operator++(int) { return *this; } Property_back_inserter& operator*() { return *this; } Property_back_inserter& operator= (const value_type& p) { - - if(ps.size() <= index) - ps.insert(); - put(map, index, p); - ++index; + if(ps->size() <= ind) + ps->insert(); + put(*prop, ind, p); + ++ ind; return *this; - -// auto new_index = *ps.insert(); -// put(map, new_index, p); -// return *this; } }; - // todo: this should be provided by the Property system template class Push_property_map { public: typedef Index key_type; - typedef Property value_type; + typedef typename Property::value_type value_type; typedef value_type& reference; typedef boost::read_write_property_map_tag category; - Point_set& ps; - Property_map map; - mutable Index index; + Point_set* ps; + Property* prop; + mutable Index ind; - Push_property_map( - Point_set& ps, - Properties::Property_array& prop, - Index ind = Index{0} - ) : ps(ps), map(prop) {} + Push_property_map(Point_set* ps = nullptr, + Property* prop = nullptr, + Index ind=Index()) + : ps(ps), prop(prop), ind(ind) {} friend void put(const Push_property_map& pm, Index& i, const value_type& t) { - ++pm.index; - if(pm.ps.size() <= pm.index) - pm.ps.insert(); - put(pm.map, pm.index, t); - i = pm.index; + if(pm.ps->size() <= (pm.ind)) + pm.ps->insert(); + put(*(pm.prop), pm.ind, t); + i = pm.ind; + ++pm.ind; } friend reference get (const Push_property_map& pm, const Index& i) { - return ((*(pm.map))[i]); + return ((*(pm.prop))[i]); } }; @@ -1178,22 +1159,22 @@ class Point_set_3 /// \cgalAdvancedBegin /// Back inserter on indices /// \cgalAdvancedEnd - typedef Property_back_inserter Index_back_inserter; + typedef Property_back_inserter Index_back_inserter; /// \cgalAdvancedType /// \cgalAdvancedBegin /// Back inserter on points /// \cgalAdvancedEnd - typedef Property_back_inserter Point_back_inserter; + typedef Property_back_inserter Point_back_inserter; /// \cgalAdvancedType /// \cgalAdvancedBegin /// Property map for pushing new points /// \cgalAdvancedEnd - typedef Push_property_map Point_push_map; + typedef Push_property_map Point_push_map; /// \cgalAdvancedType /// \cgalAdvancedBegin /// Property map for pushing new vectors /// \cgalAdvancedEnd - typedef Push_property_map Vector_push_map; + typedef Push_property_map Vector_push_map; /*! \cgalAdvancedFunction @@ -1210,10 +1191,10 @@ class Point_set_3 \cgalAdvancedEnd */ template - Push_property_map + Push_property_map > push_property_map (Property_map& prop) { - return Push_property_map (*this, prop.array()); + return Push_property_map > (this, &prop, size()); } /*! \cgalAdvancedFunction @@ -1223,7 +1204,7 @@ class Point_set_3 */ Point_push_map point_push_map () { - return Point_push_map (*this, m_base.template get_property("point")); + return Point_push_map (this, &m_points, size()); } /*! \cgalAdvancedFunction @@ -1236,8 +1217,7 @@ class Point_set_3 */ Vector_push_map normal_push_map () { - CGAL_precondition(m_normals.has_value()); - return Vector_push_map (*this, m_base.template get_property("normal")); + return Vector_push_map (this, &m_normals, size()); } /*! \cgalAdvancedFunction @@ -1247,7 +1227,7 @@ class Point_set_3 */ Index_back_inserter index_back_inserter () { - return Index_back_inserter (*this, m_base.template get_property("index")); + return Index_back_inserter (this, &m_indices, size()); } /*! \cgalAdvancedFunction @@ -1257,7 +1237,7 @@ class Point_set_3 */ Point_back_inserter point_back_inserter () { - return Point_back_inserter (*this, m_base.template get_property("point")); + return Point_back_inserter (this, &m_points, size()); } /// @} diff --git a/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h b/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h index 02138772da9e..67aa82e161c9 100644 --- a/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h +++ b/Point_set_3/include/CGAL/Point_set_3/IO/PLY.h @@ -49,15 +49,17 @@ class Point_set_3_filler class PLY_property_to_point_set_property : public Abstract_ply_property_to_point_set_property { typedef typename Point_set::template Property_map Map; - typedef typename Point_set::template Push_property_map Pmap; + typedef typename Point_set::template Push_property_map Pmap; Map m_map; Pmap m_pmap; std::string m_name; public: - PLY_property_to_point_set_property(Point_set& ps, const std::string& name) : - m_name(name), - m_map(ps.add_property_map(name, Type()).first), - m_pmap(ps.push_property_map(m_map)) {} + PLY_property_to_point_set_property(Point_set& ps, const std::string& name) + : m_name(name) + { + boost::tie(m_map, boost::tuples::ignore) = ps.add_property_map(name, Type()); + m_pmap = ps.push_property_map(m_map); + } virtual void assign(PLY_element& element, typename Point_set::Index index) { @@ -538,92 +540,102 @@ bool write_PLY(std::ostream& os, bool okay = false; { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Int8_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property char " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Uint8_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property uchar " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Int16_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property short " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Uint16_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property ushort " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Int32_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property int " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Uint32_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property uint " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Int64_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property int " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Uint64_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property uint " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Float_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property float " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } { - auto pmap = point_set.template property_map(prop[i]); - if(pmap) + Double_map pmap; + boost::tie(pmap, okay) = point_set.template property_map(prop[i]); + if(okay) { os << "property double " << prop[i] << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); continue; } } diff --git a/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h b/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h index 2d183776f947..bdf76f541ebe 100644 --- a/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h +++ b/Point_set_3/include/CGAL/Point_set_3/IO/XYZ.h @@ -57,7 +57,7 @@ bool read_XYZ(std::istream& is, bool has_normals = false; for(typename CGAL::Point_set_3::const_iterator it=point_set.begin(); it!=point_set.end(); ++it) { - if(std::size_t(*it) < point_set.size() && point_set.normal(*it) != CGAL::NULL_VECTOR) + if(point_set.normal(*it) != CGAL::NULL_VECTOR) { has_normals = true; break; diff --git a/Point_set_3/test/Point_set_3/CMakeLists.txt b/Point_set_3/test/Point_set_3/CMakeLists.txt index 29a9dba31415..4daea00a56b9 100644 --- a/Point_set_3/test/Point_set_3/CMakeLists.txt +++ b/Point_set_3/test/Point_set_3/CMakeLists.txt @@ -7,9 +7,6 @@ project(Point_set_3_Tests) # CGAL and its components find_package(CGAL REQUIRED) -create_single_source_cgal_program("copy_construction_test.cpp") -create_single_source_cgal_program("file_load_test.cpp") -create_single_source_cgal_program("point_insertion_test.cpp") create_single_source_cgal_program("point_set_test.cpp") create_single_source_cgal_program("point_set_test_join.cpp") create_single_source_cgal_program("test_deprecated_io_ps.cpp") diff --git a/Point_set_3/test/Point_set_3/copy_construction_test.cpp b/Point_set_3/test/Point_set_3/copy_construction_test.cpp deleted file mode 100644 index b5936f859966..000000000000 --- a/Point_set_3/test/Point_set_3/copy_construction_test.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include - -#include -#include - -#include - -#include -#include - -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::FT FT; -typedef Kernel::Point_3 Point; -typedef Kernel::Vector_3 Vector; - -typedef CGAL::Point_set_3 Point_set; -typedef std::array Color; - -int main (int, char**) -{ - Point_set original; - original.insert({1, 2, 3}); - original.insert({4, 5, 6}); - - Point_set copy_constructed{original}; - assert(copy_constructed.number_of_points() == original.number_of_points()); - assert(copy_constructed.point(0) == original.point(0)); - assert(copy_constructed.point(1) == original.point(1)); - - Point_set copy_assigned; - copy_assigned = original; - assert(copy_assigned.number_of_points() == original.number_of_points()); - assert(copy_assigned.point(0) == original.point(0)); - assert(copy_assigned.point(1) == original.point(1)); - -} diff --git a/Point_set_3/test/Point_set_3/file_load_test.cpp b/Point_set_3/test/Point_set_3/file_load_test.cpp deleted file mode 100644 index 82d3eb34fbd4..000000000000 --- a/Point_set_3/test/Point_set_3/file_load_test.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include -#include -#include - -#include - -#include -#include - -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::FT FT; -typedef Kernel::Point_3 Point; -typedef Kernel::Vector_3 Vector; - -typedef CGAL::Point_set_3 Point_set; - -int main() { - - std::ifstream stream_xyz{CGAL::data_file_path("points_3/cube.xyz")}; - Point_set points_xyz; - stream_xyz >> points_xyz; - assert(points_xyz.number_of_points() == 8); - assert(!points_xyz.has_normal_map()); - - std::ifstream stream_pwn{CGAL::data_file_path("points_3/cube.pwn")}; - Point_set points_pwn; - stream_pwn >> points_pwn; - assert(points_pwn.number_of_points() == 50000); - assert(points_pwn.has_normal_map()); - -} diff --git a/Point_set_3/test/Point_set_3/point_insertion_test.cpp b/Point_set_3/test/Point_set_3/point_insertion_test.cpp deleted file mode 100644 index ad3ab40e8009..000000000000 --- a/Point_set_3/test/Point_set_3/point_insertion_test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include - -#include -#include -#include - -#include - -#include -#include - -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; -typedef Kernel::FT FT; -typedef Kernel::Point_3 Point; -typedef Kernel::Vector_3 Vector; - -typedef CGAL::Point_set_3 Point_set; - -int main() { - - Point_set points; - assert(points.number_of_points() == 0); - - // Add a large number of points using a generator - std::size_t reserved_points_count = 1000; - CGAL::Random_points_in_cube_3 generator; - points.reserve(reserved_points_count); - assert(points.number_of_points() == 0); - for (std::size_t i = 0; i < reserved_points_count; ++i) - points.insert(*(generator++)); - assert(points.number_of_points() == reserved_points_count); - - // Add more points without making a reservation beforehand - std::size_t additional_points_count = 100; - for (std::size_t i = 0; i < additional_points_count; ++i) - points.insert(*(generator++)); - assert(points.number_of_points() == reserved_points_count + additional_points_count); - -} diff --git a/Point_set_3/test/Point_set_3/point_set_test.cpp b/Point_set_3/test/Point_set_3/point_set_test.cpp index 61808849bd0d..f436114762df 100644 --- a/Point_set_3/test/Point_set_3/point_set_test.cpp +++ b/Point_set_3/test/Point_set_3/point_set_test.cpp @@ -19,15 +19,12 @@ typedef std::array Color; std::size_t nb_test = 0; std::size_t nb_success = 0; -// todo: Automatically numbering the tests is unhelpful void test (bool expr, const char* msg) { ++ nb_test; - if (!expr) { + if (!expr) std::cerr << "Error on test " << nb_test << ": " << msg << std::endl; - // stop on fail, so it's easier to find the error (and so it shows up in CI) - exit(1); - } else + else ++ nb_success; } @@ -83,8 +80,10 @@ int main (int, char**) point_set.collect_garbage(); test (!(point_set.has_garbage()), "point set shouldn't have garbage."); - test (!(point_set.has_property_map("color")), "point set shouldn't have colors."); - auto [color_prop, garbage] = point_set.add_property_map ("color", Color()); + test (!(point_set.has_property_map ("color")), "point set shouldn't have colors."); + Point_set::Property_map color_prop; + bool garbage; + boost::tie (color_prop, garbage) = point_set.add_property_map ("color", Color()); test (point_set.has_property_map ("color"), "point set should have colors."); for (Point_set::iterator it = point_set.begin(); it != point_set.end(); ++ it) @@ -96,7 +95,8 @@ int main (int, char**) test ((get (color_prop, *it) == c), "recovered color is incorrect."); } - auto color_prop_2 = point_set.property_map("color").value(); + Point_set::Property_map color_prop_2; + boost::tie (color_prop_2, garbage) = point_set.property_map("color"); test ((color_prop_2 == color_prop), "color property not recovered correctly."); point_set.remove_normal_map (); @@ -114,13 +114,12 @@ int main (int, char**) for (const auto& p : pnt) std::cerr << " * " << p.first << " with type " << p.second.name() << std::endl; - // todo: was it okay to rename this to num_properties? - test (point_set.base().num_properties() == 4, "point set should have 4 properties."); + test (point_set.base().n_properties() == 4, "point set should have 4 properties."); Point p_before = *(point_set.points().begin()); point_set.clear_properties(); - test (point_set.base().num_properties() == 2, "point set should have 2 properties."); + test (point_set.base().n_properties() == 2, "point set should have 2 properties."); test (!(point_set.has_property_map("label")), "point set shouldn' have labels."); test (!(point_set.has_property_map("intensity")), "point set shouldn' have intensity."); test (!(point_set.empty()), "point set shouldn' be empty."); diff --git a/Point_set_3/test/Point_set_3/point_set_test_join.cpp b/Point_set_3/test/Point_set_3/point_set_test_join.cpp index 3764b4cde997..d46066dba4f4 100644 --- a/Point_set_3/test/Point_set_3/point_set_test_join.cpp +++ b/Point_set_3/test/Point_set_3/point_set_test_join.cpp @@ -30,7 +30,9 @@ void test (bool expr, const char* msg) void print_point_set (const Point_set& ps, const char* msg) { - auto intensity = ps.property_map("intensity"); + Point_set::Property_map intensity; + bool has_intensity; + boost::tie (intensity, has_intensity) = ps.property_map("intensity"); std::cerr << msg << std::endl; for (Point_set::const_iterator it = ps.begin(); it != ps.end(); ++ it) @@ -38,8 +40,8 @@ void print_point_set (const Point_set& ps, const char* msg) std::cerr << *it << ": " << ps.point(*it); if (ps.has_normal_map()) std::cerr << ", normal " << ps.normal(*it); - if (intensity.has_value()) - std::cerr << ", intensity " << intensity.value()[*it]; + if (has_intensity) + std::cerr << ", intensity " << intensity[*it]; std::cerr << std::endl; } } @@ -68,7 +70,10 @@ int main (int, char**) Point_set ps3; ps3.add_normal_map(); - auto [intensity, okay] = ps3.add_property_map("intensity", 0); + Point_set::Property_map intensity; + bool okay; + + boost::tie (intensity, okay) = ps3.add_property_map("intensity", 0); assert (okay); Point_set::iterator it = ps3.insert (Point (double(0), double(1), double(2)), diff --git a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h index e4c1f4e004a5..19ee7849a78e 100644 --- a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h +++ b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/hypothesis.h @@ -747,7 +747,7 @@ namespace CGAL { if (v == Polygon_mesh::null_vertex()) // failed splitting edge return Polygon_mesh::null_halfedge(); - auto &coords = mesh.points(); + typename Polygon_mesh::template Property_map& coords = mesh.points(); coords[v] = *ep.pos; Edge_descriptor e1 = mesh.edge(h); diff --git a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h index 1e771121ee21..02e68d733ce6 100644 --- a/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h +++ b/Polygonal_surface_reconstruction/include/CGAL/Polygonal_surface_reconstruction/internal/point_set_with_planes.h @@ -154,7 +154,7 @@ namespace CGAL { std::size_t idx = 0; for (typename PointRange::const_iterator it = points.begin(); it != points.end(); ++it) { Base_class::m_points[idx] = get(point_map, *it); - Base_class::m_normals.value()[idx] = get(normal_map, *it); + Base_class::m_normals[idx] = get(normal_map, *it); int plane_index = get(plane_index_map, *it); if (plane_index != -1) { auto it_and_bool = plane_index_remap.emplace(plane_index, planar_segments_.size()); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp index 2c861f7c0c71..b26135e231ae 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.cpp @@ -30,8 +30,9 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po backup_existing_colors_and_add_new(); - auto m_cluster_id = m_points->point_set()->property_map("shape"); - if (!m_cluster_id) + bool cluster_found = false; + boost::tie (m_cluster_id, cluster_found) = m_points->point_set()->property_map("shape"); + if (!cluster_found) { std::cerr << "Error! Cluster not found!" << std::endl; abort(); @@ -39,7 +40,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po CGAL::Classification::create_clusters_from_indices (*(m_points->point_set()), m_points->point_set()->point_map(), - m_cluster_id.value(), + m_cluster_id, m_clusters); std::cerr << m_clusters.size() << " cluster(s) found" << std::endl; @@ -56,20 +57,20 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po if (!classif_found) { - auto las_classif = m_points->point_set()->property_map("classification"); - las_found = las_classif.has_value(); - if (las_classif) + Point_set::Property_map las_classif; + boost::tie (las_classif, las_found) = m_points->point_set()->property_map("classification"); + if (las_found) { m_input_is_las = true; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - unsigned char uc = las_classif.value()[*it]; - m_classif.value()[*it] = int(uc); + unsigned char uc = las_classif[*it]; + m_classif[*it] = int(uc); if (!training_found) - m_training.value()[*it] = int(uc); + m_training[*it] = int(uc); } - m_points->point_set()->remove_property_map (las_classif.value()); + m_points->point_set()->remove_property_map (las_classif); classif_found = true; training_found = true; } @@ -84,7 +85,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po { if (training_found) { - int l = m_training.value()[*it]; + int l = m_training[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -94,7 +95,7 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po } if (classif_found) { - int l = m_classif.value()[*it]; + int l = m_classif[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -124,31 +125,31 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int c = m_cluster_id.value()[*it]; + int c = m_cluster_id[*it]; if (training_found) { if (std::size_t(current_idx) != used_indices.size()) // Empty indices -> reorder indices in point set { - if (las_found && (m_training.value()[*it] == 0 || m_training.value()[*it] == 1)) // Unclassified class in LAS - m_training.value()[*it] = -1; - else if (m_training.value()[*it] != -1) - m_training.value()[*it] = used_indices[std::size_t(m_training.value()[*it])]; + if (las_found && (m_training[*it] == 0 || m_training[*it] == 1)) // Unclassified class in LAS + m_training[*it] = -1; + else if (m_training[*it] != -1) + m_training[*it] = used_indices[std::size_t(m_training[*it])]; } - if (c != -1 && m_training.value()[*it] != -1) - m_clusters[c].training() = m_training.value()[*it]; + if (c != -1 && m_training[*it] != -1) + m_clusters[c].training() = m_training[*it]; } if (classif_found) { if (std::size_t(current_idx) != used_indices.size()) // Empty indices -> reorder indices in point set { - if (las_found && (m_classif.value()[*it] == 0 || m_classif.value()[*it] == 1)) // Unclassified class in LAS - m_classif.value()[*it] = -1; - else if (m_classif.value()[*it] != -1) - m_classif.value()[*it] = used_indices[std::size_t(m_classif.value()[*it])]; + if (las_found && (m_classif[*it] == 0 || m_classif[*it] == 1)) // Unclassified class in LAS + m_classif[*it] = -1; + else if (m_classif[*it] != -1) + m_classif[*it] = used_indices[std::size_t(m_classif[*it])]; } - if (c != -1 && m_classif.value()[*it] != -1) - m_clusters[c].label() = m_classif.value()[*it]; + if (c != -1 && m_classif[*it] != -1) + m_clusters[c].label() = m_classif[*it]; } } @@ -239,11 +240,11 @@ Cluster_classification::Cluster_classification(Scene_points_with_normal_item* po Delaunay dt (boost::make_transform_iterator (m_points->point_set()->begin(), Point_set_with_cluster_info (m_points->point_set(), - m_cluster_id.value())), + m_cluster_id)), boost::make_transform_iterator (m_points->point_set()->end(), Point_set_with_cluster_info (m_points->point_set(), - m_cluster_id.value()))); + m_cluster_id))); std::set > adjacencies; @@ -293,16 +294,16 @@ Cluster_classification::~Cluster_classification() for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int c = m_cluster_id.value()[*it]; + int c = m_cluster_id[*it]; if (c != -1) { - m_training.value()[*it] = m_clusters[c].training(); - m_classif.value()[*it] = m_clusters[c].label(); + m_training[*it] = m_clusters[c].training(); + m_classif[*it] = m_clusters[c].label(); } else { - m_training.value()[*it] = -1; - m_classif.value()[*it] = -1; + m_training[*it] = -1; + m_classif[*it] = -1; } } @@ -358,22 +359,22 @@ Cluster_classification::~Cluster_classification() for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif.value()[*it]; + int c = m_classif[*it]; unsigned char lc = 1; // unclassified in LAS standard if (c != -1) lc = label_indices[std::size_t(c)]; las_classif[*it] = lc; - int t = m_training.value()[*it]; + int t = m_training[*it]; unsigned char lt = 1; // unclassified in LAS standard if (t != -1) lt = label_indices[std::size_t(t)]; - m_training.value()[*it] = int(lt); + m_training[*it] = int(lt); } - m_points->point_set()->remove_property_map (m_classif.value()); + m_points->point_set()->remove_property_map (m_classif); } @@ -390,7 +391,7 @@ void Cluster_classification::backup_existing_colors_and_add_new() m_color = m_points->point_set()->add_property_map("real_color").first; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_color.value()[*it] = CGAL::IO::Color ((unsigned char)(255 * m_points->point_set()->red(*it)), + m_color[*it] = CGAL::IO::Color ((unsigned char)(255 * m_points->point_set()->red(*it)), (unsigned char)(255 * m_points->point_set()->green(*it)), (unsigned char)(255 * m_points->point_set()->blue(*it))); @@ -402,15 +403,15 @@ void Cluster_classification::backup_existing_colors_and_add_new() void Cluster_classification::reset_colors() { - if (!m_color) + if (m_color == Point_set::Property_map()) m_points->point_set()->remove_colors(); else { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color.value()[*it]); + m_points->point_set()->set_color(*it, m_color[*it]); - m_points->point_set()->remove_property_map(m_color.value()); + m_points->point_set()->remove_property_map(m_color); } } @@ -437,7 +438,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color.value()[*it]); + m_points->point_set()->set_color(*it, m_color[*it]); } else if (index_color == 1) // classif { @@ -445,7 +446,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { std::size_t c = m_clusters[cid].label(); @@ -463,7 +464,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; float div = 1; if (cid != -1) @@ -485,7 +486,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { @@ -515,7 +516,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { float v = (std::max) (0.f, (std::min)(1.f, m_label_probabilities[corrected_index][cid])); @@ -552,7 +553,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { if (feature->value(cid) > max) @@ -566,7 +567,7 @@ void Cluster_classification::change_color (int index, float* vmin, float* vmax) for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { float v = (feature->value(cid) - min) / (max - min); @@ -597,14 +598,15 @@ int Cluster_classification::real_index_color() const { int out = m_index_color; - if (out == 0 && !m_color) + if (out == 0 && m_color == Point_set::Property_map()) out = -1; return out; } void Cluster_classification::reset_indices () { - auto indices = m_points->point_set()->property_map("index").value(); + Point_set::Property_map indices + = m_points->point_set()->property_map("index").first; m_points->point_set()->unselect_all(); Point_set::Index idx; @@ -627,16 +629,18 @@ void Cluster_classification::compute_features (std::size_t nb_scales, float voxe m_features.clear(); - std::optional normal_map; + Point_set::Vector_map normal_map; bool normals = m_points->point_set()->has_normal_map(); if (normals) normal_map = m_points->point_set()->normal_map(); - bool colors = m_color.has_value(); + bool colors = (m_color != Point_set::Property_map()); - auto echo_map = m_points->point_set()->template property_map("echo"); - if (!echo_map) - echo_map = m_points->point_set()->template property_map("number_of_returns").value(); + Point_set::Property_map echo_map; + bool echo; + boost::tie (echo_map, echo) = m_points->point_set()->template property_map("echo"); + if (!echo) + boost::tie (echo_map, echo) = m_points->point_set()->template property_map("number_of_returns"); Feature_set pointwise_features; @@ -651,11 +655,11 @@ void Cluster_classification::compute_features (std::size_t nb_scales, float voxe generator.generate_point_based_features(pointwise_features); if (normals) - generator.generate_normal_based_features (pointwise_features, normal_map.value()); + generator.generate_normal_based_features (pointwise_features, normal_map); if (colors) - generator.generate_color_based_features (pointwise_features, m_color.value()); - if (echo_map) - generator.generate_echo_based_features (pointwise_features, echo_map.value()); + generator.generate_color_based_features (pointwise_features, m_color); + if (echo) + generator.generate_echo_based_features (pointwise_features, echo_map); #ifdef CGAL_LINKED_WITH_TBB pointwise_features.end_parallel_additions(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h index c89e25e747ba..ef4d006e2ce0 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Cluster_classification.h @@ -125,12 +125,13 @@ class Cluster_classification : public Item_classification_base { typedef typename Point_set::template Property_map Pmap; bool okay = false; - auto pmap = m_points->point_set()->template property_map(name.c_str()); - if (pmap) + Pmap pmap; + boost::tie (pmap, okay) = m_points->point_set()->template property_map(name.c_str()); + if (okay) feature_set.template add > - (*(m_points->point_set()), pmap.value(), name.c_str()); + (*(m_points->point_set()), pmap, name.c_str()); - return pmap.has_value(); + return okay; } void add_selection_to_training_set (std::size_t label) @@ -138,7 +139,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { m_clusters[cid].training() = int(label); @@ -164,7 +165,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { m_clusters[cid].training() = -1; @@ -186,7 +187,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) m_clusters[cid].training() = m_clusters[cid].label(); } @@ -211,7 +212,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { int c = m_clusters[cid].label(); @@ -237,7 +238,7 @@ class Cluster_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int cid = m_cluster_id.value()[*it]; + int cid = m_cluster_id[*it]; if (cid != -1) { int c = m_clusters[cid].label(); @@ -375,10 +376,13 @@ class Cluster_classification : public Item_classification_base std::vector m_clusters; - std::optional> m_color; - std::optional> m_cluster_id; - std::optional> m_training; - std::optional> m_classif; + Point_set::Property_map m_red; + Point_set::Property_map m_green; + Point_set::Property_map m_blue; + Point_set::Property_map m_color; + Point_set::Property_map m_cluster_id; + Point_set::Property_map m_training; + Point_set::Property_map m_classif; std::vector > m_label_probabilities; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp index 3c827920cb5f..12bc858fea91 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.cpp @@ -16,20 +16,23 @@ #include Point_set_item_classification::Point_set_item_classification(Scene_points_with_normal_item* points) - : m_points(points), m_generator(nullptr), m_input_is_las(false), - m_training(), - m_classif(){ + : m_points (points) + , m_generator (nullptr) + , m_input_is_las (false) +{ m_index_color = 1; reset_indices(); - auto cluster_id = m_points->point_set()->property_map("shape"); - if (cluster_id) + Point_set::Property_map cluster_id; + bool cluster_found = false; + boost::tie (cluster_id, cluster_found) = m_points->point_set()->property_map("shape"); + if (cluster_found) { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - int c = cluster_id.value()[*it]; + int c = cluster_id[*it]; if (c == -1) continue; if (std::size_t(c) >= m_clusters.size()) @@ -51,20 +54,20 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n if (!classif_found) { - auto las_classif = m_points->point_set()->property_map("classification"); - las_found = las_classif.has_value(); - if (las_classif) + Point_set::Property_map las_classif; + boost::tie (las_classif, las_found) = m_points->point_set()->property_map("classification"); + if (las_found) { m_input_is_las = true; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - unsigned char uc = las_classif.value()[*it]; - m_classif.value()[*it] = int(uc); + unsigned char uc = las_classif[*it]; + m_classif[*it] = int(uc); if (!training_found) - m_training.value()[*it] = int(uc); + m_training[*it] = int(uc); } - m_points->point_set()->remove_property_map (las_classif.value()); + m_points->point_set()->remove_property_map (las_classif); classif_found = true; training_found = true; } @@ -79,7 +82,7 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n { if (training_found) { - int l = m_training.value()[*it]; + int l = m_training[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -89,7 +92,7 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n } if (classif_found) { - int l = m_classif.value()[*it]; + int l = m_classif[*it]; if (l >= 0) { if (std::size_t(l) >= used_indices.size()) @@ -122,17 +125,17 @@ Point_set_item_classification::Point_set_item_classification(Scene_points_with_n { if (training_found) { - if (las_found && (m_training.value()[*it] == 0 || m_training.value()[*it] == 1)) // Unclassified class in LAS - m_training.value()[*it] = -1; - else if (m_training.value()[*it] != -1) - m_training.value()[*it] = used_indices[std::size_t(m_training.value()[*it])]; + if (las_found && (m_training[*it] == 0 || m_training[*it] == 1)) // Unclassified class in LAS + m_training[*it] = -1; + else if (m_training[*it] != -1) + m_training[*it] = used_indices[std::size_t(m_training[*it])]; } if (classif_found) { - if (las_found && (m_classif.value()[*it] == 0 || m_classif.value()[*it] == 1)) // Unclassified class in LAS - m_classif.value()[*it] = -1; - else if (m_classif.value()[*it] != -1) - m_classif.value()[*it] = used_indices[std::size_t(m_classif.value()[*it])]; + if (las_found && (m_classif[*it] == 0 || m_classif[*it] == 1)) // Unclassified class in LAS + m_classif[*it] = -1; + else if (m_classif[*it] != -1) + m_classif[*it] = used_indices[std::size_t(m_classif[*it])]; } } } @@ -292,22 +295,22 @@ Point_set_item_classification::~Point_set_item_classification() for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif.value()[*it]; + int c = m_classif[*it]; unsigned char lc = 1; // unclassified in LAS standard if (c != -1) lc = label_indices[std::size_t(c)]; las_classif[*it] = lc; - int t = m_training.value()[*it]; + int t = m_training[*it]; unsigned char lt = 1; // unclassified in LAS standard if (t != -1) lt = label_indices[std::size_t(t)]; - m_training.value()[*it] = int(lt); + m_training[*it] = int(lt); } - m_points->point_set()->remove_property_map (m_classif.value()); + m_points->point_set()->remove_property_map (m_classif); } reset_colors(); @@ -324,7 +327,7 @@ void Point_set_item_classification::backup_existing_colors_and_add_new() m_color = m_points->point_set()->add_property_map("real_color").first; for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_color.value()[*it] = CGAL::IO::Color((unsigned char)(255 * m_points->point_set()->red(*it)), + m_color[*it] = CGAL::IO::Color((unsigned char)(255 * m_points->point_set()->red(*it)), (unsigned char)(255 * m_points->point_set()->green(*it)), (unsigned char)(255 * m_points->point_set()->blue(*it))); @@ -336,15 +339,15 @@ void Point_set_item_classification::backup_existing_colors_and_add_new() void Point_set_item_classification::reset_colors() { - if (!m_color) + if (m_color == Point_set::Property_map()) m_points->point_set()->remove_colors(); else { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color.value()[*it]); + m_points->point_set()->set_color(*it, m_color[*it]); - m_points->point_set()->remove_property_map(m_color.value()); + m_points->point_set()->remove_property_map(m_color); } } @@ -371,7 +374,7 @@ void Point_set_item_classification::change_color (int index, float* vmin, float* for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_points->point_set()->set_color(*it, m_color.value()[*it]); + m_points->point_set()->set_color(*it, m_color[*it]); } else if (index_color == 1) // classif { @@ -379,7 +382,7 @@ void Point_set_item_classification::change_color (int index, float* vmin, float* it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - std::size_t c = m_classif.value()[*it]; + std::size_t c = m_classif[*it]; if (c != std::size_t(-1)) color = label_qcolor (m_labels[c]); @@ -393,8 +396,8 @@ void Point_set_item_classification::change_color (int index, float* vmin, float* it != m_points->point_set()->first_selected(); ++ it) { QColor color (0, 0, 0); - int c = m_training.value()[*it]; - int c2 = m_classif.value()[*it]; + int c = m_training[*it]; + int c2 = m_classif[*it]; if (c != -1) color = label_qcolor (m_labels[std::size_t(c)]); @@ -487,14 +490,15 @@ int Point_set_item_classification::real_index_color() const { int out = m_index_color; - if (out == 0 && !m_color) + if (out == 0 && m_color == Point_set::Property_map()) out = -1; return out; } void Point_set_item_classification::reset_indices () { - auto indices = m_points->point_set()->property_map("index").value(); + Point_set::Property_map indices + = m_points->point_set()->property_map("index").first; m_points->point_set()->unselect_all(); Point_set::Index idx; @@ -520,16 +524,18 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales, flo m_features.clear(); - std::optional normal_map; + Point_set::Vector_map normal_map; bool normals = m_points->point_set()->has_normal_map(); if (normals) normal_map = m_points->point_set()->normal_map(); - bool colors = m_color.has_value(); + bool colors = (m_color != Point_set::Property_map()); - auto echo_map = m_points->point_set()->template property_map("echo"); - if (!echo_map) - echo_map = m_points->point_set()->template add_property_map("number_of_returns").first; + Point_set::Property_map echo_map; + bool echo; + boost::tie (echo_map, echo) = m_points->point_set()->template property_map("echo"); + if (!echo) + boost::tie (echo_map, echo) = m_points->point_set()->template property_map("number_of_returns"); m_generator = new Generator (*(m_points->point_set()), m_points->point_set()->point_map(), nb_scales, voxel_size); @@ -542,11 +548,11 @@ void Point_set_item_classification::compute_features (std::size_t nb_scales, flo m_generator->generate_point_based_features(m_features); if (normals) - m_generator->generate_normal_based_features (m_features, normal_map.value()); + m_generator->generate_normal_based_features (m_features, normal_map); if (colors) - m_generator->generate_color_based_features (m_features, m_color.value()); - if (echo_map) - m_generator->generate_echo_based_features (m_features, echo_map.value()); + m_generator->generate_color_based_features (m_features, m_color); + if (echo) + m_generator->generate_echo_based_features (m_features, echo_map); #ifdef CGAL_LINKED_WITH_TBB m_features.end_parallel_additions(); @@ -703,7 +709,7 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - training[*it] = m_training.value()[*it]; + training[*it] = m_training[*it]; if (training[*it] != -1) { nb_label[std::size_t(training[*it])] ++; @@ -752,7 +758,7 @@ void Point_set_item_classification::train(int classifier, const QMultipleInputDi for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) - m_classif.value()[*it] = indices[*it]; + m_classif[*it] = indices[*it]; if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h index 0b0f21e96f6f..f52e2438aabd 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Point_set_item_classification.h @@ -47,9 +47,13 @@ class Point_set_item_classification : public Item_classification_base Point_set::Property_map cluster_id; std::vector* clusters; - Cluster_neighborhood(Point_set* point_set, - std::vector& clusters) - : point_set(point_set), clusters(&clusters), cluster_id(point_set->add_property_map("shape").first) {} + Cluster_neighborhood (Point_set* point_set, + std::vector& clusters) + : point_set (point_set) + , clusters (&clusters) + { + cluster_id = point_set->property_map("shape").first; + } template OutputIterator operator() (const Point_set::Index& idx, @@ -143,16 +147,18 @@ class Point_set_item_classification : public Item_classification_base bool try_adding_simple_feature (const std::string& name) { typedef typename Point_set::template Property_map Pmap; - auto pmap = m_points->point_set()->template property_map(name.c_str()); - if (pmap) + bool okay = false; + Pmap pmap; + boost::tie (pmap, okay) = m_points->point_set()->template property_map(name.c_str()); + if (okay) { std::cerr << "Adding property<" << CGAL::demangle(typeid(Type).name()) << ">(" << name << ") as feature" << std::endl; m_features.template add > - (*(m_points->point_set()), pmap.value(), name.c_str()); + (*(m_points->point_set()), pmap, name.c_str()); } - return pmap.has_value(); + return okay; } void add_selection_to_training_set (std::size_t label) @@ -160,8 +166,8 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - m_training.value()[*it] = int(label); - m_classif.value()[*it] = int(label); + m_training[*it] = int(label); + m_classif[*it] = int(label); } m_points->resetSelection(); @@ -172,8 +178,8 @@ class Point_set_item_classification : public Item_classification_base { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) - if (m_training.value()[*it] == int(label)) - m_training.value()[*it] = -1; + if (m_training[*it] == int(label)) + m_training[*it] = -1; if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); } @@ -182,8 +188,8 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) { - m_training.value()[*it] = -1; - m_classif.value()[*it] = -1; + m_training[*it] = -1; + m_classif[*it] = -1; } if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); @@ -192,7 +198,7 @@ class Point_set_item_classification : public Item_classification_base { for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) - m_training.value()[*it] = -1; + m_training[*it] = -1; if (m_index_color == 1 || m_index_color == 2) change_color (m_index_color); } @@ -200,7 +206,7 @@ class Point_set_item_classification : public Item_classification_base { for (Point_set::const_iterator it = m_points->point_set()->first_selected(); it != m_points->point_set()->end(); ++ it) - m_training.value()[*it] = m_classif.value()[*it]; + m_training[*it] = m_classif[*it]; m_points->resetSelection(); if (m_index_color == 1 || m_index_color == 2) @@ -223,7 +229,7 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif.value()[*it]; + int c = m_classif[*it]; if (c == label) points_item->point_set()->insert (m_points->point_set()->point(*it)); } @@ -245,7 +251,7 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - int c = m_classif.value()[*it]; + int c = m_classif[*it]; if (c != -1) points_item[c]->point_set()->insert (m_points->point_set()->point(*it)); } @@ -265,15 +271,15 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->end(); ++ it) { - if (m_training.value()[*it] == int(position)) - m_training.value()[*it] = -1; - else if (m_training.value()[*it] > int(position)) - m_training.value()[*it] --; - - if (m_classif.value()[*it] == int(position)) - m_classif.value()[*it] = -1; - else if (m_classif.value()[*it] > int(position)) - m_classif.value()[*it] --; + if (m_training[*it] == int(position)) + m_training[*it] = -1; + else if (m_training[*it] > int(position)) + m_training[*it] --; + + if (m_classif[*it] == int(position)) + m_classif[*it] = -1; + else if (m_classif[*it] > int(position)) + m_classif[*it] --; } update_comments_of_point_set_item(); } @@ -358,8 +364,8 @@ class Point_set_item_classification : public Item_classification_base for (Point_set::const_iterator it = m_points->point_set()->begin(); it != m_points->point_set()->first_selected(); ++ it) { - m_classif.value()[*it] = indices[*it]; - ground_truth[*it] = m_training.value()[*it]; + m_classif[*it] = indices[*it]; + ground_truth[*it] = m_training[*it]; } if (m_index_color == 1 || m_index_color == 2) @@ -390,11 +396,14 @@ class Point_set_item_classification : public Item_classification_base std::vector m_clusters; - std::optional> m_color; + Point_set::Property_map m_red; + Point_set::Property_map m_green; + Point_set::Property_map m_blue; + Point_set::Property_map m_color; std::vector > m_label_probabilities; - std::optional> m_training; - std::optional> m_classif; + Point_set::Property_map m_training; + Point_set::Property_map m_classif; Generator* m_generator; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp index 00a9e29a5327..4880c0917db3 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.cpp @@ -15,13 +15,13 @@ Surface_mesh_item_classification::Surface_mesh_item_classification(Scene_surface_mesh_item* mesh) : m_mesh (mesh), m_selection (nullptr), - m_generator (nullptr), - m_training(m_mesh->polyhedron()->add_property_map("f:training", std::size_t(-1)).first), - m_classif(m_mesh->polyhedron()->add_property_map("f:label", std::size_t(-1)).first) + m_generator (nullptr) { m_index_color = 1; backup_existing_colors_and_add_new(); + m_training = m_mesh->polyhedron()->add_property_map("f:training", std::size_t(-1)).first; + m_classif = m_mesh->polyhedron()->add_property_map("f:label", std::size_t(-1)).first; m_labels.add("ground"); m_labels.add("vegetation"); @@ -64,8 +64,8 @@ void Surface_mesh_item_classification::backup_existing_colors_and_add_new() = m_mesh->polyhedron()->add_property_map("f:real_color").first; for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) { - m_real_color.value()[fd] = m_color.value()[fd]; - m_color.value()[fd] = CGAL::IO::Color(128, 128, 128); + m_real_color[fd] = m_color[fd]; + m_color[fd] = CGAL::IO::Color(128, 128, 128); } } else @@ -77,7 +77,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo { m_index_color = index; int index_color = index; - if (index == 0 && !m_real_color) + if (index == 0 && m_real_color == Mesh::Property_map()) index_color = -1; static Color_ramp ramp; @@ -86,12 +86,12 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo if (index_color == -1) // item color { for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) - m_color.value()[fd] = CGAL::IO::Color(128,128,128); + m_color[fd] = CGAL::IO::Color(128,128,128); } else if (index_color == 0) // real colors { for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) - m_color.value()[fd] = m_real_color.value()[fd]; + m_color[fd] = m_real_color[fd]; } else if (index_color == 1) // classif { @@ -103,7 +103,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo if (c != std::size_t(-1)) color = label_qcolor (m_labels[c]); - m_color.value()[fd] = CGAL::IO::Color(color.red(), color.green(), color.blue()); + m_color[fd] = CGAL::IO::Color(color.red(), color.green(), color.blue()); } } else if (index_color == 2) // training @@ -120,7 +120,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo float div = 1; if (c != c2) div = 2; - m_color.value()[fd] = CGAL::IO::Color(color.red() / div, + m_color[fd] = CGAL::IO::Color(color.red() / div, color.green() / div, color.blue() / div); } @@ -135,7 +135,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo { for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) { - m_color.value()[fd] = CGAL::IO::Color((unsigned char)(128), + m_color[fd] = CGAL::IO::Color((unsigned char)(128), (unsigned char)(128), (unsigned char)(128)); } @@ -145,7 +145,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo for(face_descriptor fd : faces(*(m_mesh->polyhedron()))) { float v = (std::max) (0.f, (std::min)(1.f, m_label_probabilities[corrected_index][fd])); - m_color.value()[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), + m_color[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), (unsigned char)(ramp.g(v) * 255), (unsigned char)(ramp.b(v) * 255)); } @@ -189,7 +189,7 @@ void Surface_mesh_item_classification::change_color (int index, float* vmin, flo if (v < 0.f) v = 0.f; if (v > 1.f) v = 1.f; - m_color.value()[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), + m_color[fd] = CGAL::IO::Color((unsigned char)(ramp.r(v) * 255), (unsigned char)(ramp.g(v) * 255), (unsigned char)(ramp.b(v) * 255)); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h index df71c098b744..797e88de4385 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h +++ b/Polyhedron/demo/Polyhedron/Plugins/Classification/Surface_mesh_item_classification.h @@ -200,9 +200,8 @@ class Surface_mesh_item_classification : public Item_classification_base Scene_polyhedron_selection_item* m_selection; Mesh::Property_map m_training; Mesh::Property_map m_classif; - - std::optional> m_color; - std::optional> m_real_color; + Mesh::Property_map m_color; + Mesh::Property_map m_real_color; std::vector > m_label_probabilities; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp index 1507def7cc7f..994dcd2e8d9b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Display_property_plugin.cpp @@ -616,9 +616,11 @@ private Q_SLOTS: return; // Here we only target the property maps added by this plugin, so 'double' is fine - auto property = sm->get_property_map(property_name); - if(property) - sm->remove_property_map(property.value()); + SMesh::Property_map property; + bool found; + std::tie(property, found) = sm->property_map(property_name); + if(found) + sm->remove_property_map(property); } void removeDisplayPluginProperties(Scene_item* item) @@ -667,14 +669,18 @@ private Q_SLOTS: return sector_angle; }; - auto [fangle, fangle_created] = sm->add_property_map( - (extremum == MIN_VALUE ? "f:display_plugin_smallest_angle" : "f:display_plugin_largest_angle"), 0 - ); + bool not_initialized; + SMesh::Property_map fangle; + + if(extremum == MIN_VALUE) + std::tie(fangle, not_initialized) = sm->add_property_map("f:display_plugin_smallest_angle", 0); + else + std::tie(fangle, not_initialized) = sm->add_property_map("f:display_plugin_largest_angle", 0); SMesh& mesh = *sm; auto vpm = get(boost::vertex_point, mesh); - if(fangle_created) + if(not_initialized) { for(face_descriptor f : faces(mesh)) { @@ -747,9 +753,11 @@ private Q_SLOTS: if(sm == nullptr) return; - auto [fjacobian, fjacobian_created] = sm->add_property_map("f:display_plugin_scaled_jacobian", 0); + bool not_initialized; + SMesh::Property_map fjacobian; + std::tie(fjacobian, not_initialized) = sm->add_property_map("f:display_plugin_scaled_jacobian", 0); - if(fjacobian_created) + if(not_initialized) { for(face_descriptor f : faces(*sm)) fjacobian[f] = scaled_jacobian(f, *sm); @@ -788,9 +796,11 @@ private Q_SLOTS: if(sm == nullptr) return; - auto [farea, farea_created] = sm->add_property_map("f:display_plugin_area", 0); + bool not_initialized; + SMesh::Property_map farea; + std::tie(farea, not_initialized) = sm->add_property_map("f:display_plugin_area", 0); - if(farea_created) + if(not_initialized) { for(face_descriptor f : faces(*sm)) farea[f] = area(f, *sm); @@ -1328,26 +1338,26 @@ call_on_PS_property(const std::string& name, const Point_set& ps, const Functor& functor) const { - if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); - else if(ps.template property_map(name)) - return functor(ps.template property_map(name).value()); + if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); + else if(ps.template property_map(name).second) + return functor(ps.template property_map(name).first); return false; } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp index c5cffdac60b2..fc6d564379b2 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Display/Heat_method_plugin.cpp @@ -730,9 +730,11 @@ private Q_SLOTS: return; // Here we only target the property maps added by this plugin, so 'double' is fine - auto property = sm->get_property_map(property_name); - if(property) - sm->remove_property_map(property.value()); + SMesh::Property_map property; + bool found; + std::tie(property, found) = sm->property_map(property_name); + if(found) + sm->remove_property_map(property); } void removePluginProperties(Scene_item* item) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp index 2c56411ec611..c45bfdc619e7 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Engrave_text_plugin.cpp @@ -151,8 +151,8 @@ public : pen.setWidth(0); painter->setPen(pen); painter->setBrush(brush); - std::optional> u; - std::optional> v; + SMesh::Property_map u; + SMesh::Property_map v; u = graph->add_property_map ("h:u", 0.0f).first; @@ -167,11 +167,11 @@ public : boost::graph_traits::face_descriptor f(*fi); QPointF points[3]; boost::graph_traits::halfedge_descriptor h = halfedge(f, *graph);; - points[0] = QPointF(get(*u, h), -get(*v, h)); + points[0] = QPointF(get(u, h), -get(v, h)); h = next(halfedge(f, *graph), *graph); - points[1] = QPointF(get(*u, h), -get(*v, h)); + points[1] = QPointF(get(u, h), -get(v, h)); h = next(next(halfedge(f, *graph), *graph), *graph); - points[2] = QPointF(get(*u, h), -get(*v, h)); + points[2] = QPointF(get(u, h), -get(v, h)); painter->drawPolygon(points,3); } @@ -515,7 +515,7 @@ public Q_SLOTS: sm->add_property_map("v:uv3").first; for(SMesh::Vertex_index v : sm->vertices()) { - uv_map_3.value()[v] = Point_3(uv_map[v][0], uv_map[v] + uv_map_3[v] = Point_3(uv_map[v][0], uv_map[v] [1], 0); if(uv_map[v][0] > xmax) xmax = uv_map[v][0]; @@ -582,7 +582,7 @@ public Q_SLOTS: } // build AABB-tree for face location queries - Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3.value()); + Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3); visu_item = new Scene_polylines_item; connect(visu_item, &Scene_polylines_item::aboutToBeDestroyed, this, @@ -609,7 +609,7 @@ public Q_SLOTS: Face_location loc = Surface_mesh_shortest_path::locate( Point_3(p_2.x(), p_2.y(), 0), - aabb_tree, *sm, uv_map_3.value()); + aabb_tree, *sm, uv_map_3); visu_item->polylines.back().push_back( Surface_mesh_shortest_path::point(loc.first, loc.second, *sm, sm->points())); } @@ -630,8 +630,12 @@ public Q_SLOTS: { component->insert(*bfit); } - auto umap = sm->add_property_map("h:u", 0.0f).first; - auto vmap = sm->add_property_map("h:v", 0.0f).first; + SMesh::Property_map umap; + SMesh::Property_map vmap; + umap = sm->add_property_map + ("h:u", 0.0f).first; + vmap = sm->add_property_map + ("h:v", 0.0f).first; SMesh::Halfedge_iterator it; SMesh::Property_map uv_map = sm->property_map("v:uv").first; @@ -882,7 +886,7 @@ public Q_SLOTS: TriangleMesh& tm) { - Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3.value()); + Tree aabb_tree(faces(*sm).first, faces(*sm).second, *sm, uv_map_3); typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef std::map Map; @@ -903,7 +907,7 @@ public Q_SLOTS: const EPICK::Point_2& pt=fit->vertex(i)->point(); Face_location loc = Surface_mesh_shortest_path::locate( Point_3(pt.x(), pt.y(), 0), - aabb_tree, *sm, uv_map_3.value()); + aabb_tree, *sm, uv_map_3); it->second = add_vertex(Surface_mesh_shortest_path::point(loc.first, loc.second, *sm, sm->points()), tm); } @@ -951,7 +955,7 @@ public Q_SLOTS: EPICK::Vector_2 translation; EPICK::Aff_transformation_2 transfo; std::vector > polylines; - std::optional> uv_map_3; + SMesh::Property_map uv_map_3; SMesh* sm; float xmin, xmax, ymin, ymax; int pointsize; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index dbddde1db691..b998a35750e2 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -121,10 +121,13 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() QApplication::processEvents(); CGAL::Real_timer task_timer; task_timer.start(); - auto [cluster_map, _] = points->add_property_map( + Point_set::Property_map cluster_map; + + if (add_property->isChecked()) + cluster_map = points->add_property_map ("cluster_map").first; + else // Use long name to avoid overwriting potentially existing map - add_property->isChecked() ? "cluster_map" : "cluster_point_set_property_map" - ); + cluster_map = points->add_property_map ("cluster_point_set_property_map").first; // Default value if (neighbor_radius->value() == 0) @@ -173,13 +176,14 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() if (gen_color->isChecked()) { Scene_points_with_normal_item* colored; + Point_set::Property_map red, green, blue; colored = new Scene_points_with_normal_item; colored->setName (QString("%1 (clustering)").arg(item->name())); - auto red = colored->point_set()->add_property_map("red", 0).first; - auto green = colored->point_set()->add_property_map("green", 0).first; - auto blue = colored->point_set()->add_property_map("blue", 0).first; + red = colored->point_set()->add_property_map("red", 0).first; + green = colored->point_set()->add_property_map("green", 0).first; + blue = colored->point_set()->add_property_map("blue", 0).first; colored->point_set()->check_colors(); colored->point_set()->reserve (points->size()); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp index e6cc5dacca4c..2f05e7383088 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp @@ -408,36 +408,42 @@ void Polyhedron_demo_point_set_normal_estimation_plugin::on_actionNormalOrientat CGAL::Timer task_timer; task_timer.start(); std::cerr << "Orient normals with along 2.5D scanlines..." << std::endl; - auto scan_angle = points->property_map("scan_angle"); - auto scan_direction_flag = points->property_map("scan_direction_flag"); + Point_set::Property_map scan_angle; + Point_set::Property_map scan_direction_flag; + bool angle_found = false, flag_found = false; - if (!scan_angle && !scan_direction_flag) + std::tie (scan_angle, angle_found) + = points->property_map("scan_angle"); + std::tie (scan_direction_flag, flag_found) + = points->property_map("scan_direction_flag"); + + if (!angle_found && !flag_found) { std::cerr << " using no additional properties" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters()); } - else if (!scan_angle && scan_direction_flag) + else if (!angle_found && flag_found) { std::cerr << " using scan direction flag" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters(). - scanline_id_map (scan_direction_flag.value())); + scanline_id_map (scan_direction_flag)); } - else if (scan_angle && !scan_direction_flag) + else if (angle_found && !flag_found) { std::cerr << " using scan angle" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters(). - scan_angle_map (scan_angle.value())); + scan_angle_map (scan_angle)); } - else // if (scan_angle && scan_direction_flag) + else // if (angle_found && flag_found) { std::cerr << " using scan angle and direction flag" << std::endl; CGAL::scanline_orient_normals(points->all_or_selection_if_not_empty(), points->parameters(). - scan_angle_map (scan_angle.value()). - scanline_id_map (scan_direction_flag.value())); + scan_angle_map (scan_angle). + scanline_id_map (scan_direction_flag)); } std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Orient normals: " diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp index e431b66ebb52..e79f43e02d6b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp @@ -274,13 +274,13 @@ class Polyhedron_demo_point_set_shape_detection_plugin : } std::string& comments = item->comments(); - std::optional> shape_id; + Point_set::Property_map shape_id; if (dialog.add_property()) { - auto [shape_id_pmap, added] = points->template add_property_map ("shape", -1); - shape_id = shape_id_pmap; + bool added = false; + boost::tie(shape_id, added) = points->template add_property_map ("shape", -1); if (!added) { for (auto it = points->begin(); it != points->end(); ++ it) - shape_id.value()[*it] = -1; + shape_id[*it] = -1; } // Remove previously detected shapes from comments. @@ -382,8 +382,8 @@ class Polyhedron_demo_point_set_shape_detection_plugin : for (auto &item : regions[index].second) { point_item->point_set()->insert(points->point(item)); - if (dialog.add_property() && shape_id) - shape_id.value()[item] = index; + if (dialog.add_property()) + shape_id[item] = index; } unsigned char r, g, b; @@ -559,13 +559,15 @@ class Polyhedron_demo_point_set_shape_detection_plugin : std::string& comments = item->comments(); - std::optional> shape_id; - if (dialog.add_property()) { - auto [shape_id_pmap, added] = points->template add_property_map ("shape", -1); - shape_id = shape_id_pmap; - if (!added) { - for (auto it = points->begin(); it != points->end(); ++ it) - shape_id.value()[*it] = -1; + Point_set::Property_map shape_id; + if (dialog.add_property()) + { + bool added = false; + boost::tie (shape_id, added) = points->template add_property_map ("shape", -1); + if (!added) + { + for (Point_set::iterator it = points->begin(); it != points->end(); ++ it) + shape_id[*it] = -1; } // Remove previously detected shapes from comments @@ -694,8 +696,8 @@ class Polyhedron_demo_point_set_shape_detection_plugin : for(std::size_t i : shape->indices_of_assigned_points()) { point_item->point_set()->insert(points->point(*(points->begin()+i))); - if (dialog.add_property() && shape_id) - shape_id.value()[*(points->begin()+i)] = index; + if (dialog.add_property()) + shape_id[*(points->begin()+i)] = index; } unsigned char r, g, b; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp index 87aaf100fee9..68b777f9ab89 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_to_mesh_distance_plugin.cpp @@ -177,22 +177,27 @@ class Point_set_to_mesh_distance_plugin : } private Q_SLOTS: - void select() { + void select() + { Scene_points_with_normal_item* item = - qobject_cast(scene->item(scene->mainSelectionIndex())); - if (!item || !item->point_set()->has_property_map("distance")) { + qobject_cast(scene->item(scene->mainSelectionIndex())); + if(!item || + !item->point_set()->has_property_map("distance")) + { CGAL::Three::Three::warning("You must select the resulting point set."); return; } - auto distance_map = item->point_set()->property_map("distance").value(); - double distance = dock_widget->distance_spinbox->value(); - for (Point_set::iterator it = item->point_set()->begin(); - it != item->point_set()->end(); ++it) { - if (distance <= distance_map[*it]) - item->point_set()->select(*it); - } - item->invalidateOpenGLBuffers(); - item->itemChanged(); + PMap distance_map; + boost::tie (distance_map, boost::tuples::ignore) = item->point_set()->property_map("distance"); + double distance = dock_widget->distance_spinbox->value(); + for (Point_set::iterator it = item->point_set()->begin(); + it != item->point_set()->end(); ++ it) + { + if(distance <= distance_map[*it]) + item->point_set()->select(*it); + } + item->invalidateOpenGLBuffers(); + item->itemChanged(); } void perform() { @@ -211,14 +216,18 @@ private Q_SLOTS: Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item(*pn); Color_ramp thermal_ramp; thermal_ramp.build_blue(); + PMap distance_map; + PMap fred_map; + PMap fgreen_map; + PMap fblue_map; - + bool d, r, g, b; new_item->point_set()->remove_colors(); //bind pmaps - auto [distance_map, d] = new_item->point_set()->add_property_map("distance", 0); - auto [fred_map, r] = new_item->point_set()->add_property_map("red", 0); - auto [fgreen_map, g] = new_item->point_set()->add_property_map("green", 0); - auto [fblue_map, b] = new_item->point_set()->add_property_map("blue", 0); + boost::tie(distance_map , d) = new_item->point_set()->add_property_map("distance",0); + boost::tie(fred_map , r) = new_item->point_set()->add_property_map("red",0); + boost::tie(fgreen_map, g) = new_item->point_set()->add_property_map("green",0); + boost::tie(fblue_map , b) = new_item->point_set()->add_property_map("blue",0); new_item->point_set()->check_colors(); Point_set* points = new_item->point_set(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp index 38dffa910f21..a8c93069b290 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_advancing_front_impl.cpp @@ -19,8 +19,9 @@ struct Construct{ template Construct(SMesh& mesh, const PointRange& points) - : mesh(mesh), vpmap(get(boost::vertex_point, mesh)) + : mesh(mesh) { + vpmap = get(boost::vertex_point, mesh); for (const auto& p : points) { typename boost::graph_traits::vertex_descriptor v; @@ -191,7 +192,8 @@ SMesh* advancing_front (const Point_set& points, if (structuring) // todo { - auto shape_map = points.property_map("shape").value(); + Point_set::Property_map shape_map + = points.property_map("shape").first; typedef CGAL::Point_set_with_structure Structuring; std::vector planes; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp index 8e075d26ce46..3e320160372e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Surface_reconstruction_polygonal_impl.cpp @@ -26,9 +26,11 @@ SMesh* polygonal_reconstruct (const Point_set& points, CGAL_USE (model_complexity); CGAL_USE (solver_name); - auto shape_map = points.property_map("shape").value(); + Point_set::Property_map shape_map + = points.property_map("shape").first; - Polygonal_surface_reconstruction poly(points, points.point_map(), points.normal_map(), shape_map); + Polygonal_surface_reconstruction poly + (points, points.point_map(), points.normal_map(), shape_map); SMesh* mesh = new SMesh; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp index 596a315eb747..fffd332a8322 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp @@ -269,9 +269,10 @@ public : pen.setWidth(0); painter->setPen(pen); painter->setBrush(brush); + SMesh::Property_map u,v; - auto u = graph->add_property_map("h:u", 0.0f).first; - auto v = graph->add_property_map("h:v", 0.0f).first; + u = graph->add_property_map("h:u", 0.0f).first; + v = graph->add_property_map("h:v", 0.0f).first; for( Component::iterator fi = components->at(m_current_component).begin(); @@ -925,8 +926,11 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio QApplication::restoreOverrideCursor(); QPointF pmin(FLT_MAX, FLT_MAX), pmax(-FLT_MAX, -FLT_MAX); - auto umap = tMesh.add_property_map("h:u", 0.0f).first; - auto vmap = tMesh.add_property_map("h:v", 0.0f).first; + SMesh::Property_map umap; + SMesh::Property_map vmap; + + umap = tMesh.add_property_map("h:u", 0.0f).first; + vmap = tMesh.add_property_map("h:v", 0.0f).first; tMesh.property_stats(std::cerr); Base_face_graph::Halfedge_iterator it; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp index db12442eeb73..c4a060696cdb 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Scene_edit_polyhedron_item.cpp @@ -448,7 +448,11 @@ struct ROI_faces_pmap SMesh::Property_map irmap; ROI_faces_pmap(std::map*, SMesh* mesh) - : mesh(mesh), irmap(mesh->add_property_map("f:is_roi", false).first) {} + :mesh(mesh) + { + //add a is_ROI property_map to mesh + irmap = mesh->add_property_map("f:is_roi",false).first; + } friend value_type get(const ROI_faces_pmap&pmap, const key_type& f) { @@ -531,9 +535,14 @@ struct Is_constrained_map SMesh::Property_map icmap; SMesh* mesh; + Is_constrained_map() + {} Is_constrained_map(std::vector* vec, SMesh* mesh) - : mesh(mesh), icmap(mesh->add_property_map("v:is_control", -1).first) { - for (unsigned int i = 0; i < vec->size(); ++i) { + :mesh(mesh) + { + icmap = mesh->add_property_map("v:is_control", -1).first; + for(unsigned int i=0; isize(); ++i) + { icmap[sm_vertex_descriptor(i)] = (*vec)[i]; } } diff --git a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp index c8a20a888768..75bea895cfac 100644 --- a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp @@ -77,21 +77,21 @@ struct Scene_points_with_normal_item_priv } Scene_points_with_normal_item_priv(Scene_points_with_normal_item* parent) - : m_points() + : m_points(new Point_set) { init_values(parent); } Scene_points_with_normal_item_priv(const Scene_points_with_normal_item& toCopy, Scene_points_with_normal_item* parent) - : m_points(toCopy.d->m_points) + : m_points(new Point_set(*toCopy.d->m_points)) { init_values(parent); } Scene_points_with_normal_item_priv(const SMesh& input_mesh, Scene_points_with_normal_item* parent) - : m_points() + : m_points(new Point_set) { using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; @@ -110,7 +110,8 @@ struct Scene_points_with_normal_item_priv { if(m_points) { - m_points.reset(); + delete m_points; + m_points = nullptr; } delete normal_Slider; delete point_Slider; @@ -118,9 +119,9 @@ struct Scene_points_with_normal_item_priv bool isPointSliderMoving() { return is_point_slider_moving; } void initializeBuffers(CGAL::Three::Viewer_interface *viewer) const; - void compute_normals_and_vertices(); + void compute_normals_and_vertices() const; - std::optional m_points; + Point_set* m_points; std::string m_comments; QAction* actionDeleteSelection; QAction* actionResetSelection; @@ -285,7 +286,7 @@ initializeBuffers(CGAL::Three::Viewer_interface *viewer) const colors_points .shrink_to_fit(); } -void Scene_points_with_normal_item_priv::compute_normals_and_vertices() +void Scene_points_with_normal_item_priv::compute_normals_and_vertices() const { const CGAL::qglviewer::Vec offset = static_cast( CGAL::QGLViewer::QGLViewerPool().first())->offset(); @@ -330,9 +331,9 @@ void Scene_points_with_normal_item_priv::compute_normals_and_vertices() positions_lines.resize(m_points->size() * 3); } - Fill_buffers fill_buffers (&m_points.value(), indices, positions_lines, positions_normals, + Fill_buffers fill_buffers (m_points, indices, positions_lines, positions_normals, item->has_normals(), offset, normal_length * length_factor); - Fill_buffers fill_buffers_2 (&m_points.value(), indices, positions_lines, positions_selected_normals, + Fill_buffers fill_buffers_2 (m_points, indices, positions_lines, positions_selected_normals, item->has_normals(), offset, normal_length * length_factor, m_points->first_selected() - m_points->begin()); @@ -547,7 +548,7 @@ void Scene_points_with_normal_item::selectDuplicates() // Loads point set from .PLY file bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream) { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); d->m_points->clear(); @@ -575,7 +576,7 @@ bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream) // Write point set to .PLY file bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream, bool binary) const { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); d->m_points->reset_indices(); @@ -591,7 +592,7 @@ bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream, bo // Loads point set from .OFF file bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream) { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); d->m_points->clear(); bool ok = CGAL::IO::read_OFF(stream, *(d->m_points)) && !isEmpty(); @@ -604,7 +605,7 @@ bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream) // Write point set to .OFF file bool Scene_points_with_normal_item::write_off_point_set(std::ostream& stream) const { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); d->m_points->reset_indices(); @@ -614,7 +615,7 @@ bool Scene_points_with_normal_item::write_off_point_set(std::ostream& stream) co // Loads point set from .XYZ file bool Scene_points_with_normal_item::read_xyz_point_set(std::istream& stream) { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); d->m_points->clear(); @@ -628,7 +629,7 @@ bool Scene_points_with_normal_item::read_xyz_point_set(std::istream& stream) // Write point set to .XYZ file bool Scene_points_with_normal_item::write_xyz_point_set(std::ostream& stream) const { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); d->m_points->reset_indices(); @@ -638,7 +639,7 @@ bool Scene_points_with_normal_item::write_xyz_point_set(std::ostream& stream) co QString Scene_points_with_normal_item::toolTip() const { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); return QObject::tr("

%1 (color: %4)
" "Point_set_3

" @@ -748,13 +749,13 @@ drawPoints(CGAL::Three::Viewer_interface* viewer) const // Gets wrapped point set Point_set* Scene_points_with_normal_item::point_set() { - Q_ASSERT(d->m_points); - return &d->m_points.value(); + Q_ASSERT(d->m_points != nullptr); + return d->m_points; } const Point_set* Scene_points_with_normal_item::point_set() const { - Q_ASSERT(d->m_points); - return &d->m_points.value(); + Q_ASSERT(d->m_points != nullptr); + return d->m_points; } // Gets wrapped point set @@ -770,14 +771,14 @@ const std::string& Scene_points_with_normal_item::comments() const bool Scene_points_with_normal_item::isEmpty() const { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); return d->m_points->empty(); } void Scene_points_with_normal_item::compute_bbox()const { - Q_ASSERT(d->m_points); + Q_ASSERT(d->m_points != nullptr); Kernel::Iso_cuboid_3 bbox = d->m_points->bounding_box(); setBbox(Bbox(bbox.xmin(),bbox.ymin(),bbox.zmin(), @@ -949,7 +950,8 @@ void Scene_points_with_normal_item::itemAboutToBeDestroyed(Scene_item *item) Scene_item::itemAboutToBeDestroyed(item); if(d && d->m_points && item == this) { - d->m_points.reset(); + delete d->m_points; + d->m_points = nullptr; } } diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 688108e772de..49e2da75e659 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -276,11 +276,11 @@ struct Scene_surface_mesh_item_priv{ mutable QOpenGLShaderProgram *program; Scene_surface_mesh_item *item; - mutable std::optional> fpatch_id_map; + mutable SMesh::Property_map fpatch_id_map; mutable int min_patch_id; - mutable std::optional> v_selection_map; - mutable std::optional> f_selection_map; - mutable std::optional::edge_descriptor, bool>> e_is_feature_map; + mutable SMesh::Property_map v_selection_map; + mutable SMesh::Property_map f_selection_map; + mutable SMesh::Property_map::edge_descriptor, bool> e_is_feature_map; mutable Color_vector colors_; double volume, area; @@ -358,7 +358,7 @@ Scene_surface_mesh_item::vertex_selection_map() if(! d->v_selection_map){ d->v_selection_map = d->smesh_->add_property_map("v:selection").first; } - return d->v_selection_map.value(); + return d->v_selection_map; } Scene_surface_mesh_item::Face_selection_map @@ -367,7 +367,7 @@ Scene_surface_mesh_item::face_selection_map() if(! d->f_selection_map){ d->f_selection_map = d->smesh_->add_property_map("f:selection").first; } - return d->f_selection_map.value(); + return d->f_selection_map; } std::vector& @@ -514,7 +514,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: idx_edge_data_.push_back(source(ed, *smesh_)); idx_edge_data_.push_back(target(ed, *smesh_)); if(has_feature_edges && - get(*e_is_feature_map, ed)) + get(e_is_feature_map, ed)) { idx_feature_edge_data_.push_back(source(ed, *smesh_)); idx_feature_edge_data_.push_back(target(ed, *smesh_)); @@ -556,7 +556,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: { //The sharp features detection produces patch ids >=1, this //is meant to insure the wanted id is in the range [min,max] - QColor c = item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; + QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id]; CGAL::IO::Color color(c.red(),c.green(),c.blue()); CPF::add_color_in_buffer(color, f_colors); } @@ -585,7 +585,7 @@ void Scene_surface_mesh_item_priv::compute_elements(Scene_item_rendering_helper: CGAL::IO::Color *c; if(has_fpatch_id) { - QColor color = item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; + QColor color = item->color_vector()[fpatch_id_map[fd] - min_patch_id]; c = new CGAL::IO::Color(color.red(),color.green(),color.blue()); } else if(has_fcolors) @@ -724,8 +724,8 @@ void Scene_surface_mesh_item_priv::initialize_colors() const int max = 0; min_patch_id = (std::numeric_limits::max)(); for(face_descriptor fd : faces(*smesh_)){ - max = (std::max)(max, fpatch_id_map.value()[fd]); - min_patch_id = (std::min)(min_patch_id, fpatch_id_map.value()[fd]); + max = (std::max)(max, fpatch_id_map[fd]); + min_patch_id = (std::min)(min_patch_id, fpatch_id_map[fd]); } if(item->property("recompute_colors").toBool()) { @@ -911,7 +911,7 @@ void Scene_surface_mesh_item_priv::triangulate_convex_facet(face_descriptor fd, CGAL::IO::Color* color; if(has_fpatch_id) { - QColor c = item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; + QColor c = item->color_vector()[fpatch_id_map[fd] - min_patch_id]; color = new CGAL::IO::Color(c.red(),c.green(),c.blue()); } else if(has_fcolors) @@ -1003,7 +1003,7 @@ Scene_surface_mesh_item_priv::triangulate_facet(face_descriptor fd, CGAL::IO::Color* color; if(has_fpatch_id) { - QColor c= item->color_vector()[fpatch_id_map.value()[fd] - min_patch_id]; + QColor c= item->color_vector()[fpatch_id_map[fd] - min_patch_id]; color = new CGAL::IO::Color(c.red(),c.green(),c.blue()); } else if(has_fcolors) @@ -1447,23 +1447,31 @@ bool Scene_surface_mesh_item::intersect_face(double orig_x, } void Scene_surface_mesh_item::setItemIsMulticolor(bool b) { - if (b) { - d->fpatch_id_map = d->smesh_->add_property_map("f:patch_id", 1).first; + if(b) + { + d->fpatch_id_map = d->smesh_->add_property_map("f:patch_id", 1).first; d->has_fcolors = true; - } else { - d->fpatch_id_map = d->smesh_->get_property_map("f:patch_id"); - if (d->fpatch_id_map) { - d->smesh_->remove_property_map(d->fpatch_id_map.value()); + } + else + { + if(d->smesh_->property_map("f:patch_id").second) + { + d->fpatch_id_map = d->smesh_->property_map("f:patch_id").first; + d->smesh_->remove_property_map(d->fpatch_id_map); d->has_fcolors = false; } - auto fcolormap = d->smesh_->get_property_map("f:color"); - if (fcolormap) { - d->smesh_->remove_property_map(*fcolormap); + if(d->smesh_->property_map("f:color").second) + { + SMesh::Property_map pmap = + d->smesh_->property_map("f:color").first; + d->smesh_->remove_property_map(pmap); d->has_fcolors = false; } - auto vcolormap = d->smesh_->get_property_map("v:color"); - if (vcolormap) { - d->smesh_->remove_property_map(*vcolormap); + if(d->smesh_->property_map("v:color").second) + { + SMesh::Property_map pmap = + d->smesh_->property_map("v:color").first; + d->smesh_->remove_property_map(pmap); d->has_vcolors = false; } this->setProperty("NbPatchIds", 0); //for the joinandsplit_plugin @@ -1573,10 +1581,12 @@ Scene_surface_mesh_item::load_obj(std::istream& in) bool Scene_surface_mesh_item::save_obj(std::ostream& out) const { - auto vnormals = d->smesh_->template get_property_map("v:normal"); + SMesh::template Property_map vnormals; + bool has_normals = false; + boost::tie(vnormals, has_normals) = d->smesh_->template property_map("v:normal"); - if(vnormals) - return CGAL::IO::write_OBJ(out, *(d->smesh_), CGAL::parameters::vertex_normal_map(*vnormals)); + if(has_normals) + return CGAL::IO::write_OBJ(out, *(d->smesh_), CGAL::parameters::vertex_normal_map(vnormals)); else return CGAL::IO::write_OBJ(out, *(d->smesh_)); } @@ -1990,7 +2000,7 @@ void Scene_surface_mesh_item::resetColors() setItemIsMulticolor(false); if(d->has_feature_edges){ for(boost::graph_traits::edge_descriptor e : edges(*d->smesh_)){ - put(d->e_is_feature_map.value(), e, false); + put(d->e_is_feature_map, e, false); } d->has_feature_edges = false; } diff --git a/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp index 75cb252b81ac..0985491e4458 100644 --- a/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_textured_surface_mesh_item.cpp @@ -16,7 +16,7 @@ typedef Edge_container Ec; struct Scene_textured_surface_mesh_item_priv { Scene_textured_surface_mesh_item_priv(Scene_textured_surface_mesh_item* parent) - :sm() + :sm(new SMesh) { item = parent; texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255); @@ -24,7 +24,7 @@ struct Scene_textured_surface_mesh_item_priv vmap = sm->add_property_map("h:v", 0.0f).first; } Scene_textured_surface_mesh_item_priv(const SMesh& p, Scene_textured_surface_mesh_item* parent) - : sm(p) + : sm(new SMesh(p)) { item = parent; @@ -32,8 +32,8 @@ struct Scene_textured_surface_mesh_item_priv umap = sm->add_property_map("h:u", 0.0f).first; vmap = sm->add_property_map("h:v", 0.0f).first; } - Scene_textured_surface_mesh_item_priv(SMesh* const p, Scene_textured_surface_mesh_item* parent) - :sm(*p) + Scene_textured_surface_mesh_item_priv(SMesh* const p,Scene_textured_surface_mesh_item* parent) + :sm(p) { item = parent; texture.GenerateCheckerBoard(2048,2048,128,0,0,0,250,250,255); @@ -41,12 +41,17 @@ struct Scene_textured_surface_mesh_item_priv vmap = sm->add_property_map("h:v", 0.0f).first; } - void compute_normals_and_vertices(void); + ~Scene_textured_surface_mesh_item_priv() + { + delete sm; + } + + void compute_normals_and_vertices(void) const; - std::optional sm; + SMesh* sm; ::Texture texture; - std::optional> umap; - std::optional> vmap; + SMesh::Property_map umap; + SMesh::Property_map vmap; //[Px][Py][Pz][Nx][Ny][Nz][u][v] mutable std::vector faces_buffer; @@ -64,7 +69,7 @@ struct Scene_textured_surface_mesh_item_priv typedef Scene_textured_surface_mesh_item I; }; void -Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) +Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) const { faces_buffer.resize(0); @@ -76,7 +81,7 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) SMesh::Property_map positions = sm->points(); SMesh::Property_map fnormals = - sm->add_property_map("f:normal").first; + sm->add_property_map("f:normal").first; CGAL::Polygon_mesh_processing::compute_face_normals(*sm,fnormals); for(face_iterator f = faces(*sm).begin(); @@ -99,8 +104,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) faces_buffer.push_back(n[1]); faces_buffer.push_back(n[2]); //uvs [2] - const float u = get(*umap, *he); - const float v = get(*vmap, *he); + const float u = get(umap, *he); + const float v = get(vmap, *he); faces_buffer.push_back(u); faces_buffer.push_back(v); } @@ -124,8 +129,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) edges_buffer.push_back(a.y() + offset.y); edges_buffer.push_back(a.z() + offset.z); //uvs [2] - float u = get(*umap, halfedge(*he, *sm)); - float v = get(*vmap, halfedge(*he, *sm)); + float u = get(umap, halfedge(*he, *sm)); + float v = get(vmap, halfedge(*he, *sm)); edges_buffer.push_back(u); edges_buffer.push_back(v); @@ -135,8 +140,8 @@ Scene_textured_surface_mesh_item_priv::compute_normals_and_vertices(void) edges_buffer.push_back(b.z() + offset.z); //uvs [2] - u = get(*umap, opposite(halfedge(*he, *sm), *sm)); - v = get(*vmap, opposite(halfedge(*he, *sm), *sm)); + u = get(umap, opposite(halfedge(*he, *sm), *sm)); + v = get(vmap, opposite(halfedge(*he, *sm), *sm)); edges_buffer.push_back(u); edges_buffer.push_back(v); @@ -283,13 +288,13 @@ void Scene_textured_surface_mesh_item::drawEdges( SMesh* -Scene_textured_surface_mesh_item::textured_face_graph() { return &d->sm.value(); } +Scene_textured_surface_mesh_item::textured_face_graph() { return d->sm; } const SMesh* -Scene_textured_surface_mesh_item::textured_face_graph() const { return &d->sm.value(); } +Scene_textured_surface_mesh_item::textured_face_graph() const { return d->sm; } bool Scene_textured_surface_mesh_item::isEmpty() const { - return (!d->sm) || d->sm->is_empty(); + return (d->sm == nullptr) || d->sm->is_empty(); } void diff --git a/Polyhedron/demo/Polyhedron/include/Point_set_3.h b/Polyhedron/demo/Polyhedron/include/Point_set_3.h index c3b00ac3258a..e72edbd90848 100644 --- a/Polyhedron/demo/Polyhedron/include/Point_set_3.h +++ b/Polyhedron/demo/Polyhedron/include/Point_set_3.h @@ -78,13 +78,13 @@ class Point_set_3 : public CGAL::Point_set_3 m_radius; - std::optional m_red; - std::optional m_green; - std::optional m_blue; - std::optional m_fred; - std::optional m_fgreen; - std::optional m_fblue; + Double_map m_radius; + Byte_map m_red; + Byte_map m_green; + Byte_map m_blue; + Double_map m_fred; + Double_map m_fgreen; + Double_map m_fblue; mutable CGAL::Iterator_range m_const_range; CGAL::Iterator_range m_range; @@ -134,36 +134,38 @@ class Point_set_3 : public CGAL::Point_set_3template add_property_map ("radius", 0.); - m_radius = {radius_map}; + bool out = false; + boost::tie (m_radius, out) = this->template add_property_map ("radius", 0.); return out; } - double& radius (const Index& index) { return m_radius.value()[index]; } - const double& radius (const Index& index) const { return m_radius.value()[index]; } + double& radius (const Index& index) { return m_radius[index]; } + const double& radius (const Index& index) const { return m_radius[index]; } bool check_colors() { - m_red = this->template property_map("red"); - if (!m_red) + bool found = false; + + boost::tie (m_red, found) = this->template property_map("red"); + if (!found) { - m_red = this->template property_map("r"); - if (!m_red) + boost::tie (m_red, found) = this->template property_map("r"); + if (!found) return get_float_colors(); } - m_green = this->template property_map("green"); - if (!m_green) + boost::tie (m_green, found) = this->template property_map("green"); + if (!found) { - m_green = this->template property_map("g"); - if (!m_green) + boost::tie (m_green, found) = this->template property_map("g"); + if (!found) return false; } - m_blue = this->template property_map("blue"); - if (!m_blue) + boost::tie (m_blue, found) = this->template property_map("blue"); + if (!found) { - m_blue = this->template property_map("b"); - if (!m_blue) + boost::tie (m_blue, found) = this->template property_map("b"); + if (!found) return false; } @@ -174,26 +176,29 @@ class Point_set_3 : public CGAL::Point_set_3template property_map("red"); - if (!m_fred) { - m_fred = this->template property_map("r"); - if (!m_fred) - return get_las_colors(); - } + boost::tie (m_fred, found) = this->template property_map("red"); + if (!found) + { + boost::tie (m_fred, found) = this->template property_map("r"); + if (!found) + return get_las_colors(); + } - m_fgreen = this->template property_map("green"); - if (!m_fgreen) { - m_fgreen = this->template property_map("g"); - if (!m_fgreen) - return false; - } + boost::tie (m_fgreen, found) = this->template property_map("green"); + if (!found) + { + boost::tie (m_fgreen, found) = this->template property_map("g"); + if (!found) + return false; + } - m_fblue = this->template property_map("blue"); - if (!m_fblue) { - m_fblue = this->template property_map("b"); - if (!m_fblue) - return false; - } + boost::tie (m_fblue, found) = this->template property_map("blue"); + if (!found) + { + boost::tie (m_fblue, found) = this->template property_map("b"); + if (!found) + return false; + } return true; } @@ -202,24 +207,25 @@ class Point_set_3 : public CGAL::Point_set_3 Ushort_map; + Ushort_map red, green, blue; - auto red = this->template property_map("R"); - if (!red) + boost::tie (red, found) = this->template property_map("R"); + if (!found) return false; - auto green = this->template property_map("G"); - if (!green) + boost::tie (green, found) = this->template property_map("G"); + if (!found) return false; - auto blue = this->template property_map("B"); - if (!blue) + boost::tie (blue, found) = this->template property_map("B"); + if (!found) return false; unsigned int bit_short_to_char = 0; for (iterator it = begin(); it != end(); ++ it) - if (get(*red, *it) > 255 - || get(*green, *it) > 255 - || get(*blue, *it) > 255) + if (get(red, *it) > 255 + || get(green, *it) > 255 + || get(blue, *it) > 255) { bit_short_to_char = 8; break; @@ -230,25 +236,25 @@ class Point_set_3 : public CGAL::Point_set_3template add_property_map("b").first; for (iterator it = begin(); it != end(); ++ it) { - put (*m_red, *it, (unsigned char)((get(*red, *it) >> bit_short_to_char))); - put (*m_green, *it, (unsigned char)((get(*green, *it) >> bit_short_to_char))); - put (*m_blue, *it, (unsigned char)((get(*blue, *it) >> bit_short_to_char))); + put (m_red, *it, (unsigned char)((get(red, *it) >> bit_short_to_char))); + put (m_green, *it, (unsigned char)((get(green, *it) >> bit_short_to_char))); + put (m_blue, *it, (unsigned char)((get(blue, *it) >> bit_short_to_char))); } - this->remove_property_map(*red); - this->remove_property_map(*green); - this->remove_property_map(*blue); + this->remove_property_map(red); + this->remove_property_map(green); + this->remove_property_map(blue); return true; } bool has_colors() const { - return (m_blue || m_fblue); + return (m_blue != Byte_map() || m_fblue != Double_map()); } bool has_byte_colors() const { - return (m_blue); + return (m_blue != Byte_map()); } bool add_colors () @@ -265,47 +271,47 @@ class Point_set_3 : public CGAL::Point_set_3template remove_property_map(*m_red); - this->template remove_property_map(*m_green); - this->template remove_property_map(*m_blue); + this->template remove_property_map(m_red); + this->template remove_property_map(m_green); + this->template remove_property_map(m_blue); } - if (m_fblue) + if (m_fblue != Double_map()) { - this->template remove_property_map(*m_fred); - this->template remove_property_map(*m_fgreen); - this->template remove_property_map(*m_fblue); + this->template remove_property_map(m_fred); + this->template remove_property_map(m_fgreen); + this->template remove_property_map(m_fblue); } } double red (const Index& index) const - { return (m_red) ? m_fred.value()[index] : double(m_red.value()[index]) / 255.; } + { return (m_red == Byte_map()) ? m_fred[index] : double(m_red[index]) / 255.; } double green (const Index& index) const - { return (m_green) ? m_fgreen.value()[index] : double(m_green.value()[index]) / 255.; } + { return (m_green == Byte_map()) ? m_fgreen[index] : double(m_green[index]) / 255.; } double blue (const Index& index) const - { return (m_blue) ? m_fblue.value()[index] : double(m_blue.value()[index]) / 255.; } + { return (m_blue == Byte_map()) ? m_fblue[index] : double(m_blue[index]) / 255.; } void set_color (const Index& index, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0) { - m_red.value()[index] = r; - m_green.value()[index] = g; - m_blue.value()[index] = b; + m_red[index] = r; + m_green[index] = g; + m_blue[index] = b; } void set_color (const Index& index, const QColor& color) { - m_red.value()[index] = color.red(); - m_green.value()[index] = color.green(); - m_blue.value()[index] = color.blue(); + m_red[index] = color.red(); + m_green[index] = color.green(); + m_blue[index] = color.blue(); } template void set_color (const Index& index, const ColorRange& color) { - m_red.value()[index] = color[0]; - m_green.value()[index] = color[1]; - m_blue.value()[index] = color[2]; + m_red[index] = color[0]; + m_green[index] = color[1]; + m_blue[index] = color[2]; } @@ -491,20 +497,18 @@ class Point_set_3 : public CGAL::Point_set_3, - CGAL::internal_np::normal_t, - CGAL::Named_function_parameters< - typename Base::template Property_map, - CGAL::internal_np::point_t - > - > - > - inline parameters() const { + , + CGAL::internal_np::normal_t, + CGAL::Named_function_parameters + , + CGAL::internal_np::point_t> > > + inline parameters() const + { return CGAL::parameters::point_map (this->m_points). - normal_map (this->m_normals.value()). + normal_map (this->m_normals). geom_traits (Kernel()); } diff --git a/Spatial_searching/include/CGAL/Search_traits_adapter.h b/Spatial_searching/include/CGAL/Search_traits_adapter.h index a43dca7bd308..ff640dc639a4 100644 --- a/Spatial_searching/include/CGAL/Search_traits_adapter.h +++ b/Spatial_searching/include/CGAL/Search_traits_adapter.h @@ -66,15 +66,13 @@ struct Get_iso_box_d template class Search_traits_adapter : public Base_traits{ - std::optional ppmap; + PointPropertyMap ppmap; public: typedef Base_traits Base; typedef typename internal::Get_iso_box_d::type Iso_box_d; - Search_traits_adapter() {} - - Search_traits_adapter(const PointPropertyMap& ppmap_, + Search_traits_adapter(const PointPropertyMap& ppmap_=PointPropertyMap(), const Base_traits& base=Base_traits() ):Base_traits(base),ppmap(ppmap_){} @@ -248,30 +246,28 @@ class Search_traits_adapter : public Base_traits{ } Iso_box_d operator() (const Point_with_info& p, const Point_with_info& q) const { - return Base_functor::operator() (get(ppmap.value(),p),get(ppmap.value(),q)); + return Base_functor::operator() (get(ppmap,p),get(ppmap,q)); } }; - const PointPropertyMap& point_property_map() const {return ppmap.value();} + const PointPropertyMap& point_property_map() const {return ppmap;} Construct_cartesian_const_iterator_d construct_cartesian_const_iterator_d_object() const { return Construct_cartesian_const_iterator_d( Base::construct_cartesian_const_iterator_d_object(), - ppmap.value()); + ppmap); } }; template class Distance_adapter : public Base_distance { - std::optional ppmap; + PointPropertyMap ppmap; public: - Distance_adapter() {} - - Distance_adapter(const PointPropertyMap& ppmap_, - const Base_distance& distance = Base_distance()) - : Base_distance(distance), ppmap(ppmap_) {} + Distance_adapter( const PointPropertyMap& ppmap_=PointPropertyMap(), + const Base_distance& distance=Base_distance() + ):Base_distance(distance),ppmap(ppmap_){} using Base_distance::transformed_distance; @@ -279,11 +275,11 @@ class Distance_adapter : public Base_distance { typedef Point_with_info Point_d; typedef typename Base_distance::Query_item Query_item; - const PointPropertyMap& point_property_map() const {return ppmap.value();} + const PointPropertyMap& point_property_map() const {return ppmap;} FT transformed_distance(const Query_item& p1, const Point_with_info& p2) const { - return Base_distance::transformed_distance(p1,get(ppmap.value(),p2)); + return Base_distance::transformed_distance(p1,get(ppmap,p2)); } template diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h index 170ffa75bfa2..fced570172d6 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/OFF.h @@ -108,11 +108,24 @@ bool read_OFF_with_or_without_fcolors(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; + typename Mesh::template Property_map fcm; + bool is_fcm_requested = !(is_default_parameter::value); - if (is_fcm_requested || scanner.has_colors()) { - auto [fcm, created] = sm.template add_property_map("f:color"); - return CGAL::IO::internal::read_OFF_BGL(is, sm, np.face_color_map(fcm)); - } else { + if(!is_fcm_requested && scanner.has_colors()) + { + bool created; + std::tie(fcm, created) = sm.template add_property_map("f:color", Color(0,0,0)); + CGAL_assertion(created); + is_fcm_requested = true; + } + + if(is_fcm_requested) + { + FCM fcolors = choose_parameter(get_parameter(np, internal_np::face_color_map), fcm); + return CGAL::IO::internal::read_OFF_BGL(is, sm, np.face_color_map(fcolors)); + } + else + { return CGAL::IO::internal::read_OFF_BGL(is, sm, np); } } @@ -134,11 +147,24 @@ bool read_OFF_with_or_without_vtextures(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; + typename Mesh::template Property_map vtm; + bool is_vtm_requested = !(is_default_parameter::value); - if (is_vtm_requested || scanner.has_textures()) { - auto [vtm, created] = sm.template add_property_map("v:texcoord"); - return read_OFF_with_or_without_fcolors(is, sm, scanner, np.vertex_texture_map(vtm)); - } else { + if(!is_vtm_requested && scanner.has_textures()) + { + bool created; + std::tie(vtm, created) = sm.template add_property_map("v:texcoord"); + CGAL_assertion(created); + is_vtm_requested = true; + } + + if(is_vtm_requested) + { + VTM vtextures = choose_parameter(get_parameter(np, internal_np::vertex_texture_map), vtm); + return read_OFF_with_or_without_fcolors(is, sm, scanner, np.vertex_texture_map(vtextures)); + } + else + { return read_OFF_with_or_without_fcolors(is, sm, scanner, np); } } @@ -159,11 +185,24 @@ bool read_OFF_with_or_without_vcolors(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; - bool is_vcm_requested = !(is_default_parameter::value); - if (is_vcm_requested || scanner.has_colors()) { - auto [vcm, created] = sm.template add_property_map("v:color"); - return read_OFF_with_or_without_vtextures(is, sm, scanner, np.vertex_color_map(vcm)); - } else { + typename Mesh::template Property_map vcm; + + bool is_vcm_requested = !(is_default_parameter::value); + if(!is_vcm_requested && scanner.has_colors()) + { + bool created; + std::tie(vcm, created) = sm.template add_property_map("v:color", Color(0,0,0)); + CGAL_assertion(created); + is_vcm_requested = true; + } + + if(is_vcm_requested) + { + VCM vcolors = choose_parameter(get_parameter(np, internal_np::vertex_color_map), vcm); + return read_OFF_with_or_without_vtextures(is, sm, scanner, np.vertex_color_map(vcolors)); + } + else + { return read_OFF_with_or_without_vtextures(is, sm, scanner, np); } } @@ -185,11 +224,24 @@ bool read_OFF_with_or_without_vnormals(std::istream& is, using parameters::is_default_parameter; using parameters::get_parameter; + typename Mesh::template Property_map vnm; + bool is_vnm_requested = !(is_default_parameter::value); - if (is_vnm_requested || scanner.has_normals()) { - auto [vnm, created] = sm.template add_property_map("v:normal"); - return read_OFF_with_or_without_vcolors(is, sm, scanner, np.vertex_normal_map(vnm)); - } else { + if(!is_vnm_requested && scanner.has_normals()) + { + bool created; + std::tie(vnm, created) = sm.template add_property_map("v:normal"); + CGAL_assertion(created); + is_vnm_requested = true; + } + + if(is_vnm_requested) + { + VNM vnormals = choose_parameter(get_parameter(np, internal_np::vertex_normal_map), vnm); + return read_OFF_with_or_without_vcolors(is, sm, scanner, np.vertex_normal_map(vnormals)); + } + else + { return read_OFF_with_or_without_vcolors(is, sm, scanner, np); } } @@ -338,10 +390,12 @@ bool write_OFF_with_or_without_fcolors(std::ostream& os, const bool has_fcolors = !(is_default_parameter::value); - auto fcolors = sm.template get_property_map("f:color"); + typename Mesh::template Property_map fcolors; + bool has_internal_fcolors; + std::tie(fcolors, has_internal_fcolors) = sm.template property_map("f:color"); - if(!has_fcolors && fcolors && fcolors->size() > 0) - return write_OFF_BGL(os, sm, np.face_color_map(fcolors.value())); + if(!has_fcolors && has_internal_fcolors && std::distance(fcolors.begin(), fcolors.end()) > 0) + return write_OFF_BGL(os, sm, np.face_color_map(fcolors)); else return write_OFF_BGL(os, sm, np); } @@ -361,10 +415,12 @@ bool write_OFF_with_or_without_vtextures(std::ostream& os, const bool has_vtextures = !(is_default_parameter::value); - auto vtextures = sm.template get_property_map("v:texcoord"); + typename Mesh::template Property_map vtextures; + bool has_internal_vtextures; + std::tie(vtextures, has_internal_vtextures) = sm.template property_map("v:texcoord"); - if(!has_vtextures && vtextures && vtextures->size() > 0) - return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures.value())); + if(!has_vtextures && has_internal_vtextures && std::distance(vtextures.begin(), vtextures.end()) > 0) + return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures)); else return write_OFF_with_or_without_fcolors(os, sm, np); } @@ -382,10 +438,12 @@ bool write_OFF_with_or_without_vcolors(std::ostream& os, const bool has_vcolors = !(is_default_parameter::value); - auto vcolors = sm.template get_property_map("v:color"); + typename Mesh::template Property_map vcolors; + bool has_internal_vcolors; + std::tie(vcolors, has_internal_vcolors) = sm.template property_map("v:color"); - if(!has_vcolors && vcolors && vcolors->size() > 0) - return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors.value())); + if(!has_vcolors && has_internal_vcolors && std::distance(vcolors.begin(), vcolors.end()) > 0) + return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors)); else return write_OFF_with_or_without_vtextures(os, sm, np); } @@ -405,10 +463,12 @@ bool write_OFF_with_or_without_vnormals(std::ostream& os, const bool has_vnormals = !(is_default_parameter::value); - auto vnormals = sm.template get_property_map("v:normal"); + typename Mesh::template Property_map vnormals; + bool has_internal_vnormals; + std::tie(vnormals, has_internal_vnormals) = sm.template property_map("v:normal"); - if(!has_vnormals && vnormals && vnormals->size() > 0) - return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals.value())); + if(!has_vnormals && has_internal_vnormals && std::distance(vnormals.begin(), vnormals.end()) > 0) + return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals)); else return write_OFF_with_or_without_vcolors(os, sm, np); } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h index b28d80689b72..324b78a1af41 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h @@ -23,8 +23,6 @@ #include -#include - namespace CGAL { namespace IO { namespace internal { @@ -59,7 +57,10 @@ class Surface_mesh_filler public: PLY_property_to_surface_mesh_property(Surface_mesh& sm, const std::string& name) - : m_name(name), m_map(sm.template add_property_map(prefix(Simplex()) + name).first){} + : m_name(name) + { + m_map = sm.template add_property_map(prefix(Simplex()) + name).first; + } virtual void assign(PLY_element& element, size_type index) { @@ -78,11 +79,11 @@ class Surface_mesh_filler std::vector m_map_v2v; bool m_use_floats; int m_normals; - std::optional> m_normal_map; + typename Surface_mesh::template Property_map m_normal_map; int m_vcolors; - std::optional> m_vcolor_map; + typename Surface_mesh::template Property_map m_vcolor_map; int m_fcolors; - std::optional> m_fcolor_map; + typename Surface_mesh::template Property_map m_fcolor_map; bool m_use_int32_t; std::string m_index_tag; std::vector m_vertex_properties; @@ -124,7 +125,7 @@ class Surface_mesh_filler { ++ m_normals; if(m_normals == 3) - m_normal_map.emplace(m_mesh.template add_property_map("v:normal").first); + m_normal_map = m_mesh.template add_property_map("v:normal").first; return true; } if(name == "red" || @@ -133,7 +134,7 @@ class Surface_mesh_filler { ++ m_vcolors; if(m_vcolors == 3) - m_vcolor_map.emplace(m_mesh.template add_property_map("v:color").first); + m_vcolor_map = m_mesh.template add_property_map("v:color").first; return true; } return false; @@ -156,7 +157,7 @@ class Surface_mesh_filler { ++ m_fcolors; if(m_fcolors == 3) - m_fcolor_map.emplace(m_mesh.template add_property_map("f:color").first); + m_fcolor_map = m_mesh.template add_property_map("f:color").first; return true; } @@ -282,7 +283,7 @@ class Surface_mesh_filler element.assign(ny, "ny"); element.assign(nz, "nz"); Vector normal(nx, ny, nz); - (*m_normal_map)[vi] = normal; + m_normal_map[vi] = normal; } if(m_vcolors == 3) @@ -303,7 +304,7 @@ class Surface_mesh_filler g = static_cast(std::floor(gf*255)); b = static_cast(std::floor(bf*255)); } - (*m_vcolor_map)[vi] = CGAL::IO::Color(r, g, b); + m_vcolor_map[vi] = CGAL::IO::Color(r, g, b); } } @@ -357,7 +358,7 @@ class Surface_mesh_filler g = static_cast(std::floor(gf*255)); b = static_cast(std::floor(bf*255)); } - (*m_fcolor_map)[fi] = CGAL::IO::Color(r, g, b); + m_fcolor_map[fi] = CGAL::IO::Color(r, g, b); } } @@ -459,10 +460,12 @@ bool fill_simplex_specific_header(std::ostream& os, return true; } + bool okay = false; if(prop == "v:normal") { - auto pmap = sm.template get_property_map(prop); - if(pmap) + Vector_map pmap; + std::tie(pmap, okay) = sm.template property_map(prop); + if(okay) { if(std::is_same::value) { @@ -476,22 +479,23 @@ bool fill_simplex_specific_header(std::ostream& os, << "property double ny" << std::endl << "property double nz" << std::endl; } - printers.push_back(new Property_printer(pmap.value())); + printers.push_back(new Property_printer(pmap)); return true; } } if(prop == "v:color") { - auto pmap = sm.template get_property_map(prop); - if(pmap) + Vcolor_map pmap; + std::tie(pmap, okay) = sm.template property_map(prop); + if(okay) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; - printers.push_back(new Property_printer(pmap.value())); + printers.push_back(new Property_printer(pmap)); return true; } } @@ -513,17 +517,19 @@ bool fill_simplex_specific_header(std::ostream& os, if(prop == "f:connectivity" || prop == "f:removed") return true; + bool okay = false; if(prop == "f:color") { - auto pmap = sm.template get_property_map(prop); - if(pmap) + Fcolor_map pmap; + std::tie(pmap, okay) = sm.template property_map(prop); + if(okay) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; - printers.push_back(new Property_printer(pmap.value())); + printers.push_back(new Property_printer(pmap)); return true; } } @@ -635,25 +641,28 @@ void fill_header_impl(std::tuple, std::vector*>& printers) { constexpr std::size_t cid = s-std::tuple_size>::value; + bool okay = false; { typedef typename Surface_mesh::template Property_map Pmap; - std::optional pmap = sm.template get_property_map(pname); - if(pmap) + Pmap pmap; + std::tie(pmap, okay) = sm.template property_map(pname); + if(okay) { std::string name = get_property_raw_name(pname, Simplex()); os << "property " << type_strings[cid] << " " << name << std::endl; - printers.push_back(new internal::Simple_property_printer(pmap.value())); + printers.push_back(new internal::Simple_property_printer(pmap)); return; } } { typedef typename Surface_mesh::template Property_map> Pmap; - std::optional pmap = sm.template get_property_map>(pname); - if(pmap) + Pmap pmap; + std::tie(pmap, okay) = sm.template property_map>(pname); + if(okay) { std::string name = get_property_raw_name(pname, Simplex()); os << "property list uchar " << type_strings[cid] << " " << name << std::endl; - printers.push_back(new internal::Simple_property_vector_printer(pmap.value())); + printers.push_back(new internal::Simple_property_vector_printer(pmap)); return; } } @@ -688,40 +697,41 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, typedef typename Surface_mesh::Vertex_index VIndex; using VCM = typename internal_np::Lookup_named_param_def< - internal_np::vertex_color_map_t, CGAL_NP_CLASS, - typename Surface_mesh::template Property_map - >::type; - using FCM = typename internal_np::Lookup_named_param_def< - internal_np::face_color_map_t, CGAL_NP_CLASS, - typename Surface_mesh::template Property_map - >::type; + internal_np::vertex_color_map_t, + CGAL_NP_CLASS, + typename Surface_mesh::template Property_map >::type; using parameters::choose_parameter; using parameters::is_default_parameter; using parameters::get_parameter; - constexpr bool has_vcolor = !is_default_parameter::value; - constexpr bool has_fcolor = !is_default_parameter::value; + VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_color_map), VCM()); + bool has_vcolor = !is_default_parameter::value; + + using FCM = typename internal_np::Lookup_named_param_def< + internal_np::face_color_map_t, + CGAL_NP_CLASS, + typename Surface_mesh::template Property_map >::type; + FCM fcm = choose_parameter(get_parameter(np, internal_np::face_color_map), FCM()); + bool has_fcolor = !is_default_parameter::value; std::vector prop = sm.template properties(); - if constexpr (std::is_same::value && has_fcolor) { + if (std::is_same::value && has_fcolor) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; - FCM fcm = get_parameter(np, internal_np::face_color_map); add_color_map()(printers, fcm); } - if constexpr (std::is_same::value && has_vcolor) + if (std::is_same::value && has_vcolor) { os << "property uchar red" << std::endl << "property uchar green" << std::endl << "property uchar blue" << std::endl << "property uchar alpha" << std::endl; - VCM vcm = get_parameter(np, internal_np::vertex_color_map); add_color_map()(printers, vcm); } diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Properties.h b/Surface_mesh/include/CGAL/Surface_mesh/Properties.h new file mode 100644 index 000000000000..a81dd63d530a --- /dev/null +++ b/Surface_mesh/include/CGAL/Surface_mesh/Properties.h @@ -0,0 +1,683 @@ +// Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen +// Copyright (C) 2011 by Graphics & Geometry Group, Bielefeld University +// Copyright (C) 2014 GeometryFactory +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// + +#ifndef CGAL_SURFACE_MESH_PROPERTY_H +#define CGAL_SURFACE_MESH_PROPERTY_H + +#include + +#ifndef DOXYGEN_RUNNING + +#include +#include + +#include +#include +#include +#include + +namespace CGAL { + +namespace Properties { + +/// \addtogroup PkgSurface_mesh +/// +/// @{ + +/// @cond CGAL_DOCUMENT_INTERNALS +class Base_property_array +{ +public: + + /// Default constructor + Base_property_array(const std::string& name) : name_(name) {} + + /// Destructor. + virtual ~Base_property_array() {} + + /// Reserve memory for n elements. + virtual void reserve(size_t n) = 0; + + /// Resize storage to hold n elements. + virtual void resize(size_t n) = 0; + + /// Free unused memory. + virtual void shrink_to_fit() = 0; + + /// Extend the number of elements by one. + virtual void push_back() = 0; + + /// Reset element to default value + virtual void reset(size_t idx) = 0; + + virtual bool transfer(const Base_property_array& other) = 0; + virtual bool transfer(const Base_property_array& other, std::size_t from, std::size_t to) = 0; + + /// Let two elements swap their storage place. + virtual void swap(size_t i0, size_t i1) = 0; + + /// Return a deep copy of self. + virtual Base_property_array* clone () const = 0; + + /// Return an empty copy of self. + virtual Base_property_array* empty_clone () const = 0; + + /// Return the type_info of the property + virtual const std::type_info& type() const = 0; + + /// Return the name of the property + const std::string& name() const { return name_; } + + bool is_same (const Base_property_array& other) + { + return (name() == other.name() && type() == other.type()); + } + +protected: + + std::string name_; +}; + + /// @endcond + + +//== CLASS DEFINITION ========================================================= + +/// @cond CGAL_DOCUMENT_INTERNALS + +template +class Property_array : public Base_property_array +{ +public: + + typedef T value_type; + typedef std::vector vector_type; + typedef typename vector_type::reference reference; + typedef typename vector_type::const_reference const_reference; + typedef typename vector_type::iterator iterator; + typedef typename vector_type::const_iterator const_iterator; + + Property_array(const std::string& name, T t=T()) : Base_property_array(name), value_(t) {} + +public: // virtual interface of Base_property_array + + virtual void reserve(size_t n) + { + data_.reserve(n); + } + + virtual void resize(size_t n) + { + data_.resize(n, value_); + } + + virtual void push_back() + { + data_.push_back(value_); + } + + virtual void reset(size_t idx) + { + data_[idx] = value_; + } + + bool transfer(const Base_property_array& other) + { + const Property_array* pa = dynamic_cast(&other); + if(pa != nullptr){ + std::copy((*pa).data_.begin(), (*pa).data_.end(), data_.end()-(*pa).data_.size()); + return true; + } + return false; + } + + bool transfer(const Base_property_array& other, std::size_t from, std::size_t to) + { + const Property_array* pa = dynamic_cast(&other); + if (pa != nullptr) + { + data_[to] = (*pa)[from]; + return true; + } + + return false; + } + + virtual void shrink_to_fit() + { + vector_type(data_).swap(data_); + } + + virtual void swap(size_t i0, size_t i1) + { + T d(data_[i0]); + data_[i0]=data_[i1]; + data_[i1]=d; + } + + virtual Base_property_array* clone() const + { + Property_array* p = new Property_array(this->name_, this->value_); + p->data_ = data_; + return p; + } + + virtual Base_property_array* empty_clone() const + { + Property_array* p = new Property_array(this->name_, this->value_); + return p; + } + + virtual const std::type_info& type() const { return typeid(T); } + + +public: + + /// Get pointer to array (does not work for T==bool) + const T* data() const + { + return &data_[0]; + } + + /// Access the i'th element. No range check is performed! + reference operator[](std::size_t _idx) + { + CGAL_assertion( _idx < data_.size() ); + return data_[_idx]; + } + + /// Const access to the i'th element. No range check is performed! + const_reference operator[](std::size_t _idx) const + { + CGAL_assertion( _idx < data_.size()); + return data_[_idx]; + } + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + const_iterator begin() const { return data_.begin(); } + const_iterator end() const { return data_.end(); } + +private: + vector_type data_; + value_type value_; +}; + + + /// @endcond + +//== CLASS DEFINITION ========================================================= + +/// @cond CGAL_DOCUMENT_INTERNALS + +template +class Property_container; +/// @endcond + + + + +//== CLASS DEFINITION ========================================================= +/// @cond CGAL_DOCUMENT_INTERNALS + +template +class Property_container +{ +public: + + // default constructor + Property_container() = default; + + // destructor (deletes all property arrays) + virtual ~Property_container() { clear(); } + + // copy constructor: performs deep copy of property arrays + Property_container(const Property_container& _rhs) { operator=(_rhs); } + + Property_container(Property_container&& c) noexcept + { + c.swap(*this); + } + + // assignment: performs deep copy of property arrays + Property_container& operator=(const Property_container& _rhs) + { + if (this != &_rhs) + { + clear(); + parrays_.resize(_rhs.n_properties()); + size_ = _rhs.size(); + capacity_ = _rhs.capacity(); + for (std::size_t i=0; iclone(); + } + return *this; + } + + Property_container& operator=(Property_container&& c) noexcept + { + Property_container tmp(std::move(c)); + tmp.swap(*this); + return *this; + } + + + void transfer(const Property_container& _rhs) + { + for(std::size_t i=0; iis_same (*(_rhs.parrays_[j]))){ + parrays_[i]->transfer(* _rhs.parrays_[j]); + break; + } + } + } + } + + // Copy properties that don't already exist from another container + void copy_properties (const Property_container& _rhs) + { + for (std::size_t i = 0; i < _rhs.parrays_.size(); ++ i) + { + bool property_already_exists = false; + for (std::size_t j = 0; j < parrays_.size(); ++ j) + if (_rhs.parrays_[i]->is_same (*(parrays_[j]))) + { + property_already_exists = true; + break; + } + + if (property_already_exists) + continue; + + parrays_.push_back (_rhs.parrays_[i]->empty_clone()); + parrays_.back()->reserve(capacity_); + parrays_.back()->resize(size_); + } + } + + // Transfer one element with all properties + // WARNING: properties must be the same in the two containers + bool transfer(const Property_container& _rhs, std::size_t from, std::size_t to) + { + bool out = true; + for(std::size_t i=0; itransfer(* _rhs.parrays_[i], from, to))) + out = false; + return out; + } + + // returns the current size of the property arrays + size_t size() const { return size_; } + + // returns the current capacity of the property arrays + size_t capacity() const { return capacity_; } + + // returns the number of property arrays + size_t n_properties() const { return parrays_.size(); } + + // returns a vector of all property names + std::vector properties() const + { + std::vector names; + for (std::size_t i=0; iname()); + return names; + } + + template + struct Get_pmap_type { + typedef typename Ref_class::template Get_property_map::type type; + }; + + template + std::pair::type, bool> + get(const std::string& name, std::size_t i) const + { + typedef typename Ref_class::template Get_property_map::type Pmap; + if (parrays_[i]->name() == name) + { + if (Property_array* array = dynamic_cast*>(parrays_[i])) + return std::make_pair (Pmap(array), true); + } + return std::make_pair(Pmap(), false); + } + + // add a property with name \c name and default value \c t + template + std::pair::type, bool> + add(const std::string& name, const T t=T()) + { + typedef typename Ref_class::template Get_property_map::type Pmap; + for (std::size_t i=0; i out = get(name, i); + if (out.second) + { + out.second = false; + return out; + } + } + + // otherwise add the property + Property_array* p = new Property_array(name, t); + p->reserve(capacity_); + p->resize(size_); + parrays_.push_back(p); + return std::make_pair(Pmap(p), true); + } + + + // get a property by its name. returns invalid property if it does not exist. + template + std::pair::type, bool> + get(const std::string& name) const + { + typedef typename Ref_class::template Get_property_map::type Pmap; + for (std::size_t i=0; i out = get(name, i); + if (out.second) + return out; + } + return std::make_pair(Pmap(), false); + } + + + // returns a property if it exists, otherwise it creates it first. + template + typename Get_pmap_type::type + get_or_add(const std::string& name, const T t=T()) + { + typename Ref_class::template Get_property_map::type p; + bool b; + boost::tie(p,b)= get(name); + if (!b) p = add(name, t).first; + return p; + } + + + // get the type of property by its name. returns typeid(void) if it does not exist. + const std::type_info& + get_type(const std::string& name) const + { + for (std::size_t i=0; iname() == name) + return parrays_[i]->type(); + return typeid(void); + } + + + // delete a property + template + bool + remove(typename Get_pmap_type::type& h) + { + typename std::vector::iterator it=parrays_.begin(), end=parrays_.end(); + for (; it!=end; ++it) + { + if (*it == h.parray_) + { + delete *it; + parrays_.erase(it); + h.reset(); + return true; + } + } + return false; + } + + + // delete all properties + void clear() + { + for (std::size_t i=0; ireserve(n); + capacity_ = (std::max)(n, capacity_); + } + + // resize all arrays to size n + void resize(size_t n) + { + for (std::size_t i=0; iresize(n); + size_ = n; + } + + // resize the vector of properties to n, deleting all other properties + void resize_property_array(size_t n) + { + if (parrays_.size()<=n) + return; + for (std::size_t i=n; ishrink_to_fit(); + capacity_ = size_; + } + + // add a new element to each vector + void push_back() + { + for (std::size_t i=0; ipush_back(); + ++size_; + capacity_ = ((std::max)(size_, capacity_)); + } + + // reset element to its default property values + void reset(size_t idx) + { + for (std::size_t i=0; ireset(idx); + } + + // swap elements i0 and i1 in all arrays + void swap(size_t i0, size_t i1) const + { + for (std::size_t i=0; iswap(i0, i1); + } + + // swap content with other Property_container + void swap (Property_container& other) + { + this->parrays_.swap (other.parrays_); + std::swap(this->size_, other.size_); + std::swap(this->capacity_, other.capacity_); + } + +private: + std::vector parrays_; + size_t size_ = 0; + size_t capacity_ = 0; +}; + + /// @endcond + +#ifndef DOXYGEN_RUNNING +/// +/// +/// `Property_map` enables to attach properties to the simplices of a +/// surface mesh. +/// +/// @tparam Key The key type of the property map. It must be a model of `Index`. +/// @tparam Value The value type of the property. +/// +/// \cgalModels{LvaluePropertyMap} +/// +template +class Property_map_base +/// @cond CGAL_DOCUMENT_INTERNALS + : public boost::put_get_helper< + typename Property_array::reference, + CRTP_derived_class> +/// @endcond +{ +public: + typedef I key_type; + typedef T value_type; + typedef boost::lvalue_property_map_tag category; + +#ifndef DOXYGEN_RUNNING + + typedef typename Property_array::reference reference; + typedef typename Property_array::const_reference const_reference; + typedef typename Property_array::iterator iterator; + typedef typename Property_array::const_iterator const_iterator; +#else + /// A reference to the value type of the property. + typedef unspecified_type reference; + + /// A const reference to the value type of the property. + typedef unspecified_type const_reference; +#endif + +#ifndef DOXYGEN_RUNNING + template + friend class Property_container; +#endif + +public: +/// @cond CGAL_DOCUMENT_INTERNALS + Property_map_base(Property_array* p=nullptr) : parray_(p) {} + + Property_map_base(Property_map_base&& pm) noexcept + : parray_(std::exchange(pm.parray_, nullptr)) + {} + + Property_map_base(const Property_map_base& pm) + : parray_(pm.parray_) + {} + + Property_map_base& operator=(const Property_map_base& pm) + { + parray_ = pm.parray_; + return *this; + } + + void reset() + { + parray_ = nullptr; + } + /// @endcond + +public: + /// \name Accessing Properties + //@{ +#ifdef DOXYGEN_RUNNING + /// Conversion to a Boolean. It is \c true when the property map + /// can be used, and \c false otherwise. + operator bool () const; +#else + explicit operator bool() const { + return parray_ != nullptr; + } +#endif + + bool operator==(const Property_map_base& pm) const { + return parray_ == pm.parray_; + } + + bool operator!=(const Property_map_base& pm) const { + return parray_ != pm.parray_; + } + + /// Access the property associated with the key \c i. + reference operator[](const I& i) + { + CGAL_assertion(parray_ != nullptr); + return (*parray_)[i]; + } + + /// Access the property associated with the key \c i. + reference operator[](const I& i) const + { + CGAL_assertion(parray_ != nullptr); + return (*parray_)[i]; + } + + iterator begin() { return parray_->begin(); } + iterator end() { return parray_->end(); } + const_iterator begin() const { return parray_->begin(); } + const_iterator end() const { return parray_->end(); } + + bool transfer (const Property_map_base& other) + { + return parray_->transfer(*(other.parray_)); + } + + bool transfer (const Property_map_base& other, std::size_t from, std::size_t to) + { + return parray_->transfer(*(other.parray_), from, to); + } + + /// Allows access to the underlying storage of the property. This + /// is useful when the key associated with the properties is + /// unimportant and only the properties are of interest + /// (e.g. rendering). + /// + /// \returns a pointer to the underlying storage of the property. + const T* data() const + { + CGAL_assertion(parray_ != nullptr); + return parray_->data(); + } + + //@} +#ifndef CGAL_TEST_SURFACE_MESH +private: +#endif + + Property_array& array() + { + CGAL_assertion(parray_ != nullptr); + return *parray_; + } + + const Property_array& array() const + { + CGAL_assertion(parray_ != nullptr); + return *parray_; + } + + Property_array* parray_; +}; + +#endif // DOXYGEN_RUNNING + +///@} + +} // Properties + +} // CGAL + +#endif // DOXYGEN_RUNNING + +//============================================================================= +#endif // CGAL_SURFACE_MESH_PROPERTY_H +//============================================================================= diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 7411c8889eec..559aec265f09 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -59,7 +59,7 @@ namespace CGAL { class SM_Index { public: - typedef std::uint32_t size_type; + typedef std::uint32_t size_type; /// Constructor. %Default construction creates an invalid index. /// We write -1, which is /// (std::numeric_limits::max)() @@ -238,10 +238,9 @@ namespace CGAL { class SM_Edge_index { public: - // todo: why does Edge_index use a different size type from other indices? - typedef std::size_t size_type; + typedef std::uint32_t size_type; - SM_Edge_index() = default; + SM_Edge_index() : halfedge_((std::numeric_limits::max)()) { } explicit SM_Edge_index(size_type idx) : halfedge_(idx * 2) { } @@ -310,7 +309,7 @@ namespace CGAL { } private: - SM_Halfedge_index halfedge_{}; + SM_Halfedge_index halfedge_; }; #endif @@ -341,15 +340,19 @@ class Surface_mesh public: #ifndef DOXYGEN_RUNNING - template - using Property_container = Properties::Property_container; - - template - using Property_array = Properties::Property_array; - - template - using Property_map = Properties::Property_array_handle; + template + struct Property_map : Properties::Property_map_base > + { + typedef Properties::Property_map_base > Base; + typedef typename Base::reference reference; + Property_map() = default; + Property_map(const Base& pm): Base(pm) {} + }; + template + struct Get_property_map { + typedef Property_map type; + }; #endif // DOXYGEN_RUNNING /// \name Basic Types @@ -903,39 +906,10 @@ class Surface_mesh ///@{ /// %Default constructor. - Surface_mesh() : - vconn_(vprops_.add_property("v:connectivity")), - hconn_(hprops_.add_property("h:connectivity")), - fconn_(fprops_.add_property("f:connectivity")), - vpoint_(vprops_.add_property("v:point")), - vremoved_(vprops_.add_property("v:removed")), - eremoved_(eprops_.add_property("e:removed")), - fremoved_(fprops_.add_property("f:removed")), - anonymous_property_(0) {} + Surface_mesh(); /// Copy constructor: copies `rhs` to `*this`. Performs a deep copy of all properties. - Surface_mesh(const Surface_mesh& rhs) : - vprops_(rhs.vprops_) - , hprops_(rhs.hprops_) - , fprops_(rhs.fprops_) - , eprops_(rhs.eprops_) - , vpoint_(vprops_.get_property("v:point")) - , vconn_(vprops_.get_property("v:connectivity")) - , hconn_(hprops_.get_property("h:connectivity")) - , fconn_(fprops_.get_property("f:connectivity")) - , vremoved_(vprops_.get_property("v:removed")) - , eremoved_(eprops_.get_property("e:removed")) - , fremoved_(fprops_.get_property("f:removed")) - , removed_vertices_(rhs.removed_vertices_) - , removed_edges_(rhs.removed_edges_) - , removed_faces_(rhs.removed_faces_) - , vertices_freelist_(rhs.vertices_freelist_) - , edges_freelist_(rhs.edges_freelist_) - , faces_freelist_(rhs.faces_freelist_) - , garbage_(rhs.garbage_) - , recycle_(rhs.recycle_) - , anonymous_property_(rhs.anonymous_property_) - {} + Surface_mesh(const Surface_mesh& rhs) { *this = rhs; } /// Move constructor. Surface_mesh(Surface_mesh&& sm) @@ -943,13 +917,13 @@ class Surface_mesh , hprops_(std::move(sm.hprops_)) , eprops_(std::move(sm.eprops_)) , fprops_(std::move(sm.fprops_)) - , vpoint_(vprops_.get_property("v:point")) - , vconn_(vprops_.get_property("v:connectivity")) - , hconn_(hprops_.get_property("h:connectivity")) - , fconn_(fprops_.get_property("f:connectivity")) - , vremoved_(vprops_.get_property("v:removed")) - , eremoved_(eprops_.get_property("e:removed")) - , fremoved_(fprops_.get_property("f:removed")) + , vconn_(std::move(sm.vconn_)) + , hconn_(std::move(sm.hconn_)) + , fconn_(std::move(sm.fconn_)) + , vremoved_(std::move(sm.vremoved_)) + , eremoved_(std::move(sm.eremoved_)) + , fremoved_(std::move(sm.fremoved_)) + , vpoint_(std::move(sm.vpoint_)) , removed_vertices_(std::exchange(sm.removed_vertices_, 0)) , removed_edges_(std::exchange(sm.removed_edges_, 0)) , removed_faces_(std::exchange(sm.removed_faces_, 0)) @@ -967,14 +941,32 @@ class Surface_mesh /// move assignment Surface_mesh& operator=(Surface_mesh&& sm) { - // Moving properties also moves their contents without invalidating references vprops_ = std::move(sm.vprops_); hprops_ = std::move(sm.hprops_); eprops_ = std::move(sm.eprops_); fprops_ = std::move(sm.fprops_); + vconn_ = std::move(sm.vconn_); + hconn_ = std::move(sm.hconn_); + fconn_ = std::move(sm.fconn_); + vremoved_ = std::move(sm.vremoved_); + eremoved_ = std::move(sm.eremoved_); + fremoved_ = std::move(sm.fremoved_); + vpoint_ = std::move(sm.vpoint_); + removed_vertices_ = std::exchange(sm.removed_vertices_, 0); + removed_edges_ = std::exchange(sm.removed_edges_, 0); + removed_faces_ = std::exchange(sm.removed_faces_, 0); + vertices_freelist_ = std::exchange(sm.vertices_freelist_, (std::numeric_limits::max)()); + edges_freelist_ = std::exchange(sm.edges_freelist_,(std::numeric_limits::max)()); + faces_freelist_ = std::exchange(sm.faces_freelist_,(std::numeric_limits::max)()); + garbage_ = std::exchange(sm.garbage_, false); + recycle_ = std::exchange(sm.recycle_, true); + anonymous_property_ = std::exchange(sm.anonymous_property_, 0); return *this; } + /// assigns `rhs` to `*this`. Does not copy custom properties. + Surface_mesh& assign(const Surface_mesh& rhs); + ///@} public: @@ -994,7 +986,8 @@ class Surface_mesh vprops_.reset(Vertex_index(idx)); return Vertex_index(idx); } else { - return Vertex_index(vprops_.emplace_back()); + vprops_.push_back(); + return Vertex_index(num_vertices()-1); } } @@ -1027,8 +1020,11 @@ class Surface_mesh eprops_.reset(Edge_index(Halfedge_index(idx))); return Halfedge_index(idx); } else { - eprops_.emplace_back(); - return Halfedge_index(hprops_.emplace_group_back(2)); + eprops_.push_back(); + hprops_.push_back(); + hprops_.push_back(); + + return Halfedge_index(num_halfedges()-2); } } @@ -1061,7 +1057,8 @@ class Surface_mesh fremoved_[Face_index(idx)] = false; return Face_index(idx); } else { - return Face_index(fprops_.emplace_back()); + fprops_.push_back(); + return Face_index(num_faces()-1); } } @@ -1179,25 +1176,12 @@ class Surface_mesh } /// removes all vertices, halfedge, edges and faces. Collects garbage but keeps all property maps. - void clear_without_removing_property_maps() - { - vprops_.reserve(0); - hprops_.reserve(0); - eprops_.reserve(0); - fprops_.reserve(0); - } + void clear_without_removing_property_maps(); /// removes all vertices, halfedge, edges and faces. Collects garbage and removes all property maps added by a call to `add_property_map()` for all simplex types. /// /// After calling this method, the object is the same as a newly constructed object. The additional property maps are also removed and must thus be re-added if needed. - void clear() - { - clear_without_removing_property_maps(); - vprops_.remove_all_properties_except({"v:connectivity", "v:point"}); - hprops_.remove_all_properties_except({"h:connectivity"}); - fprops_.remove_all_properties_except({"f:connectivity"}); - eprops_.remove_all_properties_except({}); - } + void clear(); /// reserves space for vertices, halfedges, edges, faces, and their currently @@ -1228,15 +1212,17 @@ class Surface_mesh /// the copied simplices get the default value of the property. bool join(const Surface_mesh& other) { - - // Record the original sizes of the property maps - const size_type nv = number_of_vertices(), nh = number_of_halfedges(), nf = number_of_faces(); + // increase capacity + const size_type nv = num_vertices(), nh = num_halfedges(), nf = num_faces(); + resize(num_vertices()+ other.num_vertices(), + num_edges()+ other.num_edges(), + num_faces()+ other.num_faces()); // append properties in the free space created by resize - vprops_.append(other.vprops_); - hprops_.append(other.hprops_); - fprops_.append(other.fprops_); - eprops_.append(other.eprops_); + vprops_.transfer(other.vprops_); + hprops_.transfer(other.hprops_); + fprops_.transfer(other.fprops_); + eprops_.transfer(other.eprops_); // translate halfedge index in vertex -> halfedge for(size_type i = nv; i < nv+other.num_vertices(); i++){ @@ -1416,10 +1402,10 @@ class Surface_mesh /// upon addition of new elements. /// When set to `true` (default value), new elements are first picked in the garbage (if any) /// while if set to `false` only new elements are created. - void set_recycle_garbage(bool b) { recycle_ = b; } + void set_recycle_garbage(bool b); /// Getter - bool does_recycle_garbage() const { return recycle_; } + bool does_recycle_garbage() const; /// @cond CGAL_DOCUMENT_INTERNALS /// removes unused memory from vectors. This shrinks the storage @@ -2004,30 +1990,48 @@ class Surface_mesh private: //--------------------------------------------------- property handling - template - Property_container& get_property_container() { - if constexpr (std::is_same_v) - return vprops_; - if constexpr (std::is_same_v) - return hprops_; - if constexpr (std::is_same_v) - return eprops_; - if constexpr (std::is_same_v) - return fprops_; - } - - template - const Property_container& get_property_container() const { - if constexpr (std::is_same_v) - return vprops_; - if constexpr (std::is_same_v) - return hprops_; - if constexpr (std::is_same_v) - return eprops_; - if constexpr (std::is_same_v) - return fprops_; - } - + // Property_selector maps an index type to a property_container, the + // dummy is necessary to make it a partial specialization (full + // specializations are only allowed at namespace scope). + template + struct Property_selector {}; + + template + struct Property_selector::Vertex_index, dummy> { + CGAL::Surface_mesh

* m_; + Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} + Properties::Property_container::Vertex_index>& + operator()() { return m_->vprops_; } + void resize_property_array() { m_->vprops_.resize_property_array(3); } + }; + template + struct Property_selector::Halfedge_index, dummy> { + CGAL::Surface_mesh

* m_; + Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} + Properties::Property_container::Halfedge_index>& + operator()() { return m_->hprops_; } + void resize_property_array() { m_->hprops_.resize_property_array(1); } + }; + template + struct Property_selector::Edge_index, dummy> { + CGAL::Surface_mesh

* m_; + Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} + Properties::Property_container::Edge_index>& + operator()() { return m_->eprops_; } + void resize_property_array() { m_->eprops_.resize_property_array(1); } + }; + template + struct Property_selector::Face_index, dummy> { + CGAL::Surface_mesh

* m_; + Property_selector(CGAL::Surface_mesh

* m) : m_(m) {} + Properties::Property_container::Face_index>& + operator()() { return m_->fprops_; } + void resize_property_array() { m_->fprops_.resize_property_array(2); } + }; public: @@ -2056,57 +2060,56 @@ class Surface_mesh /// for index type `I`. Returns the property map together with a Boolean /// that is `true` if a new map was created. In case it already exists /// the existing map together with `false` is returned. + + template std::pair, bool> add_property_map(std::string name=std::string(), const T t=T()) { if(name.empty()){ - // todo: maybe this should be done by the property container itself? std::ostringstream oss; oss << "anonymous-property-" << anonymous_property_++; name = std::string(oss.str()); } - auto [array, created] = - const_cast*>(this)->get_property_container().template get_or_add_property(name, t); - return {{array.get()}, created}; + return Property_selector(this)().template add(name, t); } /// returns a property map named `name` with key type `I` and value type `T`, - /// and a Boolean that is `true` if the property was created. + /// and a Boolean that is `true` if the property exists. + /// In case it does not exist the Boolean is `false` and the behavior of + /// the property map is undefined. template - std::pair, bool> - property_map(const std::string& name) const { - auto [array, created] = - const_cast*>(this)->get_property_container().template get_or_add_property(name); - return {{array.get()}, created}; + std::pair,bool> property_map(const std::string& name) const + { + return Property_selector(const_cast(this))().template get(name); } - /// returns a property map named `name` with key type `I` and value type `T`, - /// if such a property map exists - template - std::optional> - get_property_map(const std::string& name) { - auto maybe_property_map = get_property_container().template get_property_if_exists(name); - if (!maybe_property_map) return {}; - else return {{maybe_property_map.value()}}; - } + /// removes property map `p`. The memory allocated for that property map is + /// freed. template - std::optional> - get_property_map(const std::string& name) const { - auto maybe_property_map = const_cast*>(this)->get_property_container().template get_property_if_exists(name); - if (!maybe_property_map) return {}; - else return {{maybe_property_map.value()}}; + void remove_property_map(Property_map& p) + { + (Property_selector(this)()).template remove(p); } - /// removes property map `p`. The memory allocated for that property map is - /// freed. - template - void remove_property_map(Property_map p) { - // Maybe this could be replaced with removal by name? - const_cast*>(this)->get_property_container().template remove_property(p.array()); + /// removes all property maps for index type `I` added by a call to `add_property_map()`. + /// The memory allocated for those property maps is freed. + template + void remove_property_maps() + { + Property_selector(this).resize_property_array(); } + /// removes all property maps for all index types added by a call to `add_property_map()`. + /// The memory allocated for those property maps is freed. + void remove_all_property_maps() + { + remove_property_maps(); + remove_property_maps(); + remove_property_maps(); + remove_property_maps(); + } /// @cond CGAL_DOCUMENT_INTERNALS /// returns the std::type_info of the value type of the @@ -2118,7 +2121,7 @@ class Surface_mesh template const std::type_info& property_type(const std::string& name) { - return get_property_container().property_type(name); + return Property_selector(this)().get_type(name); } /// @endcond @@ -2127,16 +2130,14 @@ class Surface_mesh template std::vector properties() const { - return get_property_container().properties(); + return Property_selector(const_cast(this))().properties(); } /// returns the property for the string "v:point". - // todo: shouldn't this return a const pmap? - // In the original version, there was no difference between const & non-const maps - Property_array& + Property_map points() const { return vpoint_; } - Property_array& + Property_map& points() { return vpoint_; } /// returns the point associated to vertex `v`. @@ -2207,31 +2208,30 @@ class Surface_mesh void adjust_incoming_halfedge(Vertex_index v); private: //------------------------------------------------------- private data + Properties::Property_container vprops_; + Properties::Property_container hprops_; + Properties::Property_container eprops_; + Properties::Property_container fprops_; - Property_container vprops_; - Property_container hprops_; - Property_container eprops_; - Property_container fprops_; - - Property_array& vconn_; - Property_array& hconn_; - Property_array& fconn_; + Property_map vconn_; + Property_map hconn_; + Property_map fconn_; - Property_array &vremoved_; - Property_array &eremoved_; - Property_array &fremoved_; + Property_map vremoved_; + Property_map eremoved_; + Property_map fremoved_; - Property_array& vpoint_; + Property_map vpoint_; - size_type removed_vertices_ = 0; - size_type removed_edges_ = 0; - size_type removed_faces_ = 0; + size_type removed_vertices_; + size_type removed_edges_; + size_type removed_faces_; - size_type vertices_freelist_ = std::numeric_limits::max(); - size_type edges_freelist_ = std::numeric_limits::max(); - size_type faces_freelist_ = std::numeric_limits::max(); - bool garbage_ = false; - bool recycle_ = true; + size_type vertices_freelist_; + size_type edges_freelist_; + size_type faces_freelist_; + bool garbage_; + bool recycle_; size_type anonymous_property_; }; @@ -2280,6 +2280,27 @@ class Surface_mesh /*! @} */ +template +Surface_mesh

:: +Surface_mesh() +{ + // allocate standard properties + // same list is used in operator=() and assign() + vconn_ = add_property_map("v:connectivity").first; + hconn_ = add_property_map("h:connectivity").first; + fconn_ = add_property_map("f:connectivity").first; + vpoint_ = add_property_map("v:point").first; + vremoved_ = add_property_map("v:removed", false).first; + eremoved_ = add_property_map("e:removed", false).first; + fremoved_ = add_property_map("f:removed", false).first; + + removed_vertices_ = removed_edges_ = removed_faces_ = 0; + vertices_freelist_ = edges_freelist_ = faces_freelist_ = (std::numeric_limits::max)(); + garbage_ = false; + recycle_ = true; + anonymous_property_ = 0; +} + //----------------------------------------------------------------------------- template @@ -2295,14 +2316,77 @@ operator=(const Surface_mesh

& rhs) eprops_ = rhs.eprops_; fprops_ = rhs.fprops_; - // Property array refs don't need to be reassigned, - // because the deep copy updated the values they point to + // property handles contain pointers, have to be reassigned + vconn_ = property_map("v:connectivity").first; + hconn_ = property_map("h:connectivity").first; + fconn_ = property_map("f:connectivity").first; + vremoved_ = property_map("v:removed").first; + eremoved_ = property_map("e:removed").first; + fremoved_ = property_map("f:removed").first; + vpoint_ = property_map("v:point").first; + + // how many elements are removed? + removed_vertices_ = rhs.removed_vertices_; + removed_edges_ = rhs.removed_edges_; + removed_faces_ = rhs.removed_faces_; + vertices_freelist_ = rhs.vertices_freelist_; + edges_freelist_ = rhs.edges_freelist_; + faces_freelist_ = rhs.faces_freelist_; + garbage_ = rhs.garbage_; + recycle_ = rhs.recycle_; + anonymous_property_ = rhs.anonymous_property_; + } + + return *this; +} + +//----------------------------------------------------------------------------- +template +Surface_mesh

& +Surface_mesh

:: +assign(const Surface_mesh

& rhs) +{ + if (this != &rhs) + { + // clear properties + vprops_.clear(); + hprops_.clear(); + eprops_.clear(); + fprops_.clear(); + + // allocate standard properties + vconn_ = add_property_map("v:connectivity").first; + hconn_ = add_property_map("h:connectivity").first; + fconn_ = add_property_map("f:connectivity").first; + vpoint_ = add_property_map("v:point").first; + vremoved_ = add_property_map("v:removed", false).first; + eremoved_ = add_property_map("e:removed", false).first; + fremoved_ = add_property_map("f:removed", false).first; + + // copy properties from other mesh + vconn_.array() = rhs.vconn_.array(); + hconn_.array() = rhs.hconn_.array(); + fconn_.array() = rhs.fconn_.array(); + vpoint_.array() = rhs.vpoint_.array(); + vremoved_.array() = rhs.vremoved_.array(); + eremoved_.array() = rhs.eremoved_.array(); + fremoved_.array() = rhs.fremoved_.array(); + + // resize (needed by property containers) + vprops_.resize(rhs.num_vertices()); + hprops_.resize(rhs.num_halfedges()); + eprops_.resize(rhs.num_edges()); + fprops_.resize(rhs.num_faces()); // how many elements are removed? + removed_vertices_ = rhs.removed_vertices_; + removed_edges_ = rhs.removed_edges_; + removed_faces_ = rhs.removed_faces_; vertices_freelist_ = rhs.vertices_freelist_; edges_freelist_ = rhs.edges_freelist_; faces_freelist_ = rhs.faces_freelist_; + garbage_ = rhs.garbage_; recycle_ = rhs.recycle_; anonymous_property_ = rhs.anonymous_property_; } @@ -2310,6 +2394,38 @@ operator=(const Surface_mesh

& rhs) return *this; } +//----------------------------------------------------------------------------- +template +void +Surface_mesh

:: +clear() +{ + clear_without_removing_property_maps(); + remove_all_property_maps(); +} + +template +void +Surface_mesh

:: +clear_without_removing_property_maps() +{ + vprops_.resize(0); + hprops_.resize(0); + eprops_.resize(0); + fprops_.resize(0); + + vprops_.shrink_to_fit(); + hprops_.shrink_to_fit(); + eprops_.shrink_to_fit(); + fprops_.shrink_to_fit(); + + removed_vertices_ = removed_edges_ = removed_faces_ = 0; + vertices_freelist_ = edges_freelist_ = faces_freelist_ = (std::numeric_limits::max)(); + garbage_ = false; + recycle_ = true; + anonymous_property_ = 0; +} + //----------------------------------------------------------------------------- /// @cond CGAL_DOCUMENT_INTERNALS template @@ -2464,7 +2580,7 @@ collect_garbage(Visitor &visitor) return; } - std::uint32_t i, i0, i1, + int i, i0, i1, nV(num_vertices()), nE(num_edges()), nH(num_halfedges()), @@ -2521,9 +2637,9 @@ collect_garbage(Visitor &visitor) if (i0 >= i1) break; // swap - eprops_.swap(SM_Edge_index{i0}, SM_Edge_index{i1}); - hprops_.swap(SM_Halfedge_index{2*i0}, SM_Halfedge_index{2*i1}); - hprops_.swap(SM_Halfedge_index{2*i0+1}, SM_Halfedge_index{2*i1+1}); + eprops_.swap(i0, i1); + hprops_.swap(2*i0, 2*i1); + hprops_.swap(2*i0+1, 2*i1+1); }; // remember new size @@ -2545,7 +2661,7 @@ collect_garbage(Visitor &visitor) if (i0 >= i1) break; // swap - fprops_.swap(SM_Face_index{i0}, SM_Face_index{i1}); + fprops_.swap(i0, i1); }; // remember new size @@ -2618,6 +2734,25 @@ collect_garbage() collect_garbage(visitor); } + +template +void +Surface_mesh

:: +set_recycle_garbage(bool b) +{ + recycle_ = b; +} + + +template +bool +Surface_mesh

:: +does_recycle_garbage() const +{ + return recycle_; +} + + namespace internal{ namespace handle { template <> diff --git a/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h b/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h index d99be1fd8d67..d044c38a5570 100644 --- a/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h +++ b/Surface_mesh/include/CGAL/boost/graph/graph_traits_Surface_mesh.h @@ -429,7 +429,7 @@ template typename boost::graph_traits >::faces_size_type num_faces(const CGAL::Surface_mesh

& sm) { - return sm.number_of_faces(); + return sm.num_faces(); } template diff --git a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h index f157e714d9a2..281c2b37093e 100644 --- a/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h +++ b/Surface_mesh/include/CGAL/boost/graph/properties_Surface_mesh.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -31,51 +31,58 @@ namespace CGAL { template -class SM_edge_weight_pmap { +class SM_edge_weight_pmap +{ typedef CGAL::Surface_mesh SM; public: - typedef boost::readable_property_map_tag category; - typedef typename CGAL::Kernel_traits::type::FT value_type; - typedef value_type reference; - typedef typename SM::Edge_index key_type; + typedef boost::readable_property_map_tag category; + typedef typename CGAL::Kernel_traits::type::FT value_type; + typedef value_type reference; + typedef typename SM::Edge_index key_type; SM_edge_weight_pmap(const CGAL::Surface_mesh& sm) - : pm_(sm.template property_map< - typename SM::Vertex_index, - typename SM::Point>("v:point").first), - sm_(sm) {} - - value_type operator[](const key_type& e) const { + : pm_(sm. template property_map< + typename SM::Vertex_index, + typename SM::Point >("v:point").first), + sm_(sm) + {} + + value_type operator[](const key_type& e) const + { return approximate_sqrt(CGAL::squared_distance(pm_[source(e, sm_)], pm_[target(e, sm_)])); } friend inline - value_type get(const SM_edge_weight_pmap& m, const key_type& k) { + value_type get(const SM_edge_weight_pmap& m, const key_type& k) + { return m[k]; } private: - typename SM::template Property_map pm_; + typename SM::template Property_map< typename SM::Vertex_index, + typename SM::Point > pm_; const SM& sm_; }; template -class SM_index_pmap { +class SM_index_pmap +{ public: typedef boost::readable_property_map_tag category; - typedef std::uint32_t value_type; - typedef std::uint32_t reference; - typedef VEF key_type; + typedef std::uint32_t value_type; + typedef std::uint32_t reference; + typedef VEF key_type; - value_type operator[](const key_type& vd) const { + value_type operator[](const key_type& vd) const + { return vd; } friend inline - value_type get(const SM_index_pmap& m, const key_type& k) { + value_type get(const SM_index_pmap& m, const key_type& k) + { return m[k]; } }; @@ -88,31 +95,31 @@ namespace boost { // template -struct property_map, boost::edge_weight_t> { +struct property_map, boost::edge_weight_t > +{ typedef CGAL::SM_edge_weight_pmap type; typedef CGAL::SM_edge_weight_pmap const_type; }; } -namespace CGAL { +namespace CGAL{ template typename boost::property_map, boost::edge_weight_t>::const_type -get(boost::edge_weight_t, const CGAL::Surface_mesh& sm) { +get(boost::edge_weight_t, const CGAL::Surface_mesh& sm) +{ return CGAL::SM_edge_weight_pmap(sm); } // forward declarations, see class SM_Vertex_index; - class SM_Edge_index; - class SM_Halfedge_index; - class SM_Face_index; template typename CGAL::Kernel_traits::type::FT get(boost::edge_weight_t, const CGAL::Surface_mesh& sm, - const SM_Edge_index& e) { + const SM_Edge_index& e) +{ return CGAL::SM_edge_weight_pmap(sm)[e]; } } @@ -120,139 +127,129 @@ get(boost::edge_weight_t, const CGAL::Surface_mesh& sm, // vertex_index // -namespace boost { +namespace boost{ template -struct property_map, boost::vertex_index_t> { +struct property_map, boost::vertex_index_t > +{ typedef CGAL::SM_index_pmap >::vertex_descriptor> type; typedef CGAL::SM_index_pmap >::vertex_descriptor> const_type; }; } -namespace CGAL { +namespace CGAL{ template CGAL::SM_index_pmap -get(const boost::vertex_index_t&, const CGAL::Surface_mesh&) { +get(const boost::vertex_index_t&, const CGAL::Surface_mesh&) +{ return CGAL::SM_index_pmap >::vertex_descriptor>(); } } // // face_index // -namespace boost { +namespace boost{ template -struct property_map, boost::face_index_t> { +struct property_map, boost::face_index_t > +{ typedef CGAL::SM_index_pmap >::face_descriptor> type; typedef CGAL::SM_index_pmap >::face_descriptor> const_type; }; } -namespace CGAL { +namespace CGAL{ template CGAL::SM_index_pmap -get(const boost::face_index_t&, const CGAL::Surface_mesh&) { +get(const boost::face_index_t&, const CGAL::Surface_mesh&) +{ return CGAL::SM_index_pmap >::face_descriptor>(); } } - -// -// Face color -// todo -namespace boost { -template -struct property_map, CGAL::internal_np::face_color_map_t> { - typedef typename CGAL::Surface_mesh::template Property_map type; - typedef typename CGAL::Surface_mesh::template Property_map const_type; -}; -} -namespace CGAL { -template -typename CGAL::Surface_mesh::template Property_map -get(const CGAL::internal_np::face_color_map_t&, const CGAL::Surface_mesh&sm) { - return sm.template property_map("f:color").first; -} -} - // // edge_index // -namespace boost { +namespace boost{ template -struct property_map, boost::edge_index_t> { +struct property_map, boost::edge_index_t > +{ typedef CGAL::SM_index_pmap >::edge_descriptor> type; typedef CGAL::SM_index_pmap >::edge_descriptor> const_type; }; } -namespace CGAL { +namespace CGAL{ template CGAL::SM_index_pmap -get(const boost::edge_index_t&, const CGAL::Surface_mesh&) { +get(const boost::edge_index_t&, const CGAL::Surface_mesh&) +{ return CGAL::SM_index_pmap >::edge_descriptor>(); } } // // halfedge_index // -namespace boost { +namespace boost{ template -struct property_map, boost::halfedge_index_t> { +struct property_map, boost::halfedge_index_t > +{ typedef CGAL::SM_index_pmap >::halfedge_descriptor> type; typedef CGAL::SM_index_pmap >::halfedge_descriptor> const_type; }; } -namespace CGAL { +namespace CGAL{ template CGAL::SM_index_pmap -get(const boost::halfedge_index_t&, const CGAL::Surface_mesh&) { +get(const boost::halfedge_index_t&, const CGAL::Surface_mesh&) +{ return CGAL::SM_index_pmap >::halfedge_descriptor>(); } } // // vertex_point // -namespace boost { -template -struct property_map, CGAL::vertex_point_t> { +namespace boost{ +template +struct property_map, CGAL::vertex_point_t > +{ typedef CGAL::Surface_mesh

SM; typedef typename - SM::template Property_map type; + SM::template Property_map< typename SM::Vertex_index, + P + > type; typedef type const_type; }; } -namespace CGAL { +namespace CGAL{ namespace internal { -template -struct Get_vertex_point_map_for_Surface_mesh_return_type { - typedef typename boost::property_map + template + struct Get_vertex_point_map_for_Surface_mesh_return_type { + typedef typename boost::property_map < CGAL::Surface_mesh, CGAL::vertex_point_t - >::const_type type; -}; + >::const_type type; + }; } // end namespace internal -template +template typename boost::lazy_disable_if - < - std::is_const, - internal::Get_vertex_point_map_for_Surface_mesh_return_type - >::type +< + std::is_const, + internal::Get_vertex_point_map_for_Surface_mesh_return_type +>::type get(CGAL::vertex_point_t, const CGAL::Surface_mesh& g) { return g.points(); } namespace internal { -template -struct Get_graph_traits_of_SM { - typedef boost::graph_traits > type; -}; + template + struct Get_graph_traits_of_SM { + typedef boost::graph_traits< CGAL::Surface_mesh > type; + }; } // end namespace internal // get for intrinsic properties @@ -264,96 +261,87 @@ struct Get_graph_traits_of_SM { { return get(get(p, sm), x); } \ CGAL_SM_INTRINSIC_PROPERTY(std::uint32_t, boost::vertex_index_t, - SM_Vertex_index) - +SM_Vertex_index) CGAL_SM_INTRINSIC_PROPERTY(std::uint32_t, boost::edge_index_t, - SM_Edge_index) - +SM_Edge_index) CGAL_SM_INTRINSIC_PROPERTY(std::uint32_t, boost::halfedge_index_t, - SM_Halfedge_index) - +SM_Halfedge_index) CGAL_SM_INTRINSIC_PROPERTY(std::uint32_t, boost::face_index_t, - SM_Face_index) - -CGAL_SM_INTRINSIC_PROPERTY(Point &, CGAL::vertex_point_t, SM_Vertex_index) +SM_Face_index) +CGAL_SM_INTRINSIC_PROPERTY(Point&, CGAL::vertex_point_t, SM_Vertex_index) #undef CGAL_SM_INTRINSIC_PROPERTY // put for intrinsic properties // only available for vertex_point -template +template void put(CGAL::vertex_point_t p, const CGAL::Surface_mesh& g, - typename boost::graph_traits >::vertex_descriptor x, + typename boost::graph_traits< CGAL::Surface_mesh >::vertex_descriptor x, const Point& point) { typedef CGAL::Surface_mesh SM; CGAL_assertion(g.is_valid(x)); - typename SM::template Property_map::vertex_descriptor, - Point> prop = get(p, g); + typename SM::template Property_map< typename boost::graph_traits::vertex_descriptor, + Point> prop = get(p, g); prop[x] = point; } -template +template struct graph_has_property, boost::vertex_index_t> - : CGAL::Tag_true { -}; -template + : CGAL::Tag_true {}; +template struct graph_has_property, boost::edge_index_t> - : CGAL::Tag_true { -}; -template + : CGAL::Tag_true {}; +template struct graph_has_property, boost::halfedge_index_t> - : CGAL::Tag_true { -}; -template + : CGAL::Tag_true {}; +template struct graph_has_property, boost::face_index_t> - : CGAL::Tag_true { -}; -template + : CGAL::Tag_true {}; +template struct graph_has_property, CGAL::vertex_point_t> - : CGAL::Tag_true { -}; -template + : CGAL::Tag_true {}; +template struct graph_has_property, boost::edge_weight_t> - : CGAL::Tag_true { -}; -template -struct graph_has_property, CGAL::internal_np::face_color_map_t> - : CGAL::Tag_true { -}; + : CGAL::Tag_true {}; } // CGAL // dynamic properties -namespace boost { +namespace boost +{ template -struct property_map, CGAL::dynamic_vertex_property_t > { +struct property_map, CGAL::dynamic_vertex_property_t > +{ typedef CGAL::Surface_mesh SM; - typedef typename SM::template Property_map SMPM; + typedef typename SM:: template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; template -struct property_map, CGAL::dynamic_face_property_t > { +struct property_map, CGAL::dynamic_face_property_t > +{ typedef CGAL::Surface_mesh SM; - typedef typename SM::template Property_map SMPM; + typedef typename SM:: template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; template -struct property_map, CGAL::dynamic_halfedge_property_t > { +struct property_map, CGAL::dynamic_halfedge_property_t > +{ typedef CGAL::Surface_mesh SM; - typedef typename SM::template Property_map SMPM; + typedef typename SM:: template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; template -struct property_map, CGAL::dynamic_edge_property_t > { +struct property_map, CGAL::dynamic_edge_property_t > +{ typedef CGAL::Surface_mesh SM; - typedef typename SM::template Property_map SMPM; + typedef typename SM:: template Property_map SMPM; typedef CGAL::internal::Dynamic type; typedef CGAL::internal::Dynamic_with_index const_type; }; @@ -365,75 +353,80 @@ namespace CGAL { // get functions for dynamic properties of mutable Surface_mesh template typename boost::property_map, dynamic_vertex_property_t >::type -get(dynamic_vertex_property_t, Surface_mesh& sm) { +get(dynamic_vertex_property_t, Surface_mesh& sm) +{ typedef typename boost::property_map, dynamic_vertex_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_vertex_property_t >::type DPM; - return DPM(sm, new SMPM( - sm.template add_property_map::Vertex_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Vertex_index, T>(std::string()).first)); } template typename boost::property_map, dynamic_face_property_t >::type -get(dynamic_face_property_t, Surface_mesh& sm) { +get(dynamic_face_property_t, Surface_mesh& sm) +{ typedef typename boost::property_map, dynamic_face_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_face_property_t >::type DPM; - return DPM(sm, - new SMPM(sm.template add_property_map::Face_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Face_index, T>(std::string()).first)); } template typename boost::property_map, dynamic_edge_property_t >::type -get(dynamic_edge_property_t, Surface_mesh& sm) { +get(dynamic_edge_property_t, Surface_mesh& sm) +{ typedef typename boost::property_map, dynamic_edge_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_edge_property_t >::type DPM; - return DPM(sm, - new SMPM(sm.template add_property_map::Edge_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Edge_index, T>(std::string()).first)); } template typename boost::property_map, dynamic_halfedge_property_t >::type -get(dynamic_halfedge_property_t, Surface_mesh& sm) { +get(dynamic_halfedge_property_t, Surface_mesh& sm) +{ typedef typename boost::property_map, dynamic_halfedge_property_t >::SMPM SMPM; typedef typename boost::property_map, dynamic_halfedge_property_t >::type DPM; - return DPM(sm, new SMPM( - sm.template add_property_map::Halfedge_index, T>(std::string()).first)); + return DPM(sm, new SMPM(sm.template add_property_map::Halfedge_index, T>(std::string()).first)); } // get functions for dynamic properties of const Surface_mesh template typename boost::property_map, dynamic_vertex_property_t >::const_type -get(dynamic_vertex_property_t, const Surface_mesh& sm) { +get(dynamic_vertex_property_t, const Surface_mesh& sm) +{ return CGAL::internal::Dynamic_with_index::Vertex_index, T>(num_vertices(sm)); } template typename boost::property_map, dynamic_face_property_t >::const_type -get(dynamic_face_property_t, const Surface_mesh& sm) { +get(dynamic_face_property_t, const Surface_mesh& sm) +{ return CGAL::internal::Dynamic_with_index::Face_index, T>(num_faces(sm)); } template typename boost::property_map, dynamic_halfedge_property_t >::const_type -get(dynamic_halfedge_property_t, const Surface_mesh& sm) { +get(dynamic_halfedge_property_t, const Surface_mesh& sm) +{ return CGAL::internal::Dynamic_with_index::Halfedge_index, T>(num_halfedges(sm)); } template typename boost::property_map, dynamic_edge_property_t >::const_type -get(dynamic_edge_property_t, const Surface_mesh& sm) { +get(dynamic_edge_property_t, const Surface_mesh& sm) +{ return CGAL::internal::Dynamic_with_index::Edge_index, T>(num_edges(sm)); } // implementation detail: required by Dynamic_property_map_deleter -template +template void -remove_property(Pmap pm, CGAL::Surface_mesh

& sm) { +remove_property(Pmap pm, CGAL::Surface_mesh

& sm) +{ return sm.remove_property_map(pm); } template struct Get_pmap_of_surface_mesh { - typedef typename boost::property_map, Property_tag>::type type; + typedef typename boost::property_map, Property_tag >::type type; }; diff --git a/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp b/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp index bf446b309597..5e932869a440 100644 --- a/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_circulator_test.cpp @@ -64,20 +64,20 @@ struct test_emptiness : public Surface_fixture assert(m.is_isolated(iv)); Sm::Vertex_around_target_range vr = m.vertices_around_target(m.halfedge(iv)); - assert(is_empty_range(boost::begin(vr), boost::end(vr))); + assert(is_empty_range(std::begin(vr), std::end(vr))); Sm::Face_around_target_range fr = m.faces_around_target(m.halfedge(iv)); - assert(is_empty_range(boost::begin(fr), boost::end(fr))); + assert(is_empty_range(std::begin(fr), std::end(fr))); Sm::Halfedge_around_target_range hr = m.halfedges_around_target(m.halfedge(iv)); - assert(is_empty_range(boost::begin(hr), boost::end(hr))); + assert(is_empty_range(std::begin(hr), std::end(hr))); // not true for everything else m.remove_vertex(iv); assert(m.is_removed(iv)); Sm::Vertex_iterator vb, ve; for(boost::tie(vb, ve) = m.vertices(); vb != ve; ++vb) { Sm::Vertex_around_target_range vr = m.vertices_around_target(m.halfedge(*vb)); - assert(!is_empty_range(boost::begin(vr), boost::end(vr))); + assert(!is_empty_range(std::begin(vr), std::end(vr))); } } }; diff --git a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp index ce25271e1898..2a925c7c325c 100644 --- a/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_open_colored_off.cpp @@ -33,22 +33,20 @@ void OpenOFF(int i) assert(in && !surface_mesh.is_empty()); - auto [fcolors, created_fcolors] = surface_mesh.add_property_map("f:color"); - auto [vcolors, created_vcolors] = surface_mesh.add_property_map("v:color"); - - // Both color maps should have already existed, because they were loaded from the file - assert(!created_fcolors); - assert(!created_vcolors); - - auto first_fcolor = fcolors[*(surface_mesh.faces().begin())]; - assert(first_fcolor == CGAL::IO::Color(229, 0, 0)); - auto last_fcolor = fcolors[*(--surface_mesh.faces().end())]; - assert(last_fcolor == CGAL::IO::Color(0, 0, 229)); - - auto first_vcolor = vcolors[*(surface_mesh.vertices().begin())]; - assert((first_vcolor == CGAL::IO::Color(229, 0, 0))); - auto last_vcolor = vcolors[*(--surface_mesh.vertices().end())]; - assert((last_vcolor == CGAL::IO::Color(0, 0, 229))); + SMesh::Property_map fcolors = + surface_mesh.property_map("f:color").first; + + SMesh::Property_map vcolors = + surface_mesh.property_map("v:color").first; + CGAL::IO::Color c = fcolors[*(surface_mesh.faces().begin())]; + assert(c== CGAL::IO::Color(229,0,0)); + c = fcolors[*(--surface_mesh.faces().end())]; + assert(c== CGAL::IO::Color(0,0,229)); + + c = vcolors[*(surface_mesh.vertices().begin())]; + assert((c== CGAL::IO::Color(229,0,0))); + c = vcolors[*(--surface_mesh.vertices().end())]; + assert((c== CGAL::IO::Color(0,0,229))); } @@ -57,6 +55,6 @@ int main() OpenOFF(1); OpenOFF(2); OpenOFF(3); - std::cout << "done" << std::endl; + std::cerr << "done" << std::endl; return 0; } diff --git a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp index 2b4c8de75229..ddcda0545077 100644 --- a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp @@ -15,7 +15,6 @@ typedef boost::graph_traits::face_descriptor face_descriptor; int main() { std::ifstream in(CGAL::data_file_path("meshes/colored_tetra.ply")); - assert(in.is_open()); SMesh mesh; CGAL::IO::read_PLY(in, mesh); @@ -38,7 +37,6 @@ int main() // Append second mesh std::ifstream in2("tetra.ply"); - assert(in2.is_open()); CGAL::IO::read_PLY(in2, mesh); std::ofstream out("out.ply"); diff --git a/Surface_mesh/test/Surface_mesh/sm_remove.cpp b/Surface_mesh/test/Surface_mesh/sm_remove.cpp index 5aadc4a132e5..26713c930e18 100644 --- a/Surface_mesh/test/Surface_mesh/sm_remove.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_remove.cpp @@ -82,14 +82,17 @@ int main() auto l_eremoved = m.property_map("e:removed").first; auto l_fremoved = m.property_map("f:removed").first; - assert( vconn == l_vconn ); - assert( hconn == l_hconn ); - assert( fconn == l_fconn ); - assert( vpoint == l_vpoint ); - assert( vprop == l_vprop ); - assert( hprop == l_hprop ); - assert( fprop == l_fprop ); - assert( eprop == l_eprop ); + assert( &vconn.array() == &l_vconn.array() ); + assert( &hconn.array() == &l_hconn.array() ); + assert( &fconn.array() == &l_fconn.array() ); + assert( &vpoint.array() == &l_vpoint.array() ); + assert( &vremoved.array() == &l_vremoved.array() ); + assert( &eremoved.array() == &l_eremoved.array() ); + assert( &fremoved.array() == &l_fremoved.array() ); + assert( &vprop.array() == &l_vprop.array() ); + assert( &hprop.array() == &l_hprop.array() ); + assert( &fprop.array() == &l_fprop.array() ); + assert( &eprop.array() == &l_eprop.array() ); } { @@ -103,10 +106,13 @@ int main() auto l_eremoved = m.property_map("e:removed").first; auto l_fremoved = m.property_map("f:removed").first; - assert( vconn == l_vconn ); - assert( hconn == l_hconn ); - assert( fconn == l_fconn ); - assert( vpoint == l_vpoint ); + assert( &vconn.array() == &l_vconn.array() ); + assert( &hconn.array() == &l_hconn.array() ); + assert( &fconn.array() == &l_fconn.array() ); + assert( &vpoint.array() == &l_vpoint.array() ); + assert( &vremoved.array() == &l_vremoved.array() ); + assert( &eremoved.array() == &l_eremoved.array() ); + assert( &fremoved.array() == &l_fremoved.array() ); } return 0; diff --git a/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp b/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp index 1496c35ace5f..16fb699982c5 100644 --- a/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp +++ b/Surface_mesh/test/Surface_mesh/surface_mesh_test.cpp @@ -219,11 +219,15 @@ void point_position_accessor () void properties () { Surface_fixture f; - auto [prop, created] = f.m.add_property_map("illuminatiproperty", 23); + + Sm::Property_map prop; + bool created = false; + + boost::tie(prop,created) = f.m.add_property_map("illuminatiproperty", 23); assert(created == true); - auto [_, created_again] = f.m.add_property_map("illuminatiproperty"); - assert(created_again == false); + boost::tie(prop, created)= f.m.add_property_map("illuminatiproperty"); + assert(created == false); } void move () { diff --git a/Weights/include/CGAL/Weights/cotangent_weights.h b/Weights/include/CGAL/Weights/cotangent_weights.h index 9b7e58a9522f..dc070c5c493e 100644 --- a/Weights/include/CGAL/Weights/cotangent_weights.h +++ b/Weights/include/CGAL/Weights/cotangent_weights.h @@ -211,7 +211,7 @@ class Cotangent_weight // by the concept SurfaceMeshDeformationWeights. // A bit awkward, but better than duplicating code... PolygonMesh const * const m_pmesh_ptr; - const std::optional m_vpm; + const VertexPointMap m_vpm; const GeomTraits m_traits; bool m_use_clamped_version; @@ -293,7 +293,7 @@ class Cotangent_weight typename GeomTraits::FT operator()(const halfedge_descriptor he) const { CGAL_precondition(m_pmesh_ptr != nullptr); - return this->operator()(he, *m_pmesh_ptr, m_vpm.value(), m_traits); + return this->operator()(he, *m_pmesh_ptr, m_vpm, m_traits); } }; @@ -319,7 +319,7 @@ class Secure_cotangent_weight_with_voronoi_area private: const PolygonMesh& m_pmesh; - const std::optional m_vpm; + const VertexPointMap m_vpm; GeomTraits m_traits; Cotangent_weight cotangent_weight_calculator; @@ -329,7 +329,7 @@ class Secure_cotangent_weight_with_voronoi_area const VertexPointMap vpm, const GeomTraits& traits = GeomTraits()) : m_pmesh(pmesh), m_vpm(vpm), m_traits(traits), - cotangent_weight_calculator(m_pmesh, m_vpm.value(), m_traits, + cotangent_weight_calculator(m_pmesh, m_vpm, m_traits, true /*clamp*/, true /*bound from below*/) { } @@ -361,9 +361,9 @@ class Secure_cotangent_weight_with_voronoi_area const vertex_descriptor v1 = source(he, m_pmesh); const vertex_descriptor v2 = target(next(he, m_pmesh), m_pmesh); - const Point_ref p0 = get(m_vpm.value(), v0); - const Point_ref p1 = get(m_vpm.value(), v1); - const Point_ref p2 = get(m_vpm.value(), v2); + const Point_ref p0 = get(m_vpm, v0); + const Point_ref p1 = get(m_vpm, v1); + const Point_ref p2 = get(m_vpm, v2); const CGAL::Angle angle0 = CGAL::angle(p1, p0, p2); if((angle0 == CGAL::OBTUSE) || From c8cc615f8bfece7b5083827f1a5559c58cba344f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 7 Nov 2023 14:53:08 +0100 Subject: [PATCH 284/520] removing trailing whitespaces --- Orthtree/doc/Orthtree/Orthtree.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 6abe414ea5f3..4851856f4956 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -5,7 +5,7 @@ namespace CGAL { \anchor Chapter_Orthtree \cgalAutoToc -\authors Jackson Campolattaro, Simon Giraudot, Cédric Portaneri, Tong Zhao, Pierre Alliez +\authors Jackson Campolattaro, Simon Giraudot, Cedric Portaneri, Tong Zhao, Pierre Alliez \section Section_Orthtree_Introduction Introduction @@ -57,7 +57,7 @@ a node needs to be split. Custom predicates can easily be defined if the existing ones do not match users' needs. The following example shows the construction of an Octree from a vector of points. -`octree.refine(10, 1)` uses the default split predicate, which +`octree.refine(10, 1)` uses the default split predicate, which enforces a max-depth and a maximum number of inliers per node ("bucket size"). Nodes are split if their depth is less than 10, and they contain more than one inlier. @@ -65,8 +65,8 @@ Nodes are split if their depth is less than 10, and they contain more than one i \subsection Section_Orthtree_Quadtree Building a Quadtree -The `Orthtree` class may be templated with `Orthtree_traits_point<>` -with a 2d dimension tag and thus behave as a %quadtree. +The `Orthtree` class may be templated with `Orthtree_traits_point<>` +with a 2d dimension tag and thus behave as a %quadtree. For convenience, the alias `Quadtree` is provided. The following example shows the construction of %quadtree from a vector of `Point_2` objects. @@ -116,12 +116,12 @@ at depth 7 can hold 14. \subsection Section_Orthtree_Orthtree_Point_Vector Building an Orthtree -An orthtree can also be used with an arbitrary number of dimensions. +An orthtree can also be used with an arbitrary number of dimensions. The `Orthtree_traits_point` template can infer the arbitrary dimension count from the d-dimensional kernel. The following example shows how to build a generalized orthtree in dimension 4. As `std::vector` is manually filled with 4-dimensional points. -The vector is used as the point set, and an `Identity_property_map` is automatically +The vector is used as the point set, and an `Identity_property_map` is automatically set as the orthtree's map type, so a map does not need to be provided. \cgalExample{Orthtree/orthtree_build.cpp} @@ -141,14 +141,14 @@ number of different solutions for traversing the tree. Because our orthtree is a form of connected acyclic undirected graph, it is possible to navigate between any two nodes. What that means in practice is that given a node on the tree, it is possible to access any other node using the right set of operations. -The `Node_index` type provides a handle on a node, and the `orthree` class provides methods +The `Node_index` type provides a handle on a node, and the `orthree` class provides methods that enable the user to retrieve the indices of each of its children as well as its parent (if they exist). Given any node index `node`, the `n`th child of that node can be found with `orthtree.child(node, n)`. For an octree, values of `n` from 0-7 provide access to the different children. For non-root nodes, it is possible to access parent nodes using the `orthtree.parent()` accessor. -To access grandchildren, it isn't necessary to use nested `orthtree.child()` calls. +To access grandchildren, it isn't necessary to use nested `orthtree.child()` calls. Instead, the `orthtree.descendant(node, a, b, ...)` convenience method is provided. This retrieves the `b`th child of the `a`th child, to any depth. @@ -165,7 +165,7 @@ The following example demonstrates the use of several of these accessors. It is often useful to be able to iterate over the nodes of the tree in a particular order. For example, the stream operator `<<` uses a traversal to print out each node. -A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) +A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal). To traverse a tree in preorder is to visit each parent immediately followed by its children, whereas in postorder, traversal the children are visited first. @@ -180,7 +180,7 @@ In this case, we print out the nodes of the tree without indentation instead. \subsection Section_Orthtree_Custom_Traversal Custom Traversal -Users can define their own traversal methods by creating models of the `OrthtreeTraversal` concept. +Users can define their own traversal methods by creating models of the `OrthtreeTraversal` concept. The custom traversal must provide a method which returns the starting point of the traversal (`first_index()`) and another method which returns the next node in the sequence (`next_index()`). `next_index()` returns an empty optional when the end of the traversal is reached. @@ -223,7 +223,7 @@ Results are put in a vector, and then printed. Not all octrees are compatible with nearest neighbor functionality, as the idea of a nearest neighbor may not make sense for some tree contents. -For the nearest neighbor functions to work, the traits class must implement the +For the nearest neighbor functions to work, the traits class must implement the `CollectionPartitioningOrthtreeTraits` concept. \subsection Section_Orthtree_Grade Grading @@ -285,7 +285,7 @@ For nontrivial point counts, the naive approach's calculation time dwarfs that o \section Section_Orthtree_History History A prototype code was implemented by Pierre Alliez and improved by Tong -Zhao and Cédric Portaneri. From this prototype code, the package was +Zhao and Cedric Portaneri. From this prototype code, the package was developed by Jackson Campolatarro as part of the Google Summer of Code 2020. Simon Giraudot, supervisor of the GSoC internship, completed and finalized the package for integration in CGAL 5.3. Pierre Alliez From 696fb8339984cc03e58406bf8e83e62ecac35761 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 7 Nov 2023 15:01:37 +0100 Subject: [PATCH 285/520] moving new property system into CGAL::Experimental namespace fixing msvc compiling issues --- Orthtree/include/CGAL/Orthtree.h | 6 +++--- Property_map/include/CGAL/Property_container.h | 4 ++-- .../Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h | 6 +++--- .../include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index cd59162d91f0..ae8fa36765f5 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -110,7 +110,7 @@ class Orthtree { using Maybe_node_index = std::optional; // todo: maybe this could be private? - using Node_property_container = Properties::Property_container; + using Node_property_container = Properties::Experimental::Property_container; /*! \brief Set of bits representing this node's relationship to its parent. @@ -205,10 +205,10 @@ class Orthtree { // Determine dimensions of the root bbox Bbox_dimensions size; for (int i = 0; i < Dimension::value; ++i) - size[i] = bbox.max()[i] - bbox.min()[i]; + size[i] = (bbox.max)()[i] - (bbox.min)()[i]; // save orthtree attributes - m_bbox_min = bbox.min(); + m_bbox_min = (bbox.min)(); m_side_per_depth.push_back(size); data(root()) = m_traits.construct_root_node_contents_object()(); } diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 83d6e81cde2d..c14e34adb4c7 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -21,7 +21,7 @@ #ifndef DOXYGEN_RUNNING -namespace CGAL::Properties { +namespace CGAL::Properties::Experimental { template class Property_array_base { @@ -494,7 +494,7 @@ class Property_container { // Determine if the group fits if (std::distance(unused_begin, m_active_indices.end()) >= n) unused_end = std::find_if( - unused_begin, std::min(unused_begin + n, m_active_indices.end()), + unused_begin, (std::min)(unused_begin + n, m_active_indices.end()), [](bool used) { return used; } ); diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h index 1bc87fb7f1e8..6f0ab44c7ac8 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC.h @@ -498,9 +498,9 @@ class Efficient_RANSAC { // Use bounding box diagonal as reference for default values auto bbox = m_global_octree->boundingBox(); FT bbox_diagonal = (FT) CGAL::sqrt( - (bbox.max()[0] - bbox.min()[0]) * (bbox.max()[0] - bbox.min()[0]) - + (bbox.max()[1] - bbox.min()[1]) * (bbox.max()[1] - bbox.min()[1]) - + (bbox.max()[2] - bbox.min()[2]) * (bbox.max()[2] - bbox.min()[2])); + (bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) + + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); // Epsilon or cluster_epsilon have been set by the user? // If not, derive from bounding box diagonal diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h index 0aa26c50521b..7971b638ad10 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h @@ -32,7 +32,7 @@ namespace CGAL { namespace Shape_detection { // Forward declaration needed for automatic traits detection without -// including the deprecated header itself… +// including the deprecated header itself template struct Shape_detection_traits; From 6cdfbfea5db8d941ed18e6b6cae2f5610bb252f1 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 7 Nov 2023 16:44:53 +0100 Subject: [PATCH 286/520] =?UTF-8?q?restored=20=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 4851856f4956..cb1ce675c894 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -5,7 +5,7 @@ namespace CGAL { \anchor Chapter_Orthtree \cgalAutoToc -\authors Jackson Campolattaro, Simon Giraudot, Cedric Portaneri, Tong Zhao, Pierre Alliez +\authors Jackson Campolattaro, Simon Giraudot, Cédric Portaneri, Tong Zhao, Pierre Alliez \section Section_Orthtree_Introduction Introduction From 8274313a74d8182bddfd2831edd212284a09fbc4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 15 Nov 2023 09:36:44 +0100 Subject: [PATCH 287/520] =?UTF-8?q?fix=20ci=20restored=20another=20=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index cb1ce675c894..cb0bbbde973a 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -285,7 +285,7 @@ For nontrivial point counts, the naive approach's calculation time dwarfs that o \section Section_Orthtree_History History A prototype code was implemented by Pierre Alliez and improved by Tong -Zhao and Cedric Portaneri. From this prototype code, the package was +Zhao and Cédric Portaneri. From this prototype code, the package was developed by Jackson Campolatarro as part of the Google Summer of Code 2020. Simon Giraudot, supervisor of the GSoC internship, completed and finalized the package for integration in CGAL 5.3. Pierre Alliez diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 55692b2ffbab..8cab162d5323 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace CGAL { From dceff13beaef494641cff4bedc182ea6ff133ce2 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 15 Nov 2023 10:46:28 +0100 Subject: [PATCH 288/520] added missing header --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 547f02846a85..0f6b11eb55a1 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -17,6 +17,7 @@ #include +#include #include #include From b2ac701711a7b162f4b1bb81c63e7d4720cf7417 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 15 Nov 2023 12:18:02 +0100 Subject: [PATCH 289/520] dependencies update --- Orthtree/package_info/Orthtree/dependencies | 12 ++++++++++++ .../package_info/Shape_detection/dependencies | 5 +++++ .../package_info/Shape_regularization/dependencies | 1 + 3 files changed, 18 insertions(+) diff --git a/Orthtree/package_info/Orthtree/dependencies b/Orthtree/package_info/Orthtree/dependencies index bcb222ae1968..de7cb56ad9db 100644 --- a/Orthtree/package_info/Orthtree/dependencies +++ b/Orthtree/package_info/Orthtree/dependencies @@ -1,15 +1,27 @@ Algebraic_foundations +BGL +Circulator Distance_2 Distance_3 +Filtered_kernel +Hash_map Installation Intersections_2 Intersections_3 Interval_support +Kernel_d Kernel_23 Modular_arithmetic Number_types Orthtree +Point_set_2 +Point_set_3 +Point_set_processing_3 Profiling_tools Property_map STL_Extension Stream_support +Spatial_sorting +Surface_mesh +TDS_2 +Triangulation_2 \ No newline at end of file diff --git a/Shape_detection/package_info/Shape_detection/dependencies b/Shape_detection/package_info/Shape_detection/dependencies index 63e6337e4fbf..7758a93501ea 100644 --- a/Shape_detection/package_info/Shape_detection/dependencies +++ b/Shape_detection/package_info/Shape_detection/dependencies @@ -6,6 +6,7 @@ Circulator Distance_2 Distance_3 Filtered_kernel +Hash_map Homogeneous_kernel Installation Intersections_2 @@ -16,6 +17,7 @@ Kernel_d Modular_arithmetic Number_types Orthtree +Point_set_2 Principal_component_analysis Principal_component_analysis_LGPL Profiling_tools @@ -25,4 +27,7 @@ STL_Extension Shape_detection Solver_interface Spatial_searching +Spatial_sorting Stream_support +TDS_2 +Triangulation_2 \ No newline at end of file diff --git a/Shape_regularization/package_info/Shape_regularization/dependencies b/Shape_regularization/package_info/Shape_regularization/dependencies index 5cf917f32ed4..f71e038ce1f2 100644 --- a/Shape_regularization/package_info/Shape_regularization/dependencies +++ b/Shape_regularization/package_info/Shape_regularization/dependencies @@ -14,6 +14,7 @@ Kernel_d Modular_arithmetic Number_types Orthtree +Point_set_2 Principal_component_analysis_LGPL Profiling_tools Property_map From 04264b5e89cc0fc8583f93d709b72dbb10683d99 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 16 Nov 2023 07:16:16 +0000 Subject: [PATCH 290/520] Add non const iterator in the doc --- .../Polygon/Concepts/MultipolygonWithHoles_2.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h b/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h index 917e6f91052e..0992dda022cd 100644 --- a/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h +++ b/Polygon/doc/Polygon/Concepts/MultipolygonWithHoles_2.h @@ -22,6 +22,11 @@ typedef unspecified_type Polygon_with_holes_2; /*! a bidirectional iterator over the polygons with holes. * Its value type is `Polygon_with_holes_2`. */ +typedef unspecified_type Polygon_with_holes_iterator; + +/*! a bidirectional const iterator over the polygons with holes. + * Its value type is `Polygon_with_holes_2`. + */ typedef unspecified_type Polygon_with_holes_const_iterator; //! range type for iterating over polygons with holes. @@ -54,6 +59,14 @@ Size number_of_polygons_wih_holes(); /// \name Access Functions /// @{ +/*! returns the begin iterator of the polygons with holes. + */ +Polygon_with_holes_iterator polygons_with_holes_begin(); + +/*! returns the past-the-end iterator of the polygons with holes. + */ + Polygon_with_holes_iterator polygons_with_holes_end(); + /*! returns the begin iterator of the polygons with holes. */ Polygon_with_holes_const_iterator polygons_with_holes_begin() const; @@ -77,7 +90,7 @@ void add_polygon_with_holes(const Polygon_with_holes_2& polygon); /*! erases the specified polygon. */ -void erase_polygon_with_holes(Polygon_iterator pit); +void erase_polygon_with_holes(Polygon_with_holes_const_iterator pit); /*! removes all the polygons with holes. */ From de06eb02ffeac158182c551d8db4a1ffdc3f446c Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 2 Jan 2024 20:57:51 +0100 Subject: [PATCH 291/520] fixing orthtree bbox coordinates - also fixes efficient ransac tests --- Orthtree/include/CGAL/Orthtree.h | 37 +++++++++++-------- .../CGAL/Orthtree_traits_base_for_dimension.h | 3 ++ .../include/CGAL/Orthtree_traits_d_base.h | 1 + 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ae8fa36765f5..fea8a74713c3 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -75,6 +77,7 @@ class Orthtree { /// \name Traits Types /// @{ using Dimension = typename Traits::Dimension; ///< Dimension of the tree + using Kernel = typename Traits::Kernel; using FT = typename Traits::FT; ///< Number type. using Point = typename Traits::Point_d; ///< Point type. using Bbox = typename Traits::Bbox_d; ///< Bounding box type. @@ -163,9 +166,8 @@ class Orthtree { Node_property_container::Array & m_node_parents; Node_property_container::Array & m_node_children; - Point m_bbox_min; /* input bounding box min value */ - - using Bbox_dimensions = std::array; + using Bbox_dimensions = std::array; + Bbox_dimensions m_bbox_min; std::vector m_side_per_depth; /* side length per node's depth */ Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ @@ -203,12 +205,14 @@ class Orthtree { auto bbox = m_traits.construct_root_node_bbox_object()(); // Determine dimensions of the root bbox + Bbox_dimensions size; - for (int i = 0; i < Dimension::value; ++i) - size[i] = (bbox.max)()[i] - (bbox.min)()[i]; + for (int i = 0; i < Dimension::value; ++i) { + m_bbox_min[i] = (bbox.min)()[i]; + size[i] = CGAL::Exact_predicates_exact_constructions_kernel::FT((bbox.max)()[i]) - m_bbox_min[i]; + } // save orthtree attributes - m_bbox_min = (bbox.min)(); m_side_per_depth.push_back(size); data(root()) = m_traits.construct_root_node_contents_object()(); } @@ -469,9 +473,10 @@ class Orthtree { using Cartesian_coordinate = std::array; Cartesian_coordinate min_corner, max_corner; Bbox_dimensions size = m_side_per_depth[depth(n)]; + CGAL::Cartesian_converter conv; for (int i = 0; i < Dimension::value; i++) { - min_corner[i] = m_bbox_min[i] + (global_coordinates(n)[i] * size[i]); - max_corner[i] = min_corner[i] + size[i]; + min_corner[i] = conv(m_bbox_min[i] + int(global_coordinates(n)[i]) * size[i]); + max_corner[i] = conv(m_bbox_min[i] + int(global_coordinates(n)[i] + 1) * size[i]); } return {std::apply(m_traits.construct_point_d_object(), min_corner), std::apply(m_traits.construct_point_d_object(), max_corner)}; @@ -936,7 +941,7 @@ class Orthtree { Bbox_dimensions size = m_side_per_depth.back(); Bbox_dimensions child_size; for (int i = 0; i < Dimension::value; ++i) - child_size[i] = size[i] / FT(2); + child_size[i] = size[i] * 0.5; m_side_per_depth.push_back(child_size); } @@ -961,14 +966,16 @@ class Orthtree { // Determine the location this node should be split Bbox_dimensions bary; - std::size_t i = 0; - for (const FT& f: cartesian_range(m_bbox_min)) { - bary[i] = FT(global_coordinates(n)[i]) * size[i] + size[i] / FT(2) + f; - ++i; - } + + for (std::size_t i = 0; i < Dimension::value; i++) + bary[i] = FT(global_coordinates(n)[i]) * size[i] + size[i] / FT(2) + m_bbox_min[i]; // Convert that location into a point - return std::apply(m_traits.construct_point_d_object(), bary); + CGAL::Cartesian_converter conv; + std::array tmp; + for (std::size_t i = 0; i < Dimension::value; i++) + tmp[i] = conv(bary[i]); + return std::apply(m_traits.construct_point_d_object(), tmp); } /*! diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 8cab162d5323..ad55e7ba59d6 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -29,6 +29,7 @@ template struct Orthtree_traits_base_for_dimension { /// \name Types /// @{ + using Kernel = K; using Dimension = DimensionTag; using FT = typename K::FT; using Point_d = typename K::Point_d; @@ -63,6 +64,7 @@ template struct Orthtree_traits_base_for_dimension> { /// \name Types /// @{ + using Kernel = K; using Dimension = Dimension_tag<2>; using FT = typename K::FT; using Point_d = typename K::Point_2; @@ -120,6 +122,7 @@ template struct Orthtree_traits_base_for_dimension> { /// \name Types /// @{ + using Kernel = K; using Dimension = Dimension_tag<3>; using FT = typename K::FT; using Point_d = typename K::Point_3; diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h index 53436e01be43..675fbb02aec9 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_d_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_d_base.h @@ -39,6 +39,7 @@ struct Orthtree_traits_d_base { /// \name Types /// @{ + using Kernel = K; using Dimension = DimensionTag; using FT = typename K::FT; using Point_d = typename K::Point_d; From 43bf7ed6d24894e69d82843a33ac77772b30354c Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 4 Jan 2024 17:32:52 +0000 Subject: [PATCH 292/520] no concepts in this package --- Polygon_repair/doc/Polygon_repair/PackageDescription.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index e622d0f23b25..74c33e961e1e 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -2,9 +2,6 @@ /// \defgroup PkgPolygonRepairRef 2D Polygon Repair Reference -// \defgroup PkgPolygonRepairConcepts Concepts -// \ingroup PkgPolygonRepairRef - /// \defgroup PkgPolygonRepairFunctions Functions /// \ingroup PkgPolygonRepairRef From ef6765474a711986d726afaa10801ac542a478c7 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 4 Jan 2024 18:00:10 +0000 Subject: [PATCH 293/520] Ken did more than prototyping --- Polygon_repair/doc/Polygon_repair/Polygon_repair.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index e2ded1de8745..7f3b427b48bf 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -180,7 +180,7 @@ and reconstruct the multipolygons. It also incorporates improvements later added to prepair, such as the application of the odd-even counting heuristic to edges, which enables correct counting even on partially overlapping edges. -Ken Arroyo Ohori made a prototype version of this package for the Google Summer of +Ken Arroyo Ohori developed this package during the Google Summer of Code 2023 under the mentorship of Sébastien Loriot and Andreas Fabri. */ From d1f19a7055c1674daf146e1d6eb33dd313c220ed Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 4 Jan 2024 18:13:34 +0000 Subject: [PATCH 294/520] Add to CHANGES.md --- Installation/CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 8ee04d38577d..aeb04c624779 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -23,6 +23,11 @@ Release date: October 2023 #### Envelopes of Surfaces in 3D - ** Breaking change**: Construct_projected_boundary_2 in `EnvelopeTraits_3` is now using `std::variant` instead of `Object` +### [Polygon Repair](https://doc.cgal.org/6.0/Manual/packages.html#PkgPolygonRepair) (new package) + +- This package provides functions to repair polygons, polygons with holes, and multipolygons with holes + using the odd-even heuristic. + ### [Combinatorial Maps](https://doc.cgal.org/6.0/Manual/packages.html#PkgCombinatorialMaps) and [Generalized Maps](https://doc.cgal.org/6.0/Manual/packages.html#PkgGeneralizedMaps) - Added the function `insert_cell_1_between_two_cells_2()` to the `GenericMap` concept, which enables users to insert an edge between two different faces in order to create faces with holes. From be89ad28bd03244a472cd0b43246600d19888d13 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 5 Jan 2024 10:02:11 +0000 Subject: [PATCH 295/520] Qt5 -> Qt6 --- Polygon_repair/test/Polygon_repair/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/CMakeLists.txt b/Polygon_repair/test/Polygon_repair/CMakeLists.txt index cc6b07baefe6..6075ee3222ba 100644 --- a/Polygon_repair/test/Polygon_repair/CMakeLists.txt +++ b/Polygon_repair/test/Polygon_repair/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.1...3.23) project(Polygon_repair_Tests) -find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt5) +find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6) # create a target per cppfile file( @@ -15,7 +15,7 @@ foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") endforeach() -if(CGAL_Qt5_FOUND) +if(CGAL_Qt6_FOUND) target_link_libraries(draw_test_polygons PUBLIC CGAL::CGAL_Basic_viewer) target_link_libraries(exact_test PUBLIC CGAL::CGAL_Basic_viewer) -endif() \ No newline at end of file +endif() From 6edfffe313a3517c8187d0b5ea470be00e4364da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 5 Jan 2024 11:20:13 +0100 Subject: [PATCH 296/520] update doc to qt6 --- Polygon/include/CGAL/draw_multipolygon_with_holes_2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h index d9e311b6af25..909b4b29da82 100644 --- a/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/draw_multipolygon_with_holes_2.h @@ -29,9 +29,9 @@ namespace CGAL { * opens a new window and draws `aph`, an instance of the * `CGAL::Multipolygon_with_holes_2` class. A call to this function is blocking, that * is the program continues as soon as the user closes the window. This function - * requires `CGAL_Qt5`, and is only available if the macro + * requires `CGAL_Qt6`, and is only available if the macro * `CGAL_USE_BASIC_VIEWER` is defined. Linking with the cmake target - * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt5` and add the definition + * `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt6` and add the definition * `CGAL_USE_BASIC_VIEWER`. * \tparam PH an instance of the `CGAL::Multipolygon_with_holes_2` class. * \param aph the multipolygon with holes to draw. From d5a764c41c169569a63e1d5524b610dff3e69d41 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 8 Jan 2024 13:13:21 +0100 Subject: [PATCH 297/520] fixed test removed warning --- Property_map/include/CGAL/Property_container.h | 7 ++++--- Property_map/test/Property_map/test_Property_container.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index c14e34adb4c7..bbc52260b245 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -15,8 +15,9 @@ #include #include +#include +#include -// todo: maybe this could be avoided #include #ifndef DOXYGEN_RUNNING @@ -492,14 +493,14 @@ class Property_container { auto unused_end = unused_begin; // Determine if the group fits - if (std::distance(unused_begin, m_active_indices.end()) >= n) + if (std::distance(unused_begin, m_active_indices.end()) >= static_cast::iterator>::difference_type>(n)) unused_end = std::find_if( unused_begin, (std::min)(unused_begin + n, m_active_indices.end()), [](bool used) { return used; } ); // If the discovered range was large enough - if (std::distance(unused_begin, unused_end) >= n) { + if (std::distance(unused_begin, unused_end) >= static_cast::iterator>::difference_type>(n)) { // Mark the indices as used, and reset the properties of each of them // todo: it would be better to provide a function to set a range diff --git a/Property_map/test/Property_map/test_Property_container.cpp b/Property_map/test/Property_map/test_Property_container.cpp index 4a5a2bc42738..45698bae5848 100644 --- a/Property_map/test/Property_map/test_Property_container.cpp +++ b/Property_map/test/Property_map/test_Property_container.cpp @@ -1,7 +1,7 @@ #include -using namespace CGAL::Properties; +using namespace CGAL::Properties::Experimental; void test_property_creation() { From d2754d3bfe6313111f9f491ebc2345af1f56f39f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 8 Jan 2024 14:15:17 +0100 Subject: [PATCH 298/520] update dependencies --- Orthtree/package_info/Orthtree/dependencies | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Orthtree/package_info/Orthtree/dependencies b/Orthtree/package_info/Orthtree/dependencies index de7cb56ad9db..35131847da36 100644 --- a/Orthtree/package_info/Orthtree/dependencies +++ b/Orthtree/package_info/Orthtree/dependencies @@ -1,10 +1,13 @@ Algebraic_foundations +Arithmetic_kernel BGL +Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel Hash_map +Homogeneous_kernel Installation Intersections_2 Intersections_3 From 24a937e185e38ee3f68dab6481bfcb22d99729e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 8 Jan 2024 15:43:00 +0100 Subject: [PATCH 299/520] add missing deps needed by transitivity --- .../package_info/Shape_regularization/dependencies | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Shape_regularization/package_info/Shape_regularization/dependencies b/Shape_regularization/package_info/Shape_regularization/dependencies index f71e038ce1f2..fed80db41d63 100644 --- a/Shape_regularization/package_info/Shape_regularization/dependencies +++ b/Shape_regularization/package_info/Shape_regularization/dependencies @@ -1,10 +1,13 @@ Algebraic_foundations +Arithmetic_kernel BGL +Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel Hash_map +Homogeneous_kernel Installation Intersections_2 Intersections_3 From 626a23a49aa51b545c1d7ad729771b7f8d835598 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 10 Jan 2024 16:34:55 +0100 Subject: [PATCH 300/520] fixed exact corners, multi dimension example removed warnings --- Orthtree/examples/Orthtree/orthtree_build.cpp | 5 +++-- Orthtree/include/CGAL/Orthtree.h | 6 ++++-- Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 114e709ad12b..54d629a5916e 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -12,14 +12,15 @@ using Kernel = CGAL::Epick_d; using Point_d = Kernel::Point_d; using Point_vector = std::vector; using Traits = CGAL::Orthtree_traits_point; -using Orthtree = CGAL::Orthtree; +using Traits2 = CGAL::Orthtree_traits_point::value_type>, CGAL::Dimension_tag<4>>; +using Orthtree = CGAL::Orthtree; int main() { CGAL::Random r; Point_vector points_dd; - for (std::size_t i = 0; i < 5; ++ i) + for (std::size_t i = 0; i < 500; ++ i) { std::array init{}; for (double& v : init) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index fea8a74713c3..43d9c78c1770 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -167,6 +168,7 @@ class Orthtree { Node_property_container::Array & m_node_children; using Bbox_dimensions = std::array; + CGAL::NT_converter conv; Bbox_dimensions m_bbox_min; std::vector m_side_per_depth; /* side length per node's depth */ @@ -473,7 +475,7 @@ class Orthtree { using Cartesian_coordinate = std::array; Cartesian_coordinate min_corner, max_corner; Bbox_dimensions size = m_side_per_depth[depth(n)]; - CGAL::Cartesian_converter conv; + for (int i = 0; i < Dimension::value; i++) { min_corner[i] = conv(m_bbox_min[i] + int(global_coordinates(n)[i]) * size[i]); max_corner[i] = conv(m_bbox_min[i] + int(global_coordinates(n)[i] + 1) * size[i]); @@ -971,7 +973,7 @@ class Orthtree { bary[i] = FT(global_coordinates(n)[i]) * size[i] + size[i] / FT(2) + m_bbox_min[i]; // Convert that location into a point - CGAL::Cartesian_converter conv; + std::array tmp; for (std::size_t i = 0; i < Dimension::value; i++) tmp[i] = conv(bary[i]); diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index ad55e7ba59d6..8729fa9c98ab 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -54,7 +54,7 @@ struct Orthtree_traits_base_for_dimension { auto construct_point_d_object() const { return [](auto... Args) -> Point_d { std::initializer_list args_list{Args...}; - return Point_d{args_list.size(), args_list.begin(), args_list.end()}; + return Point_d{static_cast(args_list.size()), args_list.begin(), args_list.end()}; }; } /// @} From 85e598772d092bc3d1b536dff45a0ed129e02ae4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 12 Jan 2024 11:13:17 +0100 Subject: [PATCH 301/520] pass on doc --- Orthtree/doc/Orthtree/PackageDescription.txt | 6 ++ Orthtree/include/CGAL/Orthtree.h | 66 ++++++++++++------- .../include/CGAL/Orthtree/Nearest_neighbors.h | 9 ++- .../CGAL/Orthtree/Traversal_iterator.h | 5 +- 4 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 818358a29c88..a81cb6f42e3d 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -15,6 +15,10 @@ /// \defgroup PkgOrthtreeConcepts Concepts /// \ingroup PkgOrthtreeRef +/// \defgroup PkgOrthtreeNeighbors Neighbor Search Functions +/// \ingroup PkgOrthtreeRef + + /*! \addtogroup PkgOrthtreeRef @@ -62,4 +66,6 @@ - `CGAL::Orthtrees::Leaves_traversal` - `CGAL::Orthtrees::Level_traversal` +\cgalCRPSection{Neighbor search} +- \link PkgOrthtreeNeighbors `CGAL::Orthtrees::nearest_neighbors` \endlink */ diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 43d9c78c1770..3b620fb0138f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -63,22 +63,22 @@ namespace CGAL { \sa `CGAL::Quadtree` \sa `CGAL::Octree` - \tparam Traits_ must be a model of `OrthtreeTraits` + \tparam GeomTraits must be a model of `OrthtreeTraits` */ -template +template class Orthtree { public: /// \name Template Types /// @{ - using Traits = Traits_; ///< Geometry traits + using Traits = GeomTraits; ///< Geometry traits /// @} /// \name Traits Types /// @{ using Dimension = typename Traits::Dimension; ///< Dimension of the tree - using Kernel = typename Traits::Kernel; + using Kernel = typename Traits::Kernel; ///< Kernel type. using FT = typename Traits::FT; ///< Number type. using Point = typename Traits::Point_d; ///< Point type. using Bbox = typename Traits::Bbox_d; ///< Bounding box type. @@ -113,9 +113,6 @@ class Orthtree { */ using Maybe_node_index = std::optional; - // todo: maybe this could be private? - using Node_property_container = Properties::Experimental::Property_container; - /*! \brief Set of bits representing this node's relationship to its parent. @@ -150,22 +147,44 @@ class Orthtree { using Node_index_range = boost::iterator_range>; #endif + /*! + * \brief A model of `ConstRange` whose value type is `Node_index`. + */ +#ifdef DOXYGEN_RUNNING + using Node_range = unspecified_type; + using Node_index_range = unspecified_type; +#else + using Node_index_range = boost::iterator_range>; +#endif + /*! + * \brief A Model of `LvaluePropertyMap` with `Node_index` as key type and `T` as value type. + */ +#ifdef DOXYGEN_RUNNING + template + using Property_map = unspecified_type; +#else + template + using Property_map = Properties::Experimental::Property_container::Array; +#endif + /// \cond SKIP_IN_MANUAL - using Cartesian_ranges = Orthtrees::internal::Cartesian_ranges; /// \endcond /// @} private: // data members : + using Cartesian_ranges = Orthtrees::internal::Cartesian_ranges; + using Node_property_container = Properties::Experimental::Property_container; + Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - Node_property_container::Array & m_node_contents; - Node_property_container::Array & m_node_depths; - Node_property_container::Array & m_node_coordinates; - Node_property_container::Array & m_node_parents; - Node_property_container::Array & m_node_children; + Property_map& m_node_contents; + Property_map& m_node_depths; + Property_map& m_node_coordinates; + Property_map& m_node_parents; + Property_map& m_node_children; using Bbox_dimensions = std::array; CGAL::NT_converter conv; @@ -435,7 +454,7 @@ class Orthtree { Node_index first = traversal.first_index(); - auto next = [=](const Self& tree, Node_index index) -> Maybe_node_index { + auto next = [=](const Self&, Node_index index) -> Maybe_node_index { return traversal.next_index(index); }; @@ -497,11 +516,10 @@ class Orthtree { \param name the name of the new property \param default_value the default value assigned to nodes for this property - \return pair of a reference to the new node property array - and a boolean which is True if the property needed to be created + \return pair of the property map and a boolean which is True if the property needed to be created */ template - std::pair>, bool> + std::pair, bool> get_or_add_node_property(const std::string& name, const T default_value = T()) { return m_node_properties.get_or_add_property(name, default_value); } @@ -514,10 +532,10 @@ class Orthtree { \param name the name of the new property \param default_value the default value assigned to nodes for this property - \return a reference to the new property array + \return the created property map */ template - Node_property_container::Array & add_node_property(const std::string& name, const T default_value = T()) { + Property_map add_node_property(const std::string& name, const T default_value = T()) { return m_node_properties.add_property(name, default_value); } @@ -530,10 +548,10 @@ class Orthtree { \param name the name of the property - \return a reference to the property array + \return the property map */ template - Node_property_container::Array & get_node_property(const std::string& name) { + Property_map get_node_property(const std::string& name) { return m_node_properties.get_property(name); } @@ -544,10 +562,10 @@ class Orthtree { \param name the name of the property - \return an optional containing a reference to the property array if it exists + \return an optional containing the property map if it exists */ template - std::optional>> + std::optional> get_node_property_if_exists(const std::string& name) { return m_node_properties.get_property_if_exists(name); } @@ -568,7 +586,7 @@ class Orthtree { \return the index of the node which contains the point. */ - const Node_index locate(const Point& point) const { + Node_index locate(const Point& point) const { // Make sure the point is enclosed by the orthtree CGAL_precondition (CGAL::do_intersect(point, bbox(root()))); diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 270ac53179c9..7fcdbf36696a 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -9,8 +9,8 @@ // // Author(s) : Jackson Campolattaro -#ifndef ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H -#define ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H +#ifndef ORTHTREE_NEAREST_NEIGHBORS_H +#define ORTHTREE_NEAREST_NEIGHBORS_H #include @@ -116,6 +116,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, namespace Orthtrees { /*! + \ingroup PkgOrthtreeNeighbors \brief finds the `k` points within a specific radius that are nearest to the center of `query_sphere`. @@ -166,6 +167,7 @@ OutputIterator nearest_k_neighbors_in_radius( /*! + \ingroup PkgOrthtreeNeighbors \brief finds the `k` nearest neighbors of `query`. Nearest neighbors are outputted in order of increasing distance to @@ -188,6 +190,7 @@ OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Poin } /*! + \ingroup PkgOrthtreeNeighbors \brief finds the points in `sphere`. Nearest neighbors are outputted in order of increasing distance to @@ -209,4 +212,4 @@ OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Sphe } } -#endif //ORTHTREE_EXAMPLES_NEAREST_NEIGHBORS_H +#endif //ORTHTREE_NEAREST_NEIGHBORS_H diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index 75c7abb990a7..f202406fb1f5 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -101,10 +101,9 @@ class Index_traversal_iterator : public boost::iterator_facade< private: - Traversal_function m_next; - - std::optional m_index; const Tree* m_tree = nullptr; + std::optional m_index; + Traversal_function m_next; }; From eb845938b9a942e15b8f7cd096f28c9db3389933 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 12 Jan 2024 12:50:32 +0100 Subject: [PATCH 302/520] switching to Property_array_handle extending tests to include properties more doc --- Orthtree/include/CGAL/Orthtree.h | 67 ++++++++++--------- .../test_octree_custom_properties.cpp | 17 +++++ 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 3b620fb0138f..2af93b7fde5d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -164,7 +164,7 @@ class Orthtree { using Property_map = unspecified_type; #else template - using Property_map = Properties::Experimental::Property_container::Array; + using Property_map = Properties::Experimental::Property_array_handle; #endif /// \cond SKIP_IN_MANUAL @@ -176,15 +176,17 @@ class Orthtree { using Cartesian_ranges = Orthtrees::internal::Cartesian_ranges; using Node_property_container = Properties::Experimental::Property_container; + template + using Property_array = Node_property_container::Array; Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - Property_map& m_node_contents; - Property_map& m_node_depths; - Property_map& m_node_coordinates; - Property_map& m_node_parents; - Property_map& m_node_children; + Property_array& m_node_contents; + Property_array& m_node_depths; + Property_array& m_node_coordinates; + Property_array& m_node_parents; + Property_array& m_node_children; using Bbox_dimensions = std::array; CGAL::NT_converter conv; @@ -327,7 +329,7 @@ class Orthtree { } /*! - \brief Convenience overload that refines an orthtree using a + \brief convenience overload that refines an orthtree using a maximum depth and maximum number of inliers in a node as split predicate. @@ -417,7 +419,7 @@ class Orthtree { /// @{ /*! - * \brief Provides direct read-only access to the tree Traits. + * \brief provides direct read-only access to the tree Traits. * * @return a const reference to the Traits instantiation. */ @@ -464,7 +466,7 @@ class Orthtree { /*! - \brief Convenience method for using a traversal without constructing it yourself + \brief convenience method for using a traversal without constructing it yourself \tparam Traversal model of `OrthtreeTraversal` that provides functions compatible with the type of the orthtree @@ -509,7 +511,7 @@ class Orthtree { /// @{ /*! - \brief Gets a property for nodes, adding it if it doesn't already exist. + \brief gets a property for nodes, adding it if it doesn't already exist. \tparam T the type of the property to add @@ -521,11 +523,12 @@ class Orthtree { template std::pair, bool> get_or_add_node_property(const std::string& name, const T default_value = T()) { - return m_node_properties.get_or_add_property(name, default_value); + auto p = m_node_properties.get_or_add_property(name, default_value); + return std::pair, bool>(Property_map(p.first), p.second); } /*! - \brief Adds a property for nodes. + \brief adds a property for nodes. \tparam T the type of the property to add @@ -540,7 +543,7 @@ class Orthtree { } /*! - \brief Gets a property of the tree nodes. + \brief gets a property of the tree nodes. The property to be retrieved must exist in the tree. @@ -556,7 +559,7 @@ class Orthtree { } /*! - \brief Gets a property of the tree nodes if it exists. + \brief gets a property of the tree nodes if it exists. \tparam T the type of the property to retrieve @@ -567,7 +570,11 @@ class Orthtree { template std::optional> get_node_property_if_exists(const std::string& name) { - return m_node_properties.get_property_if_exists(name); + auto p = m_node_properties.get_property_if_exists(name); + if (p) + return std::optional >(Property_map(*p)); + else + return std::nullopt; } /// @} @@ -681,7 +688,7 @@ class Orthtree { /// @{ /*! - \brief Determines whether the node specified by index `n` is a leaf node. + \brief determines whether the node specified by index `n` is a leaf node. \param n index of the node to check. @@ -692,7 +699,7 @@ class Orthtree { } /*! - \brief Determines whether the node specified by index `n` is a root node. + \brief determines whether the node specified by index `n` is a root node. \param n index of the node to check. @@ -703,7 +710,7 @@ class Orthtree { } /*! - \brief Determines the depth of the node specified. + \brief determines the depth of the node specified. The root node has depth 0, its children have depth 1, and so on. @@ -716,7 +723,7 @@ class Orthtree { } /*! - \brief Retrieves a reference to the Node_data associated with the node specified by `n`. + \brief retrieves a reference to the Node_data associated with the node specified by `n`. \param n index of the node to retrieve the contents of. @@ -738,7 +745,7 @@ class Orthtree { } /*! - \brief Retrieves the global coordinates of the node. + \brief retrieves the global coordinates of the node. \param n index of the node to retrieve the coordinates of. @@ -749,7 +756,7 @@ class Orthtree { } /*! - \brief Retrieves the local coordinates of the node. + \brief retrieves the local coordinates of the node. \param n index of the node to retrieve the coordinates of. @@ -792,7 +799,7 @@ class Orthtree { } /*! - \brief Retrieves an arbitrary descendant of the node specified by `node`. + \brief retrieves an arbitrary descendant of the node specified by `node`. Convenience function to avoid the need to call `orthtree.child(orthtree.child(node, 0), 1)`. @@ -811,7 +818,7 @@ class Orthtree { } /*! - \brief Convenience function for retrieving arbitrary nodes. + \brief convenience function for retrieving arbitrary nodes. Equivalent to `tree.descendant(tree.root(), indices...)`. @@ -825,7 +832,7 @@ class Orthtree { } /*! - \brief Finds the next sibling in the parent of the node specified by the index `n`. + \brief finds the next sibling in the parent of the node specified by the index `n`. Traverses the tree in increasing order of local index (e.g. 000, 001, 010, etc.) @@ -851,7 +858,7 @@ class Orthtree { } /*! - \brief Finds the next sibling of the parent of the node specified by `n` if it exists. + \brief finds the next sibling of the parent of the node specified by `n` if it exists. \param n the index node to find the sibling up of. @@ -875,7 +882,7 @@ class Orthtree { } /*! - \brief Finds the leaf node reached when descending the tree and always choosing child 0. + \brief finds the leaf node reached when descending the tree and always choosing child 0. This is the starting point of a depth-first traversal. @@ -893,7 +900,7 @@ class Orthtree { } /*! - \brief Finds node reached when descending the tree to a depth `d` and always choosing child 0. + \brief finds node reached when descending the tree to a depth `d` and always choosing child 0. Similar to `deepest_first_child`, but does not go to a fixed depth. @@ -973,7 +980,7 @@ class Orthtree { } /*! - * \brief Finds the center point of a node. + * \brief finds the center point of a node. * * @param n index of the node to find the center point for * @@ -999,7 +1006,7 @@ class Orthtree { } /*! - \brief Determines whether a pair of subtrees have the same topology. + \brief determines whether a pair of subtrees have the same topology. \param lhsNode index of a node in lhsTree \param lhsTree an Orthtree @@ -1030,7 +1037,7 @@ class Orthtree { } /*! - \brief Helper function for calling `is_topology_equal` on the root nodes of two trees. + \brief helper function for calling `is_topology_equal` on the root nodes of two trees. \param lhs an Orthtree \param rhs another Orthtree diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index f7dc21198bbb..97c3ee97b57b 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -25,6 +25,23 @@ int main(void) { Octree tree({points, points.point_map()}); + // Testing built in node properties + typename Octree::Property_map data_prop = tree.get_node_property("contents"); + + auto prop2 = tree.get_or_add_node_property("test", int(0)); + assert(prop2.second); + + auto prop3 = tree.get_node_property_if_exists("test"); + assert(prop3.has_value()); + + auto prop4 = tree.get_node_property_if_exists("test"); + assert(!prop4.has_value()); + + auto prop5 = tree.get_or_add_node_property("test", int(0)); + assert(!prop5.second); + + auto prop6 = tree.add_node_property("test2", std::string()); + // Default value should be respected auto &node_int_property = tree.add_node_property("int", 5); assert(node_int_property[tree.root()] == 5); From 64b232e9e42bb7f31a5fc1979513c74cace6d5ea Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 12 Jan 2024 16:42:10 +0100 Subject: [PATCH 303/520] deleted unused traits base classes --- .../include/CGAL/Orthtree_traits_2_base.h | 103 --------------- .../include/CGAL/Orthtree_traits_3_base.h | 122 ------------------ .../include/CGAL/Orthtree_traits_d_base.h | 81 ------------ 3 files changed, 306 deletions(-) delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_2_base.h delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_3_base.h delete mode 100644 Orthtree/include/CGAL/Orthtree_traits_d_base.h diff --git a/Orthtree/include/CGAL/Orthtree_traits_2_base.h b/Orthtree/include/CGAL/Orthtree_traits_2_base.h deleted file mode 100644 index bed03a753082..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_2_base.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro - -#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H -#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H - -#include - -#include -#include -#include -#include - -namespace CGAL { - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_2_base` can be subclassed for easier implementation of a 2d OrthtreeTraits concept. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels{OrthtreeTraits} - \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_point_2` -*/ -template -struct Orthtree_traits_2_base { -public: - - /// \name Types - /// @{ - - using Dimension = Dimension_tag<2>; - using FT = typename K::FT; - using Point_d = typename K::Point_2; - using Bbox_d = typename K::Iso_rectangle_2; - using Sphere_d = typename K::Circle_2; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 2-bit integers. - * - * The first bit indicates the axis (0 = x, 1 = y), - * the second bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | - * | y+ - * | * - * 0 *------+------* 1 | - * | | - * | +-----* x+ - * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 00 | 0 | LEFT | - * | `+x` | 01 | 1 | RIGHT | - * | `-y` | 10 | 2 | DOWN | - * | `+y` | 11 | 3 | UP | - */ - enum Adjacency - { - LEFT, - RIGHT, - DOWN, - UP - }; - - /// @} - - /// \name Operations - /// @{ - - auto construct_point_d_object() const { - return [](const FT& x, const FT& y) -> Point_d { - return {x, y}; - }; - } - - /// @} - -}; - -} - -#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_3_base.h b/Orthtree/include/CGAL/Orthtree_traits_3_base.h deleted file mode 100644 index 0b2ab7ddfaa3..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_3_base.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro - -#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_3_BASE_H -#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_3_BASE_H - -#include - -#include -#include -#include -#include - -namespace CGAL { - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_3_base` can be subclassed for easier implementation of a 3d OrthtreeTraits concept. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels{OrthtreeTraits} - \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_point_3` -*/ -template -struct Orthtree_traits_3_base { -public: - - /// \name Types - /// @{ - - using GeomTraits = K; - using Dimension = Dimension_tag<3>; - using FT = typename K::FT; - using Point_d = typename K::Point_3; - using Bbox_d = typename K::Iso_cuboid_3; - using Sphere_d = typename K::Sphere_3; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; - - /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 3-bit integers, - * though the numbers 6 and 7 are not used because there are only 6 different directions. - * - * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), - * the third bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 3 * - * | * 5 - * | / y+ - * |/ * z+ - * 0 *------+------* 1 | * - * /| |/ - * / | +-----* x+ - * 4 * | - * * 2 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 000 | 0 | LEFT | - * | `+x` | 001 | 1 | RIGHT | - * | `-y` | 010 | 2 | DOWN | - * | `+y` | 011 | 3 | UP | - * | `-z` | 100 | 4 | BACK | - * | `+z` | 101 | 5 | FRONT | - */ - enum Adjacency { - LEFT, - RIGHT, - DOWN, - UP, - BACK, - FRONT - }; - - /// \cond SKIP_IN_MANUAL - enum Child { - LEFT_BOTTOM_BACK, - RIGHT_BOTTOM_BACK, - LEFT_TOP_BACK, - RIGHT_TOP_BACK, - LEFT_BOTTOM_FRONT, - RIGHT_BOTTOM_FRONT, - LEFT_TOP_FRONT, - RIGHT_TOP_FRONT - }; - /// \endcond - - /// @} - - - /// \name Operations - /// @{ - - auto construct_point_d_object() const { - return [](const FT& x, const FT& y, const FT& z) -> Point_d { - return {x, y, z}; - }; - } - - /// @} - -}; - -} - -#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_d_base.h b/Orthtree/include/CGAL/Orthtree_traits_d_base.h deleted file mode 100644 index 675fbb02aec9..000000000000 --- a/Orthtree/include/CGAL/Orthtree_traits_d_base.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2023 INRIA (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) : Jackson Campolattaro - -#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_D_BASE_H -#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_D_BASE_H - -#include - -#include -#include -#include -#include - -namespace CGAL { - -/*! - \ingroup PkgOrthtreeTraits - - The class `Orthtree_traits_d_base` can be subclassed for easier implementation of a dd OrthtreeTraits concept. - - \tparam GeomTraits model of `Kernel`. - - \cgalModels{OrthtreeTraits} - \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_point_d` -*/ -template -struct Orthtree_traits_d_base { -public: - - /// \name Types - /// @{ - - using Kernel = K; - using Dimension = DimensionTag; - using FT = typename K::FT; - using Point_d = typename K::Point_d; - using Bbox_d = typename K::Iso_box_d; - using Sphere_d = typename K::Sphere_d; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; - - /*! - Adjacency type. - - \note This type is used to identify adjacency directions with - easily understandable keywords (left, right, up, etc.) and is thus - mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In - higher dimensions, such keywords do not exist and this type is - simply an integer. Conversions from this integer to bitsets still - work but do not provide any easier API for adjacency selection. - */ - using Adjacency = int; - - /// @} - - - /// \name Operations - /// @{ - - auto construct_point_d_object() const { - return [](auto... Args) -> Point_d { - std::initializer_list args_list{Args...}; - return Point_d{args_list.size(), args_list.begin(), args_list.end()}; - }; - } - - /// @} - -}; - -} - -#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_2_BASE_H From d43432d533aa0314be6d86508640617f9bc27a4b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 12 Jan 2024 17:47:09 +0100 Subject: [PATCH 304/520] adding locate_halfspace_object to traits changed reference directions removed unused traits added traits for face graph pass on documentation --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 20 +++- Orthtree/doc/Orthtree/PackageDescription.txt | 11 ++- .../examples/Orthtree/octree_surface_mesh.cpp | 1 - Orthtree/include/CGAL/Orthtree.h | 51 ++++------ .../CGAL/Orthtree_traits_base_for_dimension.h | 94 +++++++++++-------- .../include/CGAL/Orthtree_traits_face_graph.h | 51 +++++----- Orthtree/include/CGAL/Orthtree_traits_point.h | 9 +- 7 files changed, 130 insertions(+), 107 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index d49253521817..c297cf0d52f7 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -25,12 +25,9 @@ class OrthtreeTraits /*! A random access iterator type to enumerate the %Cartesian coordinates of a point. - - todo: This isn't used, should it be? */ using Cartesian_const_iterator_d = unspecified_type; - /*! * \brief The data type contained by each node. */ @@ -39,7 +36,7 @@ class OrthtreeTraits /*! * \brief Number-type which can take on values indicating adjacency directions. * - * Must be able to take on values ranging from 0 to the number of faces of the (hyper)cube type, equivalent to 2 * D. + * Must be able to take on values ranging from 0 to the number of faces of the (hyper)rectangle type, equivalent to 2 * D. */ using Adjacency = unspecified_type; ///< Specify the adjacency directions @@ -47,7 +44,7 @@ class OrthtreeTraits * \brief Functor with an operator to create the bounding box of the root node. * * The bounding box must enclose all elements contained by the tree. - * It may be tight-fitting. The orthtree constructor produces a bounding cube surrounding this region. + * It may be tight-fitting. The orthtree constructor produces a bounding box surrounding this region. * For traits which assign no data to each node, this can be defined to return a fixed region. */ using Construct_root_node_bbox = unspecified_type; @@ -66,6 +63,14 @@ class OrthtreeTraits */ using Construct_root_node_contents = unspecified_type; + /*! + * \brief Functor to locate in which halfspace a number of type `FT` is located with respect to another number of type `FT`. + * + * The functor is used by `Orthtree::locate()` to identify in which leaf node a point is located. + * Distribute_node_contents should use `Locate_halfspace` to guarantee consistency wich `Orthtree::locate()`. + */ + using Locate_halfspace = unspecified_type; + /*! * \brief Functor which distributes a node's contents to its children. * @@ -106,6 +111,11 @@ class OrthtreeTraits */ Distribute_node_contents distribute_node_contents_object() const; + /*! + * Function used to construct an object of type `Locate_halfspace`. + */ + Locate_halfspace locate_halfspace_object() const; + /*! * Function used to construct an object of type `Construct_point_d`. */ diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index a81cb6f42e3d..27d72e488ff4 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -1,4 +1,5 @@ -/// \defgroup PkgOrthtreeRef Quadtree\, Octree and Orthtree Reference +/// \defgroup PkgOrthtreeRef Quadtrees, Octrees and Orthtrees Reference +Quadtree, Octree and Orthtree Reference /// \defgroup PkgOrthtreeClasses Classes /// \ingroup PkgOrthtreeRef @@ -48,12 +49,12 @@ \cgalCRPSection{Classes} - `CGAL::Quadtree` - `CGAL::Octree` -- `CGAL::Orthtree` +- `CGAL::Orthtree` \cgalCRPSection{Traits} -- `CGAL::Orthtree_traits_2` -- `CGAL::Orthtree_traits_point_3` -- `CGAL::Orthtree_traits_d` +- `CGAL::Orthtree_traits_point` +- `CGAL::Orthtree_traits_base_for_dimension` +- `CGAL::Orthtree_traits_face_graph` \cgalCRPSection{Split Predicates} - `CGAL::Orthtrees::Maximum_number_of_inliers` diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index 2e8fdd510330..cda6ad2b0240 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -14,7 +14,6 @@ using Octree = CGAL::Orthtree; void dump_as_polylines(const Octree& ot) { - // SL: I cheated for this part and looked at the implementation std::ofstream out("octree.polylines.txt"); for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) { diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2af93b7fde5d..8a4fa2635f09 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -50,13 +50,13 @@ namespace CGAL { /*! \ingroup PkgOrthtreeClasses - \brief A data structure using an axis-aligned hybercubic + \brief A data structure using an axis-aligned hyperrectangle decomposition of dD space for efficient access and computation. - \details It builds a hierarchy of nodes which subdivices the space. - Each node represents an axis-aligned hypercubic region of space. - The contents of nodes depend on the Traits class, non-leaf nodes also + \details It builds a hierarchy of nodes which subdivides the space. + Each node represents an axis-aligned hyperrectangle region of space. + The contents of nodes depend on the traits class, non-leaf nodes also contain \f$2^{dim}\f$ other nodes which further subdivide the region. @@ -86,7 +86,6 @@ class Orthtree { using Adjacency = typename Traits::Adjacency; ///< Adjacency type. using Node_data = typename Traits::Node_data; - // todo: Node_data_element will only exist for certain Traits types, so I don't know if it can be re-exported /// @} @@ -94,7 +93,7 @@ class Orthtree { /// @{ /*! - * \brief Self typedef for convenience. + * \brief Self alias for convenience. */ using Self = Orthtree; @@ -138,7 +137,7 @@ class Orthtree { using Split_predicate = std::function; /*! - * \brief A model of `ConstRange` whose value type is `Node_index`. + * \brief A model of `ConstRange` whose value type is `Node_index`. Its iterator type is `ForwardIterator`. */ #ifdef DOXYGEN_RUNNING using Node_range = unspecified_type; @@ -148,16 +147,7 @@ class Orthtree { #endif /*! - * \brief A model of `ConstRange` whose value type is `Node_index`. - */ -#ifdef DOXYGEN_RUNNING - using Node_range = unspecified_type; - using Node_index_range = unspecified_type; -#else - using Node_index_range = boost::iterator_range>; -#endif - /*! - * \brief A Model of `LvaluePropertyMap` with `Node_index` as key type and `T` as value type. + * \brief A model of `LvaluePropertyMap` with `Node_index` as key type and `T` as value type. */ #ifdef DOXYGEN_RUNNING template @@ -204,8 +194,8 @@ class Orthtree { \brief creates an orthtree for a traits instance. The constructed orthtree has a root node with no children, - containing the contents determined by `construct_root_node_contents_object` from the Traits class. - That root node has a bounding box determined by `construct_root_node_bbox_object` from the Traits class, + containing the contents determined by `Construct_root_node_contents` from the traits class. + That root node has a bounding box determined by `Construct_root_node_bbox` from the traits class, which typically encloses its contents. This single-node orthtree is valid and compatible @@ -419,9 +409,9 @@ class Orthtree { /// @{ /*! - * \brief provides direct read-only access to the tree Traits. + * \brief provides direct read-only access to the tree traits. * - * @return a const reference to the Traits instantiation. + * @return a const reference to the traits instantiation. */ const Traits& traits() const { return m_traits; } @@ -485,7 +475,6 @@ class Orthtree { \note The object constructed is not the bounding box of the node's contents, but the bounding box of the node itself. - For a cubic orthtree, this will always be cubic. \param n node to generate a bounding box for @@ -610,8 +599,8 @@ class Orthtree { // Find the index of the correct sub-node Local_coordinates local_coords; std::size_t dimension = 0; - for (const auto& r: cartesian_range(center, point)) - local_coords[dimension++] = (get < 0 > (r) < get < 1 > (r)); + for (const auto& r: cartesian_range(point, center)) + local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); // Find the correct sub-node of the current node node_for_point = child(node_for_point, local_coords.to_ulong()); @@ -627,7 +616,7 @@ class Orthtree { \note this function requires the function `bool CGAL::do_intersect(QueryType, Traits::Bbox_d)` to be defined. - This function finds all the intersecting leaf nodes and writes their indices to the ouput iterator. + This function finds all the intersecting leaf nodes and writes their indices to the output iterator. \tparam Query the primitive class (e.g. sphere, ray) \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types @@ -902,7 +891,7 @@ class Orthtree { /*! \brief finds node reached when descending the tree to a depth `d` and always choosing child 0. - Similar to `deepest_first_child`, but does not go to a fixed depth. + Similar to `deepest_first_child()`, but does go to a fixed depth. \param n the index of the node to find the `d`th first child of. \param d the depth to descend to. @@ -936,7 +925,7 @@ class Orthtree { When a node is split it is no longer a leaf node. A number of `Degree::value` children are constructed automatically, and their values are set. Contents of this node are _not_ propagated automatically, this is responsibility of the - `distribute_node_contents_object` in the Traits class. + `distribute_node_contents_object` in the traits class. \param n index of the node to split */ @@ -1104,7 +1093,7 @@ class Orthtree { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 - // Nodes only have up to 2*dim different adjacent nodes (since cubes have 6 sides) + // Nodes only have up to 2*dim different adjacent nodes (since boxes have 6 sides) CGAL_precondition(direction.to_ulong() < Dimension::value * 2); // The root node has no adjacent nodes! @@ -1165,11 +1154,11 @@ class Orthtree { bool do_intersect(Node_index n, const Sphere& sphere) const { - // Create a cubic bounding box from the node - Bbox node_cube = bbox(n); + // Create a bounding box from the node + Bbox node_box = bbox(n); // Check for intersection between the node and the sphere - return CGAL::do_intersect(node_cube, sphere); + return CGAL::do_intersect(node_box, sphere); } template diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 8729fa9c98ab..edcdd9542044 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -9,8 +9,8 @@ // // Author(s) : Jackson Campolattaro -#ifndef ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H -#define ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H +#ifndef ORTHTREE_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H +#define ORTHTREE_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H #include @@ -22,9 +22,6 @@ namespace CGAL { -template -struct Orthtree_traits_base_for_dimension; - template struct Orthtree_traits_base_for_dimension { /// \name Types @@ -57,6 +54,12 @@ struct Orthtree_traits_base_for_dimension { return Point_d{static_cast(args_list.size()), args_list.begin(), args_list.end()}; }; } + + auto locate_halfspace_object() const { + return [](const FT& a, const FT& b) -> bool { + return a < b; + }; + } /// @} }; @@ -72,7 +75,7 @@ struct Orthtree_traits_base_for_dimension> { using Sphere_d = typename K::Circle_2; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. + * \brief Two directions along each axis in %Cartesian space, relative to a node. * * Directions are mapped to numbers as 2-bit integers. * @@ -81,15 +84,15 @@ struct Orthtree_traits_base_for_dimension> { * * The following diagram may be a useful reference: * - * 3 * - * | - * | y+ - * | * - * 0 *------+------* 1 | - * | | - * | +-----* x+ - * | - * * 2 + * + * * 3 + * / + * / y+ + * 0 *------+------* 1 * + * / / + * / +-----* x+ + * 2 * + * * * This lookup table may also be helpful: * @@ -97,14 +100,14 @@ struct Orthtree_traits_base_for_dimension> { * | --------- | ------ | ------ | ----- | * | `-x` | 00 | 0 | LEFT | * | `+x` | 01 | 1 | RIGHT | - * | `-y` | 10 | 2 | DOWN | - * | `+y` | 11 | 3 | UP | + * | `-y` | 10 | 2 | FRONT | + * | `+y` | 11 | 3 | BACK | */ enum Adjacency { LEFT, RIGHT, - DOWN, - UP + FRONT, + BACK }; /// @} @@ -115,6 +118,12 @@ struct Orthtree_traits_base_for_dimension> { return {x, y}; }; } + + auto locate_halfspace_object() const { + return [](const FT& a, const FT& b) -> bool { + return a < b; + }; + } /// @} }; @@ -130,7 +139,7 @@ struct Orthtree_traits_base_for_dimension> { using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; /*! - * \brief Two directions along each axis in Cartesian space, relative to a node. + * \brief Two directions along each axis in %Cartesian space, relative to a node. * * Directions are mapped to numbers as 3-bit integers, * though the numbers 6 and 7 are not used because there are only 6 different directions. @@ -140,15 +149,15 @@ struct Orthtree_traits_base_for_dimension> { * * The following diagram may be a useful reference: * - * 3 * - * | * 5 - * | / y+ - * |/ * z+ + * 5 * + * | * 3 + * | / z+ + * |/ * y+ * 0 *------+------* 1 | * * /| |/ * / | +-----* x+ - * 4 * | - * * 2 + * 2 * | + * * 4 * * This lookup table may also be helpful: * @@ -156,29 +165,29 @@ struct Orthtree_traits_base_for_dimension> { * | --------- | ------ | ------ | ----- | * | `-x` | 000 | 0 | LEFT | * | `+x` | 001 | 1 | RIGHT | - * | `-y` | 010 | 2 | DOWN | - * | `+y` | 011 | 3 | UP | - * | `-z` | 100 | 4 | BACK | - * | `+z` | 101 | 5 | FRONT | + * | `-y` | 010 | 2 | FRONT | + * | `+y` | 011 | 3 | BACK | + * | `-z` | 100 | 4 | DOWN | + * | `+z` | 101 | 5 | UP | */ enum Adjacency { LEFT, RIGHT, - DOWN, - UP, + FRONT, BACK, - FRONT + DOWN, + UP }; /// \cond SKIP_IN_MANUAL enum Child { - LEFT_BOTTOM_BACK, - RIGHT_BOTTOM_BACK, - LEFT_TOP_BACK, - RIGHT_TOP_BACK, LEFT_BOTTOM_FRONT, RIGHT_BOTTOM_FRONT, + LEFT_BOTTOM_BACK, + RIGHT_BOTTOM_BACK, LEFT_TOP_FRONT, - RIGHT_TOP_FRONT + RIGHT_TOP_FRONT, + LEFT_TOP_BACK, + RIGHT_TOP_BACK }; /// \endcond /// @} @@ -190,10 +199,15 @@ struct Orthtree_traits_base_for_dimension> { return {x, y, z}; }; } + + auto locate_halfspace_object() const { + return [](const FT& a, const FT& b) -> bool { + return a < b; + }; + } /// @} }; - } -#endif //ORTHTREE_EXAMPLES_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H +#endif //ORTHTREE_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 0f6b11eb55a1..25385d696b5f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -23,21 +23,30 @@ namespace CGAL { -template +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_face_graph` can be used as a template parameter of + the `Orthtree` class. + + \tparam PolygonMesh a model of `FaceGraph`. + \tparam VertexPointMap a property map associating points to the vertices of `PolygonMesh`. + + \cgalModels{OrthtreeTraits} + \sa `CGAL::Orthtree_traits_base_for_dimension` +*/ +template struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< - typename Kernel_traits::value_type>::type, - Dimension_tag<3> - // todo: it should be possible to determine the ambient dimension automatically, but this isn't working -// Ambient_dimension< -// typename boost::property_traits::value_type, -// typename Kernel_traits::value_type>::type -// > -> { - - Orthtree_traits_face_graph(const PolygonMesh& pm, VPM vpm) + typename Kernel_traits::value_type>::type, + Dimension_tag<3> > { + + Orthtree_traits_face_graph(const PolygonMesh& pm, VertexPointMap vpm) : m_pm(pm), m_vpm(vpm) {} - using Self = Orthtree_traits_face_graph; + /// \name Types + /// @{ + + using Self = Orthtree_traits_face_graph; using Tree = Orthtree; using Point_d = typename Self::Point_d; @@ -46,11 +55,12 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< using FT = typename Self::FT; using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; - // SL: these could be considered as built-in data and if the typedefs are not present, the tree have none using Node_data = std::vector::face_descriptor>; using Geom_traits = typename Kernel_traits::type; + /// @} + auto construct_root_node_bbox_object() const { return [&]() -> Bbox_d { @@ -82,21 +92,22 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< auto distribute_node_contents_object() { return [&](typename Tree::Node_index n, Tree& tree, const Point_d& center) -> void { Node_data& ndata = tree.data(n); + auto traits = tree.traits(); for (int i = 0; i < 8; ++i) { typename Tree::Node_index child = tree.child(n, i); Node_data& child_data = tree.data(child); Bbox_d bbox = tree.bbox(child); - for (auto f: ndata) { + for (auto f : ndata) { typename boost::graph_traits::halfedge_descriptor h = halfedge(f, m_pm); typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), - get(m_vpm, target(h, m_pm)), - get(m_vpm, target(next(h, m_pm), m_pm))); + get(m_vpm, target(h, m_pm)), + get(m_vpm, target(next(h, m_pm), m_pm))); if (do_intersect(t, bbox)) child_data.push_back(f); } } - }; + }; } class Split_predicate_node_min_extent { @@ -116,9 +127,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< if (tree.data(ni).empty()) return false; Bbox_d bb = tree.bbox(ni); - // TODO: we should get better version to get guarantees - // TODO: as long as the bbox is cubic you can use depth and initial size to conclude. - // todo (jackson): bbox is _not_ guaranteed to be cubic now, this may break in some very niche cases + for (int i = 0; i < 3; ++i) if ((bb.max()[i] - bb.min()[i]) < 2 * m_min_extent) return false; @@ -127,7 +136,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< }; const PolygonMesh& m_pm; - VPM m_vpm; + VertexPointMap m_vpm; }; } // end of CGAL namespace diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index eecaf2140642..c1974acd2046 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -36,13 +36,15 @@ void reassign_points( return; } + auto traits = tree.traits(); + // Split the point collection around the center point on this dimension auto split_point = std::partition( points.begin(), points.end(), [&](const auto& p) -> bool { // This should be done with cartesian iterator, // but it seems complicated to do efficiently - return (get(point_map, p)[int(dimension)] < center[int(dimension)]); + return traits.locate_halfspace_object()(get(point_map, p)[int(dimension)], center[int(dimension)]); } ); @@ -73,9 +75,8 @@ void reassign_points( \cgalModels{OrthtreeTraits} \sa `CGAL::Octree` - \sa `CGAL::Orthtree_traits_2` - \sa `CGAL::Orthtree_traits_3` - \sa `CGAL::Orthtree_traits_d` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_base_for_dimension` */ template < typename GeomTraits, From 172044b128bbbaf5208fab53b3bac8e1c8d4b590 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 12 Jan 2024 18:43:37 +0100 Subject: [PATCH 305/520] removing old references --- Orthtree/include/CGAL/Octree.h | 2 +- .../CGAL/Orthtree_traits_base_for_dimension.h | 111 ++++++++---------- .../include/CGAL/Orthtree_traits_face_graph.h | 7 ++ Orthtree/include/CGAL/Orthtree_traits_point.h | 1 - Orthtree/include/CGAL/Quadtree.h | 2 +- 5 files changed, 57 insertions(+), 66 deletions(-) diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 324d655e1d91..87e17fc8540f 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -26,7 +26,7 @@ namespace CGAL { These two types are exactly equivalent: - `Octree` - - `Orthtree, PointRange, PointMap>`. + - `Orthtree>>`. \warning This is a not a real class but an alias, please refer to the documentation of `Orthtree`. diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index edcdd9542044..1c878049013f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -22,6 +22,19 @@ namespace CGAL { +/*! + \ingroup PkgOrthtreeTraits + + The class `Orthtree_traits_base_for_dimension` is a base class providing common choices for types and functors. + The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. + + \tparam K model of `Kernel`. + \tparam DimensionTag is a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. + + \sa `CGAL::Orthtree_traits_point` + \sa `CGAL::Orthtree_traits_face_graph` +*/ + template struct Orthtree_traits_base_for_dimension { /// \name Types @@ -38,11 +51,42 @@ struct Orthtree_traits_base_for_dimension { \note This type is used to identify adjacency directions with easily understandable keywords (left, right, up, etc.) and is thus - mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In + mainly useful for `Dimension_tag<2>` and `Dimension_tag<3>`. In higher dimensions, such keywords do not exist and this type is simply an integer. Conversions from this integer to bitsets still work but do not provide any easier API for adjacency selection. - */ + + Two directions along each axis in %Cartesian space, relative to a node. + + Directions are mapped to numbers as 3-bit integers in the 3d case or as 2-bit integers in the 2d case. + In the 3d case the numbers 6 and 7 are not used because there are only 6 different directions. + + The first two bits indicate the axis (00 = x, 01 = y, 10 = z), + the third bit indicates the direction along that axis (0 = -, 1 = +). + + The following diagram showing the 3d case may be a useful reference: + + 5 * + | * 3 + | / z+ + |/ * y+ + 0 *------+------* 1 | * + /| |/ + / | +-----* x+ + 2 * | + * 4 + + This lookup table may also be helpful: + + | Direction | bitset | number | Enum | + | --------- | ------ | ------ | ----- | + | `-x` | 000 | 0 | LEFT | + | `+x` | 001 | 1 | RIGHT | + | `-y` | 010 | 2 | FRONT | + | `+y` | 011 | 3 | BACK | + | `-z` | 100 | 4 | DOWN | + | `+z` | 101 | 5 | UP | + */ using Adjacency = int; /// @} @@ -74,35 +118,7 @@ struct Orthtree_traits_base_for_dimension> { using Bbox_d = typename K::Iso_rectangle_2; using Sphere_d = typename K::Circle_2; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; - /*! - * \brief Two directions along each axis in %Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 2-bit integers. - * - * The first bit indicates the axis (0 = x, 1 = y), - * the second bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * - * * 3 - * / - * / y+ - * 0 *------+------* 1 * - * / / - * / +-----* x+ - * 2 * - * - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 00 | 0 | LEFT | - * | `+x` | 01 | 1 | RIGHT | - * | `-y` | 10 | 2 | FRONT | - * | `+y` | 11 | 3 | BACK | - */ + enum Adjacency { LEFT, RIGHT, @@ -138,38 +154,7 @@ struct Orthtree_traits_base_for_dimension> { using Bbox_d = typename K::Iso_cuboid_3; using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; - /*! - * \brief Two directions along each axis in %Cartesian space, relative to a node. - * - * Directions are mapped to numbers as 3-bit integers, - * though the numbers 6 and 7 are not used because there are only 6 different directions. - * - * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), - * the third bit indicates the direction along that axis (0 = -, 1 = +). - * - * The following diagram may be a useful reference: - * - * 5 * - * | * 3 - * | / z+ - * |/ * y+ - * 0 *------+------* 1 | * - * /| |/ - * / | +-----* x+ - * 2 * | - * * 4 - * - * This lookup table may also be helpful: - * - * | Direction | bitset | number | Enum | - * | --------- | ------ | ------ | ----- | - * | `-x` | 000 | 0 | LEFT | - * | `+x` | 001 | 1 | RIGHT | - * | `-y` | 010 | 2 | FRONT | - * | `+y` | 011 | 3 | BACK | - * | `-z` | 100 | 4 | DOWN | - * | `+z` | 101 | 5 | UP | - */ + enum Adjacency { LEFT, RIGHT, diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 25385d696b5f..a79d72bf6313 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -61,6 +61,9 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// @} + /// \name Operations + /// @{ + auto construct_root_node_bbox_object() const { return [&]() -> Bbox_d { @@ -135,6 +138,10 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< } }; + /// @} + +private: + const PolygonMesh& m_pm; VertexPointMap m_vpm; }; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index c1974acd2046..9546dddaa9ba 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -166,7 +166,6 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension` - - `Orthtree, PointRange, PointMap>`. + - `Orthtree>>`. \warning This is a not a real class but an alias, please refer to the documentation of `Orthtree`. From beea3f2fdae490da87eb42e2fa101579898cde86 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 12 Jan 2024 19:01:03 +0100 Subject: [PATCH 306/520] fixing comment for Node_index_range removing Node_range [skip ci] --- Orthtree/include/CGAL/Orthtree.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 8a4fa2635f09..9376af0b4285 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -137,10 +137,10 @@ class Orthtree { using Split_predicate = std::function; /*! - * \brief A model of `ConstRange` whose value type is `Node_index`. Its iterator type is `ForwardIterator`. + * \brief A model of `ConstRange` whose value type is `Node_index` and its iterator type is `ForwardIterator`. */ #ifdef DOXYGEN_RUNNING - using Node_range = unspecified_type; + using Node_index_range = unspecified_type; #else using Node_index_range = boost::iterator_range>; From add12f51bf87109b8b43e85c35475f29d4f9993c Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Sun, 14 Jan 2024 17:55:45 +0000 Subject: [PATCH 307/520] small fixes --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 4 ++-- .../doc/Orthtree/Concepts/OrthtreeTraversal.h | 2 +- Orthtree/include/CGAL/Octree.h | 4 ++-- Orthtree/include/CGAL/Orthtree.h | 16 +++++++------- .../include/CGAL/Orthtree/Nearest_neighbors.h | 22 +++++++++---------- Orthtree/include/CGAL/Quadtree.h | 2 +- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index c297cf0d52f7..2307d925ff2a 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -20,7 +20,7 @@ class OrthtreeTraits using Dimension = unspecified_type; ///< Dimension type (see `CGAL::Dimension_tag`). using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` using Point_d = unspecified_type; ///< Point type. - using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of Point_d types. + using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` types. /*! A random access iterator type to enumerate the @@ -84,7 +84,7 @@ class OrthtreeTraits using Distribute_node_contents = unspecified_type; /*! - * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. FT arguments. + * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. `FT` arguments. * * For trees which use a different kernel for the Bbox type, * the return type of this functor must match the kernel used by the Bbox and not that of the contents. diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index 6bde24602f21..7572fa9c50b8 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -23,7 +23,7 @@ class OrthtreeTraversal { using Node_index = unspecified_type; ///< Index type of the orthtree to be traversed /*! - \brief returns the first node to iterate to, given the root of the Orthtree. + \brief returns the first node to iterate to, given the root of the orthtree. */ Node_index first_index() const; diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 87e17fc8540f..1a7f6622141b 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -26,13 +26,13 @@ namespace CGAL { These two types are exactly equivalent: - `Octree` - - `Orthtree>>`. + - `Orthtree>>`. \warning This is a not a real class but an alias, please refer to the documentation of `Orthtree`. \tparam GeomTraits must be a model of `Kernel` - \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap` + \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` */ template < diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9376af0b4285..51c30f8ece1f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -199,7 +199,7 @@ class Orthtree { which typically encloses its contents. This single-node orthtree is valid and compatible - with all Orthtree functionality, but any performance benefits are + with all orthtree functionality, but any performance benefits are unlikely to be realized until `refine()` is called. \param traits the traits object. @@ -500,7 +500,7 @@ class Orthtree { /// @{ /*! - \brief gets a property for nodes, adding it if it doesn't already exist. + \brief gets a property for nodes, adding it if it does not already exist. \tparam T the type of the property to add @@ -618,7 +618,7 @@ class Orthtree { This function finds all the intersecting leaf nodes and writes their indices to the output iterator. - \tparam Query the primitive class (e.g. sphere, ray) + \tparam Query the primitive class (e.g., sphere, ray) \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types \param query the intersecting primitive. @@ -823,7 +823,7 @@ class Orthtree { /*! \brief finds the next sibling in the parent of the node specified by the index `n`. - Traverses the tree in increasing order of local index (e.g. 000, 001, 010, etc.) + Traverses the tree in increasing order of local index (e.g., 000, 001, 010, etc.) \param n the index of the node to find the sibling of @@ -1026,7 +1026,7 @@ class Orthtree { } /*! - \brief helper function for calling `is_topology_equal` on the root nodes of two trees. + \brief helper function for calling `is_topology_equal()` on the root nodes of two trees. \param lhs an Orthtree \param rhs another Orthtree @@ -1047,7 +1047,7 @@ class Orthtree { - a node has at most `2 * Dimension::value` different adjacent nodes (in 3D: left, right, up, down, front, back) - adjacent nodes are not required to be leaf nodes - Here's a diagram demonstrating the concept for a Quadtree: + Here's a diagram demonstrating the concept for a quadtree: ``` +---------------+---------------+ @@ -1083,7 +1083,7 @@ class Orthtree { \param n index of the node to find a neighbor of \param direction which way to find the adjacent node relative to this one. Each successive bit selects the direction for the - corresponding dimension: for an Octree in 3D, 010 means: negative + corresponding dimension: for an octree in 3D, 010 means: negative direction in X, position direction in Y, negative direction in Z. \return the index of the adjacent node if it exists, nothing otherwise. @@ -1105,7 +1105,7 @@ class Orthtree { // The first two bits indicate the dimension/axis (x, y, z) uint8_t dimension = uint8_t((direction >> 1).to_ulong()); - // Create an offset so that the bit-significance lines up with the dimension (e.g. 1, 2, 4 --> 001, 010, 100) + // Create an offset so that the bit-significance lines up with the dimension (e.g., 1, 2, 4 --> 001, 010, 100) int8_t offset = (uint8_t) 1 << dimension; // Finally, apply the sign to the offset diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 7fcdbf36696a..06c48548d2ed 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -118,7 +118,7 @@ namespace Orthtrees { /*! \ingroup PkgOrthtreeNeighbors \brief finds the `k` points within a specific radius that are - nearest to the center of `query_sphere`. + nearest to the center of the sphere `query`. This function guarantees that there are no closer points than the ones returned, but it does not guarantee that it will return at least `k` points. @@ -132,14 +132,14 @@ namespace Orthtrees { \tparam OutputIterator must be a model of `OutputIterator` that accepts points \param orthtree the tree to search within - \param query_sphere the region to search within + \param query the region to search within \param k the number of points to find \param output the output iterator to add the found points to (in order of increasing distance) */ template OutputIterator nearest_k_neighbors_in_radius( const Tree& orthtree, - typename Tree::Sphere& query_sphere, + typename Tree::Sphere& query, std::size_t k, OutputIterator output ) { @@ -156,7 +156,7 @@ OutputIterator nearest_k_neighbors_in_radius( points_list.reserve(k); // Invoking the recursive function adds those points to the vector (passed by reference) - CGAL::internal::nearest_k_neighbors_recursive(orthtree, query_sphere, orthtree.root(), points_list); + CGAL::internal::nearest_k_neighbors_recursive(orthtree, query, orthtree.root(), points_list); // Add all the points found to the output for (auto& item: points_list) @@ -168,13 +168,13 @@ OutputIterator nearest_k_neighbors_in_radius( /*! \ingroup PkgOrthtreeNeighbors - \brief finds the `k` nearest neighbors of `query`. + \brief finds the `k` nearest neighbors of the point `query`. Nearest neighbors are outputted in order of increasing distance to `query`. - \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits - \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. + \tparam Tree must be an orthtree with traits which are a model of `CollectionPartitioningOrthtreeTraits` + \tparam OutputIterator a model of `OutputIterator` that accepts `Point_d` objects. \param orthtree the tree to search within \param query query point. @@ -191,13 +191,13 @@ OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Poin /*! \ingroup PkgOrthtreeNeighbors - \brief finds the points in `sphere`. + \brief finds the points in the sphere `query`. Nearest neighbors are outputted in order of increasing distance to - the center of `sphere`. + the center of the sphere. - \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits - \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. + \tparam Tree must be an orthtree with traits which are a model of `CollectionPartitioningOrthtreeTraits` + \tparam OutputIterator a model of `OutputIterator` that accepts `Point_d` objects. \param orthtree the tree to search within \param query query sphere. diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 9b312c588c02..6b8d832bf8da 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -32,7 +32,7 @@ namespace CGAL { the documentation of `Orthtree`. \tparam GeomTraits must be a model of `Kernel` - \tparam PointRange_ must be a model of range whose value type is the key type of `PointMap` + \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_2` */ template Date: Sun, 14 Jan 2024 21:31:03 +0000 Subject: [PATCH 308/520] PointSet -> PointRange --- .../CollectionPartitioningOrthtreeTraits.h | 4 +-- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 2 +- .../CGAL/Orthtree_traits_base_for_dimension.h | 4 +-- Orthtree/include/CGAL/Orthtree_traits_point.h | 26 +++++++++---------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index ddf2218fda3e..079c75ac7266 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -13,7 +13,7 @@ \cgalRefines{OrthtreeTraits} \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModelsEnd */ class CollectionPartitioningOrthtreeTraits { @@ -39,7 +39,7 @@ class CollectionPartitioningOrthtreeTraits { /*! * \brief Functor with an operator to produce a geometric object from a `Node_data_element`. * - * The return type of the functor must be a valid argument to `CGAL::squared_distance`. + * The return type of the functor must be a valid argument to `CGAL::squared_distance()`. */ using Get_geometric_object_for_element = unspecified_type; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 2307d925ff2a..409d53bb7a10 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -6,7 +6,7 @@ template parameter of the `CGAL::Orthtree` class. \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} \cgalHasModelsEnd */ diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 1c878049013f..eb3b6520ddc6 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -26,12 +26,12 @@ namespace CGAL { \ingroup PkgOrthtreeTraits The class `Orthtree_traits_base_for_dimension` is a base class providing common choices for types and functors. - The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. + The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. \tparam K model of `Kernel`. \tparam DimensionTag is a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. - \sa `CGAL::Orthtree_traits_point` + \sa `CGAL::Orthtree_traits_point` \sa `CGAL::Orthtree_traits_face_graph` */ diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 9546dddaa9ba..6688a8e56633 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -66,7 +66,7 @@ void reassign_points( the `Orthtree` class. \tparam GeomTraits model of `Kernel`. - \tparam PointSet must be a model of range whose value type is the key type of `PointMap` + \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` \warning The input point set is not copied. It is used directly @@ -80,10 +80,10 @@ void reassign_points( */ template < typename GeomTraits, - typename PointSet, - typename PointMap = Identity_property_map::value_type>, + typename PointRange, + typename PointMap = Identity_property_map::value_type>, typename DimensionTag = Ambient_dimension< - typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type, GeomTraits > > @@ -93,18 +93,18 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension; + using Self = Orthtree_traits_point; using Tree = Orthtree; - using Node_data = boost::iterator_range; - using Node_data_element = typename std::iterator_traits::value_type; + using Node_data = boost::iterator_range; + using Node_data_element = typename std::iterator_traits::value_type; /// @} Orthtree_traits_point( - PointSet& point_set, + PointRange& points, PointMap point_map = PointMap() - ) : m_point_set(point_set), m_point_map(point_map) {} + ) : m_points(points), m_point_map(point_map) {} /// \name Operations /// @{ @@ -117,7 +117,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension typename Self::Node_data { - return {m_point_set.begin(), m_point_set.end()}; + return {m_points.begin(), m_points.end()}; }; } @@ -164,7 +164,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension Date: Mon, 15 Jan 2024 07:40:55 +0000 Subject: [PATCH 309/520] Remove one group layer --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 10 +++++----- Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h | 4 ++-- Orthtree/doc/Orthtree/PackageDescription.txt | 9 +++------ Orthtree/include/CGAL/Octree.h | 2 +- Orthtree/include/CGAL/Orthtree.h | 2 +- Orthtree/include/CGAL/Orthtree/Traversal_iterator.h | 2 +- Orthtree/include/CGAL/Quadtree.h | 2 +- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 409d53bb7a10..c82941d11712 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -58,7 +58,7 @@ class OrthtreeTraits * It should take no arguments, and return an instance of `Node_data`. * * Typically, the `Node_data` of the root node contains all the elements in the tree. - * For a tree in which each node contains an `std::span` this function would return the span containing all items. + * For a tree in which each node contains an `std::span()` this function would return the span containing all items. * */ using Construct_root_node_contents = unspecified_type; @@ -67,7 +67,7 @@ class OrthtreeTraits * \brief Functor to locate in which halfspace a number of type `FT` is located with respect to another number of type `FT`. * * The functor is used by `Orthtree::locate()` to identify in which leaf node a point is located. - * Distribute_node_contents should use `Locate_halfspace` to guarantee consistency wich `Orthtree::locate()`. + * `Distribute_node_contents` mustt use `Locate_halfspace` to guarantee consistency wich `Orthtree::locate()`. */ using Locate_halfspace = unspecified_type; @@ -84,10 +84,10 @@ class OrthtreeTraits using Distribute_node_contents = unspecified_type; /*! - * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc. `FT` arguments. + * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc.\ `FT` arguments. * - * For trees which use a different kernel for the Bbox type, - * the return type of this functor must match the kernel used by the Bbox and not that of the contents. + * For trees which use a different kernel for the bounding box type, + * the return type of this functor must match the kernel used by the bounding box type and not that of the contents. */ using Construct_point_d = unspecified_type; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index 7572fa9c50b8..175360bda6fa 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -3,11 +3,11 @@ \ingroup PkgOrthtreeConcepts \cgalConcept - \brief a traversal provides the functions needed to traverse the + \brief A traversal provides the functions needed to traverse the nodes of an orthtree. A traversal is used to iterate on a tree with a user-selected order - (e.g. preorder, postorder). + (e.g., preorder, postorder). \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtrees::Preorder_traversal} diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 27d72e488ff4..cf761741d622 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -1,19 +1,16 @@ /// \defgroup PkgOrthtreeRef Quadtrees, Octrees and Orthtrees Reference Quadtree, Octree and Orthtree Reference -/// \defgroup PkgOrthtreeClasses Classes +/// \defgroup PkgOrthtreeConcepts Concepts /// \ingroup PkgOrthtreeRef /// \defgroup PkgOrthtreeTraits Traits -/// \ingroup PkgOrthtreeClasses +/// \ingroup PkgOrthtreeRef /// \defgroup PkgOrthtreeSplitPredicates Split Predicates -/// \ingroup PkgOrthtreeClasses +/// \ingroup PkgOrthtreeRef /// \defgroup PkgOrthtreeTraversal Traversal -/// \ingroup PkgOrthtreeClasses - -/// \defgroup PkgOrthtreeConcepts Concepts /// \ingroup PkgOrthtreeRef /// \defgroup PkgOrthtreeNeighbors Neighbor Search Functions diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 1a7f6622141b..1bdb02c7f2c8 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -20,7 +20,7 @@ namespace CGAL { /*! - \ingroup PkgOrthtreeClasses + \ingroup PkgOrthtreeRef \brief Alias that specializes the `Orthtree` class to a 3D octree. diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 51c30f8ece1f..563e6c94850d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -48,7 +48,7 @@ namespace CGAL { /*! - \ingroup PkgOrthtreeClasses + \ingroup PkgOrthtreeRef \brief A data structure using an axis-aligned hyperrectangle decomposition of dD space for efficient access and diff --git a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h index f202406fb1f5..101522875380 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h +++ b/Orthtree/include/CGAL/Orthtree/Traversal_iterator.h @@ -25,7 +25,7 @@ namespace CGAL { /*! - * \ingroup PkgOrthtreeClasses + * \ingroup PkgOrthtreeTraversal * * \brief Wraps a traversal definition to produce an iterator which traverses the tree when incremented. * diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 6b8d832bf8da..f9474a961df5 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -20,7 +20,7 @@ namespace CGAL { /*! - \ingroup PkgOrthtreeClasses + \ingroup PkgOrthtreeRef \brief Alias that specializes the `Orthtree` class to a 2D quadtree. From da9cec8359858f10d3c75d37fb13e3d40e9317bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 15 Jan 2024 09:26:40 +0100 Subject: [PATCH 310/520] update deps --- Orthtree/package_info/Orthtree/dependencies | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Orthtree/package_info/Orthtree/dependencies b/Orthtree/package_info/Orthtree/dependencies index 35131847da36..5f485c987a3b 100644 --- a/Orthtree/package_info/Orthtree/dependencies +++ b/Orthtree/package_info/Orthtree/dependencies @@ -1,6 +1,5 @@ Algebraic_foundations Arithmetic_kernel -BGL Cartesian_kernel Circulator Distance_2 @@ -12,19 +11,16 @@ Installation Intersections_2 Intersections_3 Interval_support -Kernel_d Kernel_23 +Kernel_d Modular_arithmetic Number_types Orthtree Point_set_2 -Point_set_3 -Point_set_processing_3 Profiling_tools Property_map STL_Extension -Stream_support Spatial_sorting -Surface_mesh +Stream_support TDS_2 -Triangulation_2 \ No newline at end of file +Triangulation_2 From fa8e09ecd0a4ac16f5f63f0eae65225aeb6aae8a Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 15 Jan 2024 08:32:23 +0000 Subject: [PATCH 311/520] backtick --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index c82941d11712..1bb12fa17918 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -67,17 +67,17 @@ class OrthtreeTraits * \brief Functor to locate in which halfspace a number of type `FT` is located with respect to another number of type `FT`. * * The functor is used by `Orthtree::locate()` to identify in which leaf node a point is located. - * `Distribute_node_contents` mustt use `Locate_halfspace` to guarantee consistency wich `Orthtree::locate()`. + * `Distribute_node_contents` must use `Locate_halfspace` to guarantee consistency wich `Orthtree::locate()`. */ using Locate_halfspace = unspecified_type; /*! * \brief Functor which distributes a node's contents to its children. * - * The functor takes a node index, a tree reference, and a Point_d which is the center of the node. - * It can use tree.children(node_index) to access the children of the node, and tree.data(node_index) + * The functor takes a node index, a tree reference, and a `Point_d` which is the center of the node. + * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` * to access the contents of the node and each of its children. - * It should distribute the contents of the node to each of its children. + * It must distribute the contents of the node to each of its children. * For a tree in which each node contains a span, this may mean rearranging the contents of the original node * and producing spans containing a subset of its contents for each of its children. */ From 25d658f2fad49c1831808a79ff8667c021d2da3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 15 Jan 2024 09:58:43 +0100 Subject: [PATCH 312/520] doc the real type --- Orthtree/include/CGAL/Octree.h | 7 ------- Orthtree/include/CGAL/Quadtree.h | 9 ++------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 1bdb02c7f2c8..b5b7b282b452 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -28,9 +28,6 @@ namespace CGAL { - `Octree` - `Orthtree>>`. - \warning This is a not a real class but an alias, please refer to - the documentation of `Orthtree`. - \tparam GeomTraits must be a model of `Kernel` \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` @@ -40,11 +37,7 @@ template < typename PointRange, typename PointMap = Identity_property_map::value_type> > -#ifdef DOXYGEN_RUNNING - class Octree; -#else using Octree = Orthtree>>; -#endif } // namespace CGAL diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index f9474a961df5..3c38f50b2a68 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -28,9 +28,6 @@ namespace CGAL { - `Quadtree` - `Orthtree>>`. - \warning This is a not a real class but an alias, please refer to - the documentation of `Orthtree`. - \tparam GeomTraits must be a model of `Kernel` \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_2` @@ -38,11 +35,9 @@ namespace CGAL { template ::value_type> > -#ifdef DOXYGEN_RUNNING -class Quadtree; -#else + using Quadtree = Orthtree>>; -#endif + } // namespace CGAL From cec138c58d6fdaaf6dd02af1e3423ea07ff4d2d9 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 15 Jan 2024 10:12:24 +0100 Subject: [PATCH 313/520] Update QP_solver/include/CGAL/QP_solver/basic.h --- QP_solver/include/CGAL/QP_solver/basic.h | 1 - 1 file changed, 1 deletion(-) diff --git a/QP_solver/include/CGAL/QP_solver/basic.h b/QP_solver/include/CGAL/QP_solver/basic.h index 06c2572d4416..6b48264e8f7e 100644 --- a/QP_solver/include/CGAL/QP_solver/basic.h +++ b/QP_solver/include/CGAL/QP_solver/basic.h @@ -23,7 +23,6 @@ #include #include #include -#define _HAS_AUTO_PTR_ETC False // temporarility necessary for boost's compatibility with Clang 15 #include #endif // CGAL_QP_SOLVER_BASIC_H From 825029a31103f88bd448eded4f297674a27fda31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 15 Jan 2024 12:26:33 +0100 Subject: [PATCH 314/520] improve doc --- Orthtree/doc/Orthtree/PackageDescription.txt | 2 +- .../include/CGAL/Orthtree_traits_face_graph.h | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index cf761741d622..3502660edd39 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -51,7 +51,7 @@ Quadtree, Octree and Orthtree Reference \cgalCRPSection{Traits} - `CGAL::Orthtree_traits_point` - `CGAL::Orthtree_traits_base_for_dimension` -- `CGAL::Orthtree_traits_face_graph` +- `CGAL::Orthtree_traits_face_graph` \cgalCRPSection{Split Predicates} - `CGAL::Orthtrees::Maximum_number_of_inliers` diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index a79d72bf6313..ccfaaf433bdc 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -24,29 +24,34 @@ namespace CGAL { /*! - \ingroup PkgOrthtreeTraits +\ingroup PkgOrthtreeTraits - The class `Orthtree_traits_face_graph` can be used as a template parameter of - the `Orthtree` class. +Traits class for the `Orthtree` class to be used to contruct a 3D octree around +a triangulated surface mesh. Each node of the octree will store all the faces of the +mesh intersected by its bounding box. The subdivision of the octree is controlled +by the nested class `Orthtree_traits_face_graph::Split_predicate_node_min_extent` +to which the minimal extend of a node should be provided. - \tparam PolygonMesh a model of `FaceGraph`. - \tparam VertexPointMap a property map associating points to the vertices of `PolygonMesh`. +\tparam TriangleMesh a model of `FaceListGraph` with all faces being triangles +\tparam VertexPointMap a property map associating points to the vertices of `TriangleMesh` - \cgalModels{OrthtreeTraits} - \sa `CGAL::Orthtree_traits_base_for_dimension` +\todo check how to adapt to non regular splits (cubes vs rectangular cuboid) + +\cgalModels{OrthtreeTraits} +\sa `CGAL::Orthtree_traits_base_for_dimension` */ -template +template struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< typename Kernel_traits::value_type>::type, Dimension_tag<3> > { - Orthtree_traits_face_graph(const PolygonMesh& pm, VertexPointMap vpm) + Orthtree_traits_face_graph(const TriangleMesh& pm, VertexPointMap vpm) : m_pm(pm), m_vpm(vpm) {} /// \name Types /// @{ - using Self = Orthtree_traits_face_graph; + using Self = Orthtree_traits_face_graph; using Tree = Orthtree; using Point_d = typename Self::Point_d; @@ -55,10 +60,14 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< using FT = typename Self::FT; using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; - using Node_data = std::vector::face_descriptor>; + using Node_data = std::vector::face_descriptor>; using Geom_traits = typename Kernel_traits::type; + using Construct_root_node_bbox = std::function; + using Construct_root_node_contents = std::function; + using Distribute_node_contents = std::function; + /// @} /// \name Operations @@ -101,7 +110,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< Node_data& child_data = tree.data(child); Bbox_d bbox = tree.bbox(child); for (auto f : ndata) { - typename boost::graph_traits::halfedge_descriptor + typename boost::graph_traits::halfedge_descriptor h = halfedge(f, m_pm); typename Geom_traits::Triangle_3 t(get(m_vpm, source(h, m_pm)), get(m_vpm, target(h, m_pm)), @@ -113,12 +122,18 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< }; } + /// @} + + /// Recommanded split predicate to pass to `Orthtree::refine()` function so + /// that the octree is refined until a node is either empty or has an extend + /// that would be smaller after split than the value provided to the constructor. class Split_predicate_node_min_extent { FT m_min_extent; public: + /// constructor with `me` being the minimal value a node extent could be. Split_predicate_node_min_extent(FT me) : m_min_extent(me) {} @@ -138,11 +153,10 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< } }; - /// @} private: - const PolygonMesh& m_pm; + const TriangleMesh& m_pm; VertexPointMap m_vpm; }; From 95e06ddf15c1d670215354301e04d2c56da54deb Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 16 Jan 2024 17:00:00 +0100 Subject: [PATCH 315/520] Update Orthtree/include/CGAL/Orthtree_traits_face_graph.h --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index ccfaaf433bdc..2fa8e714157e 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -124,7 +124,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// @} - /// Recommanded split predicate to pass to `Orthtree::refine()` function so + /// Recommended split predicate to pass to `Orthtree::refine()` function so /// that the octree is refined until a node is either empty or has an extend /// that would be smaller after split than the value provided to the constructor. class Split_predicate_node_min_extent { From 552209ecd2dc62ad9cc39670f82a5d1769c36bb2 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 16 Jan 2024 16:20:20 +0000 Subject: [PATCH 316/520] Add the operators in the concept --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 15 +++++++++++++-- .../CGAL/Orthtree_traits_base_for_dimension.h | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 1bb12fa17918..6ff355eba033 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -8,6 +8,7 @@ \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} + \cgalHasModels{CGAL::Orthtree_traits_base_for_dimension< K, DimensionTag >} \cgalHasModelsEnd */ class OrthtreeTraits @@ -34,7 +35,7 @@ class OrthtreeTraits using Node_data = unspecified_type; /*! - * \brief Number-type which can take on values indicating adjacency directions. + * \brief Number type which can take on values indicating adjacency directions. * * Must be able to take on values ranging from 0 to the number of faces of the (hyper)rectangle type, equivalent to 2 * D. */ @@ -43,6 +44,9 @@ class OrthtreeTraits /*! * \brief Functor with an operator to create the bounding box of the root node. * + * Provides the operator: + * `Bbox_d operator()()` + * * The bounding box must enclose all elements contained by the tree. * It may be tight-fitting. The orthtree constructor produces a bounding box surrounding this region. * For traits which assign no data to each node, this can be defined to return a fixed region. @@ -55,7 +59,10 @@ class OrthtreeTraits * Each node of a tree has an associated `Node_data` value. * For most nodes, this is set by `Distribute_node_contents`, but that is not possible for the root node. * Instead, this functor initializes the `Node_data` of the root node. - * It should take no arguments, and return an instance of `Node_data`. + * It takes no arguments, and return an instance of `Node_data`. + * + * Provides the operator: + * Node_data operator()()` * * Typically, the `Node_data` of the root node contains all the elements in the tree. * For a tree in which each node contains an `std::span()` this function would return the span containing all items. @@ -75,6 +82,10 @@ class OrthtreeTraits * \brief Functor which distributes a node's contents to its children. * * The functor takes a node index, a tree reference, and a `Point_d` which is the center of the node. + * + * Provides the operator: + * void operator()(typename Tree::Node_index, Tree&, const Point_d&)` + * * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` * to access the contents of the node and each of its children. * It must distribute the contents of the node to each of its children. diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index eb3b6520ddc6..ae649fc87158 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -31,6 +31,8 @@ namespace CGAL { \tparam K model of `Kernel`. \tparam DimensionTag is a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. + \cgalModels{OrthtreeTraits} + \sa `CGAL::Orthtree_traits_point` \sa `CGAL::Orthtree_traits_face_graph` */ From c5a186b9039455224eea093273a4e3e25e4a6b81 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 10:56:22 +0000 Subject: [PATCH 317/520] Add Even_odd_rule --- .../doc/Polygon_repair/Polygon_repair.txt | 13 ++-- .../Polygon_repair/repair_polygon_2.cpp | 2 +- .../CGAL/Polygon_repair/Polygon_repair.h | 61 ++++++++++++------- ...iangulation_with_even_odd_constraints_2.h} | 32 +++++----- .../test/Polygon_repair/clipart.cpp | 4 +- .../Polygon_repair/draw_test_polygons.cpp | 4 +- .../test/Polygon_repair/exact_test.cpp | 8 +-- .../Polygon_repair/repair_polygon_2_test.cpp | 6 +- .../test/Polygon_repair/validate_wkt.cpp | 1 + .../write_labeled_triangulation.cpp | 4 +- .../write_repaired_polygons.cpp | 4 +- 11 files changed, 78 insertions(+), 61 deletions(-) rename Polygon_repair/include/CGAL/Polygon_repair/internal/{Triangulation_with_odd_even_constraints_2.h => Triangulation_with_even_odd_constraints_2.h} (82%) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 7f3b427b48bf..54edf8f9898b 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -17,7 +17,7 @@ or hole), and reconstructs the polygon(s) represented by the arrangement. The method returns valid output stored in a multipolygon with holes. Different arrangement and labelling heuristics are possible, but -currently only the odd-even heuristic is implemented in the package. +currently only the even-odd heuristic is implemented in the package. This heuristic results in areas that are alternately assigned as polygon interiors and exterior/holes each time that an input edge is passed. It does not distinguish between edges that are part of outer boundaries @@ -104,14 +104,14 @@ or multipolygon is merely used as a container of input line segments. These line segments are added to the arrangement as edges. Internally, this is done using a constrained triangulation where they are added as constraints. -With the odd-even heuristic, only the edges that are present an odd number of +With the even-odd heuristic, only the edges that are present an odd number of times in the input will be edges in the final arrangement. When these edges are only partially overlapping, only the parts that overlap an odd number of times will be edges in the final arrangement. This procedure is done in two steps: 1. preprocessing to eliminate identical edges that are present an even number of times, and 2. adding edges incrementally -while applying an odd-even counting mechanism, which erases existing (parts of) +while applying an even-odd counting mechanism, which erases existing (parts of) edges when new overlapping ones are added. \subsection SubsectionPolygonRepair_Labeling Labeling @@ -120,7 +120,7 @@ First, the polygon exterior is labeled. For this, all of the faces that can be accessed from the exterior without passing through an edge are labeled as exterior faces. -Then, all other faces are labeled. For the odd-even heuristic, the label applied +Then, all other faces are labeled. For the even-odd heuristic, the label applied alternates between polygon interior and hole every time that an edge is passed. \subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the Multipolygon @@ -150,7 +150,8 @@ with holes has zero holes and extract these if needed. \subsection SubsectionPolygonRepair_Repair Repairing a (Multi)polygon It is possible to repair a polygon, polygon with holes or multipolygon with holes -using the odd-even rule by calling the `Polygon_repair::repair_odd_even()` function +using the even-odd rule by calling the `Polygon_repair::repair()` function +giving as second argument the `Polygon_repair::Even_odd_rule` as shown in the following example. This function returns a repaired multipolygon with holes. \cgalExample{Polygon_repair/repair_polygon_2.cpp} @@ -177,7 +178,7 @@ The polygon repair method as originally developed is described by Ledoux et al. prepair software. This package is a reimplementation of the method with a new approach to label and reconstruct the multipolygons. It also incorporates improvements later -added to prepair, such as the application of the odd-even counting heuristic +added to prepair, such as the application of the even-odd counting heuristic to edges, which enables correct counting even on partially overlapping edges. Ken Arroyo Ohori developed this package during the Google Summer of diff --git a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp index ed48aabf5933..f2f9d3302357 100644 --- a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 pin; CGAL::IO::read_polygon_WKT(in, pin); - Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair_odd_even(pin); + Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair(pin, CGAL::Polygon_repair::Even_odd_rule()); if (mp.number_of_polygons_with_holes() > 1) { CGAL::IO::write_multi_polygon_WKT(std::cout, mp); } else { diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 81e1090af03b..8010387d4ea1 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -18,9 +18,10 @@ #include #include #include +#include #include -#include +#include namespace CGAL { @@ -30,37 +31,55 @@ template class Polygon_repair; /// \ingroup PkgPolygonRepairFunctions -/// Repair a polygon without holes using the odd-even heuristic +/// Repair a polygon without holes using +template +Multipolygon_with_holes_2 repair(const Polygon_2& , Rule rule) { + CGAL_assertion(false); // rule not implemented + return Multipolygon_with_holes_2(); + } + template -Multipolygon_with_holes_2 repair_odd_even(const Polygon_2& p) { +Multipolygon_with_holes_2 repair(const Polygon_2& p, Even_odd_rule) { CGAL::Polygon_repair::Polygon_repair pr; - pr.add_to_triangulation_odd_even(p); + pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation_odd_even(); + pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } /// \ingroup PkgPolygonRepairFunctions -/// Repair a polygon with holes using the odd-even heuristic +/// Repair a polygon with holes +template +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule rule) { + CGAL_assertion(false); // rule not implemented + return Multipolygon_with_holes_2(); +} + template -Multipolygon_with_holes_2 repair_odd_even(const Polygon_with_holes_2& p) { +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Even_odd_rule) { CGAL::Polygon_repair::Polygon_repair pr; - pr.add_to_triangulation_odd_even(p); + pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation_odd_even(); + pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } /// \ingroup PkgPolygonRepairFunctions -/// Repair a multipolygon with holes using the odd-even heuristic +/// Repair a multipolygon with holes +template +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp, Rule rule) { + CGAL_assertion(false); // rule not implemented + return Multipolygon_with_holes_2(); +} + template -Multipolygon_with_holes_2 repair_odd_even(const Multipolygon_with_holes_2& mp) { +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp, Even_odd_rule) { CGAL::Polygon_repair::Polygon_repair pr; - pr.add_to_triangulation_odd_even(mp); + pr.add_to_triangulation_even_odd(mp); if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation_odd_even(); + pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); } return pr.multipolygon(); } @@ -281,7 +300,7 @@ class Polygon_repair { CGAL::Exact_predicates_tag, CGAL::Exact_intersections_tag>::type; using Constrained_Delaunay_triangulation = CGAL::Constrained_Delaunay_triangulation_2; - using Triangulation = internal::Triangulation_with_odd_even_constraints_2; + using Triangulation = internal::Triangulation_with_even_odd_constraints_2; // TODO: Edge_map and Vertex_map use std::set and set::map with exact kernels since Point_2 can't be hashed otherwise using Edge_map = typename std::conditional::value, std::unordered_set, @@ -333,7 +352,7 @@ class Polygon_repair { /// @{ // Add edges of the polygon to the triangulation - void add_to_triangulation_odd_even(const Polygon_2& polygon) { + void add_to_triangulation_even_odd(const Polygon_2& polygon) { // Get unique edges for (auto const& edge: polygon.edges()) { @@ -369,12 +388,12 @@ class Polygon_repair { // Insert edges for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second); + t.even_odd_insert_constraint(edge.first, edge.second); } } // Add edges of the polygon to the triangulation - void add_to_triangulation_odd_even(const Polygon_with_holes_2& polygon) { + void add_to_triangulation_even_odd(const Polygon_with_holes_2& polygon) { // Get unique edges for (auto const& edge: polygon.outer_boundary().edges()) { @@ -419,12 +438,12 @@ class Polygon_repair { // Insert edges for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second); + t.even_odd_insert_constraint(edge.first, edge.second); } } // Add edges of the polygon to the triangulation - void add_to_triangulation_odd_even(const Multipolygon_with_holes_2& multipolygon) { + void add_to_triangulation_even_odd(const Multipolygon_with_holes_2& multipolygon) { // Get unique edges for (auto const& polygon: multipolygon.polygons_with_holes()) { @@ -471,7 +490,7 @@ class Polygon_repair { // Insert edges for (auto const& edge: edges_to_insert) { - t.odd_even_insert_constraint(edge.first, edge.second); + t.even_odd_insert_constraint(edge.first, edge.second); } } @@ -507,7 +526,7 @@ class Polygon_repair { } // Label triangles in triangulation - void label_triangulation_odd_even() { + void label_triangulation_even_odd() { // Simplify collinear edges (gets rid of order dependency) for (auto vertex: t.all_vertex_handles()) { diff --git a/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_odd_even_constraints_2.h b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_even_odd_constraints_2.h similarity index 82% rename from Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_odd_even_constraints_2.h rename to Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_even_odd_constraints_2.h index 563527a48fb1..e75be739e613 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_odd_even_constraints_2.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/internal/Triangulation_with_even_odd_constraints_2.h @@ -9,8 +9,8 @@ // // Author(s) : Ken Arroyo Ohori -#ifndef CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H -#define CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H +#ifndef CGAL_TRIANGULATION_WITH_EVEN_ODD_CONSTRAINTS_2_H +#define CGAL_TRIANGULATION_WITH_EVEN_ODD_CONSTRAINTS_2_H #include #include @@ -20,11 +20,8 @@ namespace Polygon_repair { namespace internal { template -class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { +class Triangulation_with_even_odd_constraints_2 : public Triangulation_ { public: - /// \name Definition - - /// @{ using Base_triangulation = Triangulation_; using Point = typename Triangulation_::Point; using Edge = typename Triangulation_::Edge; @@ -33,13 +30,12 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { using List_edges = typename Triangulation_::List_edges; using List_faces = typename Triangulation_::List_faces; using All_faces_iterator = typename Triangulation_::All_faces_iterator; - /// @} class Interior_tester { - const Triangulation_with_odd_even_constraints_2 *t; + const Triangulation_with_even_odd_constraints_2 *t; public: Interior_tester() {} - Interior_tester(const Triangulation_with_odd_even_constraints_2 *tr) : t(tr) {} + Interior_tester(const Triangulation_with_even_odd_constraints_2 *tr) : t(tr) {} bool operator()(const All_faces_iterator & fit) const { return fit->label() < 1; @@ -66,7 +62,7 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { } // Add constraint from va to vb using the odd-even rule - void odd_even_insert_constraint(Vertex_handle va, Vertex_handle vb) { + void even_odd_insert_constraint(Vertex_handle va, Vertex_handle vb) { // Degenerate edge if (va == vb) return; @@ -80,7 +76,7 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { if (Base_triangulation::is_constrained(Edge(incident_face, opposite_vertex))) { Base_triangulation::remove_constrained_edge(incident_face, opposite_vertex); } else Base_triangulation::mark_constraint(incident_face, opposite_vertex); - if (vc != vb) odd_even_insert_constraint(vc, vb); // process edges along [vc, vb] + if (vc != vb) even_odd_insert_constraint(vc, vb); // process edges along [vc, vb] return; } @@ -90,24 +86,24 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { Vertex_handle intersection; if (Base_triangulation::find_intersected_faces(va, vb, intersected_faces, conflict_boundary_ab, conflict_boundary_ba, intersection)) { if (intersection != va && intersection != vb) { - odd_even_insert_constraint(va, intersection); - odd_even_insert_constraint(intersection, vb); - } else odd_even_insert_constraint(va, vb); + even_odd_insert_constraint(va, intersection); + even_odd_insert_constraint(intersection, vb); + } else even_odd_insert_constraint(va, vb); return; } // Otherwise Base_triangulation::triangulate_hole(intersected_faces, conflict_boundary_ab, conflict_boundary_ba); if (intersection != vb) { - odd_even_insert_constraint(intersection, vb); + even_odd_insert_constraint(intersection, vb); } } // Add constraint from pa to pb using the odd-even rule - void odd_even_insert_constraint(Point pa, Point pb) { + void even_odd_insert_constraint(Point pa, Point pb) { Vertex_handle va = insert(pa); Vertex_handle vb = insert(pb, va->face()); // vb is likely close to va - odd_even_insert_constraint(va, vb); + even_odd_insert_constraint(va, vb); } // Starts at an arbitrary interior face @@ -128,4 +124,4 @@ class Triangulation_with_odd_even_constraints_2 : public Triangulation_ { } // namespace Polygon_repair } // namespace CGAL -#endif // CGAL_TRIANGULATION_WITH_ODD_EVEN_CONSTRAINTS_2_H +#endif // CGAL_TRIANGULATION_WITH_EVEN_ODD_CONSTRAINTS_2_H diff --git a/Polygon_repair/test/Polygon_repair/clipart.cpp b/Polygon_repair/test/Polygon_repair/clipart.cpp index b5337840fd88..3760c477f732 100644 --- a/Polygon_repair/test/Polygon_repair/clipart.cpp +++ b/Polygon_repair/test/Polygon_repair/clipart.cpp @@ -73,12 +73,12 @@ int main(int argc, char* argv[]) { for (auto const& edge: edges_to_insert) { Polygon_repair::Triangulation::Vertex_handle va = pr.triangulation().insert(edge.first, search_start); Polygon_repair::Triangulation::Vertex_handle vb = pr.triangulation().insert(edge.second, va->face()); // vb is likely close to va - pr.triangulation().odd_even_insert_constraint(va, vb); + pr.triangulation().even_odd_insert_constraint(va, vb); search_start = vb->face(); } if (pr.triangulation().number_of_faces() > 0) { - pr.label_triangulation_odd_even(); + pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); } Multipolygon_with_holes_2 mp = pr.multipolygon(); diff --git a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp index db22777f457a..ed312122c767 100644 --- a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -35,12 +35,12 @@ int main(int argc, char* argv[]) { if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); } CGAL::draw(p); - rmp = CGAL::Polygon_repair::repair_odd_even(p); + rmp = CGAL::Polygon_repair::repair(p, CGAL::Polygon_repair::Even_odd_rule()); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); CGAL::draw(mp); - rmp = CGAL::Polygon_repair::repair_odd_even(mp); + rmp = CGAL::Polygon_repair::repair(mp, CGAL::Polygon_repair::Even_odd_rule()); } std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp index e0263867fa11..bfa34ab36c97 100644 --- a/Polygon_repair/test/Polygon_repair/exact_test.cpp +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -28,17 +28,17 @@ int main(int argc, char* argv[]) { CGAL::draw(p); Polygon_repair pr; for (auto const edge: p.outer_boundary().edges()) { - pr.triangulation().odd_even_insert_constraint(edge.source(), edge.target()); + pr.triangulation().even_odd_insert_constraint(edge.source(), edge.target()); } int spikes = 20; for (auto const& hole: p.holes()) { for (auto const edge: hole.edges()) { if (spikes-- <= 0) break; - pr.triangulation().odd_even_insert_constraint(edge.source(), edge.target()); + pr.triangulation().even_odd_insert_constraint(edge.source(), edge.target()); } } - pr.label_triangulation_odd_even(); + pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); - rmp = CGAL::Polygon_repair::repair_odd_even(p); + rmp = CGAL::Polygon_repair::repair(p, CGAL::Polygon_repair::Even_odd_rule()); std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index b36caa502b0b..055e8c15a0b4 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -31,18 +31,18 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p; if (in != "POLYGON()") { // maybe should be checked in WKT reader CGAL::IO::read_polygon_WKT(iss, p); - } rmp = CGAL::Polygon_repair::repair_odd_even(p); + } rmp = CGAL::Polygon_repair::repair(p, CGAL::Polygon_repair::Even_odd_rule()); } else if (in.find("MULTIPOLYGON") == 0) { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); - rmp = CGAL::Polygon_repair::repair_odd_even(mp); + rmp = CGAL::Polygon_repair::repair(mp, CGAL::Polygon_repair::Even_odd_rule()); } std::ostringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); // Read reference file std::string ref_path = "data/ref/"; - ref_path += file.path().filename(); + ref_path += file.path().filename().string(); std::ifstream ref_ifs(ref_path); if (ref_ifs.fail()) { std::cout << std::endl << "\tin: " << in << std::endl; diff --git a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp index c4781315c624..ac92bfd9140e 100644 --- a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp +++ b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp index 24837d7c14ad..92d33b2f70f4 100644 --- a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp +++ b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp @@ -20,8 +20,8 @@ int main(int argc, char* argv[]) { CGAL::IO::read_multi_polygon_WKT(ifs, mp); Polygon_repair pr; - pr.add_to_triangulation_odd_even(mp); - pr.label_triangulation_odd_even(); + pr.add_to_triangulation_even_odd(mp); + pr.label_triangulation_even_odd(); std::cout << "{" << std::endl; std::cout << "\t\"type\": \"FeatureCollection\"," << std::endl; diff --git a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp index f326da10c423..183c64bbd384 100644 --- a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp @@ -28,8 +28,8 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 p; CGAL::IO::read_polygon_WKT(iss, p); Polygon_repair pr; - pr.add_to_triangulation_odd_even(p); - pr.label_triangulation_odd_even(); + pr.add_to_triangulation_even_odd(p); + pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); Multipolygon_with_holes_2 rmp = pr.multipolygon(); std::ostringstream oss; From e8909924d59c69aed52d694f64e4acc12790bdc0 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 11:09:36 +0000 Subject: [PATCH 318/520] Fix clipart.cpp --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 4 ++-- Polygon_repair/test/Polygon_repair/clipart.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 8010387d4ea1..8ff40ee27d5f 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -212,14 +212,14 @@ template bool is_valid(const Multipolygon_with_holes_2& multipolygon) { // Validate polygons - for (auto const& polygon: multipolygon.polygons()) { + for (auto const& polygon: multipolygon.polygons_with_holes()) { if (!is_valid(polygon)) return false; } typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation vt; typename CGAL::Polygon_repair::Polygon_repair::Validation_triangulation::Locate_type lt; int li; - for (auto const& polygon: multipolygon.polygons()) { + for (auto const& polygon: multipolygon.polygons_with_holes()) { if (vt.number_of_faces() > 0) { diff --git a/Polygon_repair/test/Polygon_repair/clipart.cpp b/Polygon_repair/test/Polygon_repair/clipart.cpp index 3760c477f732..94516d6e7649 100644 --- a/Polygon_repair/test/Polygon_repair/clipart.cpp +++ b/Polygon_repair/test/Polygon_repair/clipart.cpp @@ -31,7 +31,7 @@ int main(int argc, char* argv[]) { if (file.path().filename().extension() != ".obj") continue; std::cout << "Reading " << file.path().filename() << "..."; - if (std::filesystem::exists(folder_out + "/" + std::string(file.path().stem()) + ".svg")) { + if (std::filesystem::exists(folder_out + "/" + file.path().stem().string() + ".svg")) { std::cout << " skipped: already processed" << std::endl; continue; } @@ -90,7 +90,7 @@ int main(int argc, char* argv[]) { double scale = desired_width/(bbox.xmax()-bbox.xmin()); - std::ofstream ofs(folder_out + "/" + std::string(file.path().stem()) + ".svg"); + std::ofstream ofs(folder_out + "/" + file.path().stem().string() + ".svg"); ofs << "" << std::endl; for (auto const& polygon: mp.polygons_with_holes()) { From ef8d2747ce143fa90c8b583cfbd1ac3d2eaf2381 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 11:18:14 +0000 Subject: [PATCH 319/520] Document the rule --- Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 8ff40ee27d5f..b3453d6146a4 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -27,8 +27,10 @@ namespace CGAL { namespace Polygon_repair { +#ifndef DOXYGEN_RUNNING template class Polygon_repair; +#endif /// \ingroup PkgPolygonRepairFunctions /// Repair a polygon without holes using @@ -289,6 +291,8 @@ bool is_valid(const Multipolygon_with_holes_2& multipolygon) return true; } +#ifndef DOXYGEN_RUNNING + template > class Polygon_repair { public: @@ -694,6 +698,8 @@ class Polygon_repair { typename Triangulation::Face_handle search_start; }; +#endif // DOXYGEN_RUNNING + } // namespace Polygon_repair } // namespace CGAL From 1d8b0171234c03a6821280966278de410b9ba10c Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 11:27:55 +0000 Subject: [PATCH 320/520] Fix package description --- Polygon_repair/doc/Polygon_repair/PackageDescription.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index 74c33e961e1e..6aeb0392d074 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -30,6 +30,8 @@ \cgalClassifedRefPages \cgalCRPSection{Functions} -- `CGAL::Polygon_repair::repair_odd_even()` +- `CGAL::Polygon_repair::repair()` +\cgalCRPSection{Simplification Rules} +- `CGAL::Polygon_repair::Even_odd_rule` */ From db6f210684e012a35e4d5191a258677d5ff545e0 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 11:31:17 +0000 Subject: [PATCH 321/520] Fix one input path --- .../test/Polygon_repair/write_labeled_triangulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp index 92d33b2f70f4..769b56420a19 100644 --- a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp +++ b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp @@ -14,7 +14,7 @@ using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; int main(int argc, char* argv[]) { - std::ifstream ifs("data/nesting-spike.wkt"); + std::ifstream ifs("data/in/nesting-spike.wkt"); Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(ifs, mp); From cdea8cf8bb7f9b63b6377a7d0a7085777aa74bca Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 12:39:46 +0100 Subject: [PATCH 322/520] Update Orthtree/include/CGAL/Orthtree_traits_face_graph.h --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 2fa8e714157e..0dbcbc7d8256 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -125,7 +125,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// @} /// Recommended split predicate to pass to `Orthtree::refine()` function so - /// that the octree is refined until a node is either empty or has an extend + /// that the octree is refined until a node is either empty or has an extent /// that would be smaller after split than the value provided to the constructor. class Split_predicate_node_min_extent { From c21a988f3a8a21323b6c198c0dec508bee0a8de0 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 12:03:20 +0000 Subject: [PATCH 323/520] Add the file --- .../CGAL/Polygon_repair/Even_odd_rule.h | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h new file mode 100644 index 000000000000..769ad2640388 --- /dev/null +++ b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h @@ -0,0 +1,31 @@ +// Copyright (c) 2023 GeometryFactory. +// 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) : Ken Arroyo Ohori + +#ifndef CGAL_POLYGON_REPAIR_EVEN_ODD_RULE_H +#define CGAL_POLYGON_REPAIR_EVEN_ODD_RULE_H + +#include + +namespace CGAL { + +namespace Polygon_repair { + + /*! + \addtogroup PkgPolygonRepairRef + + */ + struct Even_odd_rule {}; + +} // namespace Polygon_repair + +} // namespace CGAL + +#endif // CGAL_POLYGON_REPAIR_EVEN_ODD_RULE_H From 3b003535c717960c6038ef172d6b4a494dbbf0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 17 Jan 2024 10:31:19 +0100 Subject: [PATCH 324/520] WIP --- .../Polygon_mesh_processing/autorefinement.h | 421 ++++++++++++------ 1 file changed, 274 insertions(+), 147 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index a3606f816a7b..1cb0e3aaa4d8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -114,7 +114,6 @@ Segment_inter_type do_coplanar_segments_intersect(std::size_t pi, std::size_t qi, std::size_t ri, std::size_t si, const std::vector& points, - const typename K::Vector_3& /* plane_normal */, const K& k = K()) { typename K::Collinear_are_ordered_along_line_3 cln_order = k.collinear_are_ordered_along_line_3_object(); @@ -249,11 +248,13 @@ do_coplanar_segments_intersect(std::size_t pi, std::size_t qi, return NO_INTERSECTION; } + // imported from Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h +#warning TODO fill the indices correctly template void coplanar_intersections(const std::array& t1, const std::array& t2, - std::vector& inter_pts) + std::vector>& inter_pts) { const typename K::Point_3& p1 = t1[0], q1 = t1[1], r1 = t1[2]; const typename K::Point_3& p2 = t2[0], q2 = t2[1], r2 = t2[2]; @@ -270,15 +271,14 @@ void coplanar_intersections(const std::array& t1, Intersections::internal::intersection_coplanar_triangles_cutoff(r1,p1,q1,2,p2,q2,r2,k,l_inter_pts); //line r1p1 for (const Intersections::internal::Point_on_triangle& pot : l_inter_pts) - inter_pts.push_back( pot.point(p1,q1,r1,p2,q2,r2,k) ); + inter_pts.emplace_back(pot.point(p1,q1,r1,p2,q2,r2,k), 0, 0); } -// imported from Polygon_mesh_processing/internal/Corefinement/intersect_triangle_segment_3.h +// imported from Polygon_mesh_processing/internal/Corefinement/intersect_triangle_and_segment_3.h template -void +std::optional> find_intersection(const typename K::Point_3& p, const typename K::Point_3& q, //segment const typename K::Point_3& a, const typename K::Point_3& b, const typename K::Point_3& c, //triangle - std::vector& inter_pts, bool is_p_coplanar=false, bool is_q_coplanar=false) // note that in coref this was wrt a halfedge not p/q { Orientation ab=orientation(p,q,a,b); @@ -286,54 +286,56 @@ find_intersection(const typename K::Point_3& p, const typename K::Point_3& q, / Orientation ca=orientation(p,q,c,a); if ( ab==POSITIVE || bc==POSITIVE || ca==POSITIVE ) - return; + return std::nullopt; int nb_coplanar=(ab==COPLANAR?1:0) + (bc==COPLANAR?1:0) + (ca==COPLANAR?1:0); - if (is_p_coplanar) - { - inter_pts.push_back(p); - return; - } - if (is_q_coplanar) - { - inter_pts.push_back(q); - return; - } - - if (nb_coplanar!=2) - { - inter_pts.push_back( - typename K::Construct_plane_line_intersection_point_3()(a, b, c, p, q) - ); - } - else + if (nb_coplanar==2) { + // even if a common point is not new it is still needed to be reported so + // that the intersection segment is known. if (ab!=COPLANAR) - { // intersection is c - inter_pts.push_back(c); - return; - } - - if (bc!=COPLANAR) - { + return std::make_pair(c, -3); + else if (bc!=COPLANAR) // intersection is a - inter_pts.push_back(a); - return; + return std::make_pair(a, -1); + else + { + CGAL_assertion(ca!=COPLANAR); + // intersection is b + return std::make_pair(b, -2); } - CGAL_assertion(ca!=COPLANAR); - // intersection is b - inter_pts.push_back(b); } + + typename K::Point_3 ipt = is_p_coplanar ? p : + is_q_coplanar ? q : + typename K::Construct_plane_line_intersection_point_3() + (a, b, c, p, q); + + if (nb_coplanar == 0) + return std::make_pair(ipt, 0); + + + CGAL_assertion(nb_coplanar==1); + + if (ab==COPLANAR) + // intersection is ab + return std::make_pair(ipt, 1); + if (bc==COPLANAR) + // intersection is bc + return std::make_pair(ipt, 2); + CGAL_assertion(ca==COPLANAR); + // intersection is ca + return std::make_pair(ipt, 3); } template -void test_edge(const typename K::Point_3& p, const typename K::Point_3& q, - const typename K::Point_3& a, const typename K::Point_3& b, const typename K::Point_3& c, - const Orientation abcp, - const Orientation abcq, - std::vector& inter_pts) +std::optional> +test_edge(const typename K::Point_3& p, const typename K::Point_3& q, + const typename K::Point_3& a, const typename K::Point_3& b, const typename K::Point_3& c, + const Orientation abcp, + const Orientation abcq) { switch ( abcp ) { case POSITIVE: @@ -341,46 +343,40 @@ void test_edge(const typename K::Point_3& p, const typename K::Point_3& q, case POSITIVE: // the segment lies in the positive open halfspaces defined by the // triangle's supporting plane - break; + return std::nullopt; case NEGATIVE: // p sees the triangle in counterclockwise order - find_intersection(p,q,a,b,c,inter_pts); - break; + return find_intersection(p,q,a,b,c); //case COPLANAR: default: // q belongs to the triangle's supporting plane // p sees the triangle in counterclockwise order - find_intersection(p,q,a,b,c,inter_pts,false,true); + return find_intersection(p,q,a,b,c,false,true); } - break; case NEGATIVE: switch ( abcq ) { case POSITIVE: // q sees the triangle in counterclockwise order - find_intersection(q,p,a,b,c,inter_pts); - break; + return find_intersection(q,p,a,b,c); case NEGATIVE: // the segment lies in the negative open halfspaces defined by the // triangle's supporting plane - break; + return std::nullopt; // case COPLANAR: default: // q belongs to the triangle's supporting plane // p sees the triangle in clockwise order - find_intersection(q,p,a,b,c,inter_pts,true,false); + return find_intersection(q,p,a,b,c,true,false); } - break; default: //case COPLANAR: // p belongs to the triangle's supporting plane switch ( abcq ) { case POSITIVE: // q sees the triangle in counterclockwise order - find_intersection(q,p,a,b,c,inter_pts,false, true); - break; + return find_intersection(q,p,a,b,c,false, true); case NEGATIVE: // q sees the triangle in clockwise order - find_intersection(p,q,a,b,c,inter_pts,true); - break; + return find_intersection(p,q,a,b,c,true); //case COPLANAR: default: // the segment is coplanar with the triangle's supporting plane @@ -392,7 +388,7 @@ void test_edge(const typename K::Point_3& p, const typename K::Point_3& q, // nothing done as coplanar case handle in collect_intersections // and other intersection points will be collected with non-coplanar edges //} - break; + return std::nullopt; } } } @@ -400,7 +396,7 @@ void test_edge(const typename K::Point_3& p, const typename K::Point_3& q, template bool collect_intersections(const std::array& t1, const std::array& t2, - std::vector& inter_pts) + std::vector>& inter_pts) { // test edges of t1 vs t2 std::array ori; @@ -409,6 +405,7 @@ bool collect_intersections(const std::array& t1, if (ori[0]== COPLANAR && ori[1]==COPLANAR && ori[2]==COPLANAR) { + coplanar_intersections(t1, t2, inter_pts); #ifdef CGAL_AUTOREF_DEBUG_DEPTH for (auto p : inter_pts) @@ -421,7 +418,17 @@ bool collect_intersections(const std::array& t1, for (int i=0; i<3; ++i) { int j=(i+1)%3; - test_edge(t1[i], t1[j], t2[0], t2[1], t2[2], ori[i], ori[j], inter_pts); + std::optional > opt = + test_edge(t1[i], t1[j], t2[0], t2[1], t2[2], ori[i], ori[j]); + if (opt) + { + if (ori[i]==COPLANAR) + inter_pts.emplace_back(opt->first, -(i+1), opt->second); + else if (ori[j]==COPLANAR) + inter_pts.emplace_back(opt->first, -(j+1), opt->second); + else + inter_pts.emplace_back(opt->first, i+1 , opt->second); + } } // test edges of t2 vs t1 @@ -430,12 +437,23 @@ bool collect_intersections(const std::array& t1, for (int i=0; i<3; ++i) { int j=(i+1)%3; - test_edge(t2[i], t2[j], t1[0], t1[1], t1[2], ori[i], ori[j], inter_pts); + std::optional > opt = + test_edge(t2[i], t2[j], t1[0], t1[1], t1[2], ori[i], ori[j]); + if (opt) + { + if (ori[i]==COPLANAR) + inter_pts.emplace_back(opt->first, opt->second, -(i+1)); + else if (ori[j]==COPLANAR) + inter_pts.emplace_back(opt->first, opt->second, -(j+1)); + else + inter_pts.emplace_back(opt->first, opt->second, i+1 ); + } } + #warning TODO get rid of sort and unique calls // because we don't handle intersection type and can have edge-edge edge-vertex duplicates - std::sort(inter_pts.begin(), inter_pts.end()); - auto last = std::unique(inter_pts.begin(), inter_pts.end()); + std::sort(inter_pts.begin(), inter_pts.end(), [](auto p, auto q){return get<0>(p)(q);}); + auto last = std::unique(inter_pts.begin(), inter_pts.end(), [](auto p, auto q){return get<0>(p)==get<0>(q);}); inter_pts.erase(last, inter_pts.end()); #ifdef CGAL_AUTOREF_DEBUG_DEPTH @@ -446,45 +464,24 @@ bool collect_intersections(const std::array& t1, return false; } +//TODO: rename struct +struct Triangle_data +{ + std::vector points; + std::vector> segments; + std::vector segment_input_triangle_ids; +}; + template void generate_subtriangles(std::size_t ti, - std::vector>& segments, - std::vector& points, - const std::vector& in_triangle_ids, + Triangle_data& triangle_data, const std::set >& intersecting_triangles, const std::set >& coplanar_triangles, const std::vector>& triangles, PointVector& new_triangles ) { - typedef CGAL::Projection_traits_3 P_traits; - typedef CGAL::No_constraint_intersection_tag Itag; - - typedef CGAL::Constrained_Delaunay_triangulation_2 CDT_2; - //typedef CGAL::Constrained_triangulation_plus_2 CDT; - typedef CDT_2 CDT; - - const std::array& t = triangles[ti]; - - // positive triangle normal - typename EK::Vector_3 n = normal(t[0], t[1], t[2]); - typename EK::Point_3 o(CGAL::ORIGIN); - - bool orientation_flipped = false; - if ( typename EK::Less_xyz_3()(o+n,o) ) - { - n=-n; - orientation_flipped = true; - } - - P_traits cdt_traits(n); - CDT cdt(cdt_traits); - cdt.insert_outside_affine_hull(t[0]); - cdt.insert_outside_affine_hull(t[1]); - typename CDT::Vertex_handle v = cdt.tds().insert_dim_up(cdt.infinite_vertex(), orientation_flipped); - v->set_point(t[2]); - #ifdef CGAL_AUTOREFINE_DEBUG_COUNTERS struct Counter { @@ -518,7 +515,13 @@ void generate_subtriangles(std::size_t ti, #define CGAL_AUTOREF_COUNTER_INSTRUCTION(X) #endif - // pre-compute segment intersections + #warning TODO + + std::vector& points=triangle_data.points; + std::vector>& segments=triangle_data.segments; + std::vector& in_triangle_ids=triangle_data.segment_input_triangle_ids; + +// pre-compute segment intersections if (!segments.empty()) { std::size_t nbs = segments.size(); @@ -528,7 +531,7 @@ void generate_subtriangles(std::size_t ti, CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer1.start();) std::map point_id_map; - +//TODO: we already have sorted the points while deduplicating segments! for (std::size_t pid=0; pid(segments[i].first, segments[i].second, segments[j].first, segments[j].second, - points, n); + points); CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer5.stop();) switch(seg_inter_type) @@ -739,13 +742,63 @@ void generate_subtriangles(std::size_t ti, auto last = std::unique(segments.begin(), segments.end()); segments.erase(last, segments.end()); +// init CDT + insert points and constraints CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer3.start();) - if (segments.empty()) - cdt.insert(points.begin(), points.end()); - else - cdt.insert_constraints(points.begin(), points.end(), segments.begin(), segments.end()); + typedef CGAL::Projection_traits_3 P_traits; + typedef CGAL::No_constraint_intersection_tag Itag; + + typedef CGAL::Constrained_Delaunay_triangulation_2 CDT_2; + //typedef CGAL::Constrained_triangulation_plus_2 CDT; + typedef CDT_2 CDT; + + const std::array& t = triangles[ti]; + + // positive triangle normal + typename EK::Vector_3 n = normal(t[0], t[1], t[2]); + typename EK::Point_3 o(CGAL::ORIGIN); + + bool orientation_flipped = false; + if ( typename EK::Less_xyz_3()(o+n,o) ) + { + n=-n; + orientation_flipped = true; + } + + std::vector vhandles(triangle_data.points.size()); + P_traits cdt_traits(n); + CDT cdt(cdt_traits); + vhandles[0]=cdt.insert_outside_affine_hull(t[0]); + vhandles[1]=cdt.insert_outside_affine_hull(t[1]); + vhandles[2] = cdt.tds().insert_dim_up(cdt.infinite_vertex(), orientation_flipped); + vhandles[2]->set_point(t[2]); + + // insert points and fill vhandles + std::vector indices(triangle_data.points.size()-3); + std::iota(indices.begin(), indices.end(), 3); + typedef typename Pointer_property_map::type Pmap; + typedef Spatial_sort_traits_adapter_2 Search_traits; + spatial_sort(indices.begin(), indices.end(), + Search_traits(make_property_map(points), cdt_traits)); + + typename CDT::Face_handle hint; + for (std::size_t i : indices) + { + vhandles[i] = cdt.insert(points[i], hint); + hint=vhandles[i]->face(); + } + + for (const std::pair& ids : triangle_data.segments) + { + //TODO remove me + CGAL_assertion(ids.first < vhandles.size()); + CGAL_assertion(ids.second < vhandles.size()); + CGAL_assertion( vhandles[ids.first]!= typename CDT::Vertex_handle() ); + CGAL_assertion( vhandles[ids.second]!= typename CDT::Vertex_handle() ); + cdt.insert_constraint(vhandles[ids.first], vhandles[ids.second]); + } CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer3.stop();) +// extract new triangles for (typename CDT::Face_handle fh : cdt.finite_face_handles()) { if (orientation_flipped) @@ -838,7 +891,8 @@ void autorefine_triangle_soup(PointRange& soup_points, Visitor visitor(choose_parameter(get_parameter(np, internal_np::visitor))); - constexpr bool parallel_execution = std::is_same_v; + //~ constexpr bool parallel_execution = std::is_same_v; + constexpr bool parallel_execution = false; #ifndef CGAL_LINKED_WITH_TBB static_assert (!parallel_execution, @@ -895,21 +949,24 @@ void autorefine_triangle_soup(PointRange& soup_points, // init the vector of triangles used for the autorefinement of triangles typedef CGAL::Exact_predicates_exact_constructions_kernel EK; - std::vector< std::array > triangles(tiid+1); + std::vector< std::array > triangles(tiid+1); // TODO get rid of triangles and use all_triangle_data + // vector of data for refining triangles + std::vector all_triangle_data(triangles.size()); Cartesian_converter to_exact; for(Input_TID f : intersected_faces) { - triangles[tri_inter_ids[f]]= CGAL::make_array( + std::size_t tid=tri_inter_ids[f]; + triangles[tid]= CGAL::make_array( to_exact( get(pm, soup_points[soup_triangles[f][0]]) ), to_exact( get(pm, soup_points[soup_triangles[f][1]]) ), to_exact( get(pm, soup_points[soup_triangles[f][2]]) ) ); + all_triangle_data[tid].points.resize(3); + all_triangle_data[tid].points[0]=triangles[tri_inter_ids[f]][0]; + all_triangle_data[tid].points[1]=triangles[tri_inter_ids[f]][1]; + all_triangle_data[tid].points[2]=triangles[tri_inter_ids[f]][2]; } - std::vector< std::vector > > all_segments(triangles.size()); - std::vector< std::vector > all_points(triangles.size()); - std::vector< std::vector > all_in_triangle_ids(triangles.size()); - CGAL_PMP_AUTOREFINE_VERBOSE("compute intersections"); #ifdef CGAL_AUTOREF_USE_DEBUG_PARALLEL_TIMERS Real_timer t; @@ -928,7 +985,7 @@ void autorefine_triangle_soup(PointRange& soup_points, const std::array& t1 = triangles[i1]; const std::array& t2 = triangles[i2]; - std::vector inter_pts; + std::vector> inter_pts; bool triangles_are_coplanar = autorefine_impl::collect_intersections(t1, t2, inter_pts); CGAL_assertion( @@ -937,27 +994,73 @@ void autorefine_triangle_soup(PointRange& soup_points, if (!inter_pts.empty()) { + auto get_ipt_id1 = [](const std::tuple& tpl) + { + if (get<1>(tpl)<0) return -get<1>(tpl)-1; + }; + auto get_ipt_id2 = [](const std::tuple& tpl) + { + if (get<2>(tpl)<0) return -get<2>(tpl)-1; + }; + + std::size_t nbi = inter_pts.size(); switch(nbi) { + #warning TODO use inter pt info case 1: - all_points[i1].push_back(inter_pts[0]); - all_points[i2].push_back(inter_pts[0]); + if (get<1>(inter_pts[0])>=0) + all_triangle_data[i1].points.push_back(get<0>(inter_pts[0])); + if (get<2>(inter_pts[0])>=0) + all_triangle_data[i2].points.push_back(get<0>(inter_pts[0])); break; case 2: - all_segments[i1].push_back(CGAL::make_array(inter_pts[0], inter_pts[1])); - all_segments[i2].push_back(CGAL::make_array(inter_pts[0], inter_pts[1])); - all_in_triangle_ids[i1].push_back(i2); - all_in_triangle_ids[i2].push_back(i1); + { + std::size_t src_id=get<1>(inter_pts[0])<0?(-get<1>(inter_pts[0])-1):all_triangle_data[i1].points.size(); + if (get<1>(inter_pts[0])>=0) + all_triangle_data[i1].points.push_back(get<0>(inter_pts[0])); + std::size_t tgt_id=get<1>(inter_pts[1])<0?(-get<1>(inter_pts[1])-1):all_triangle_data[i1].points.size(); + if (get<1>(inter_pts[1])>=0) + all_triangle_data[i1].points.push_back(get<0>(inter_pts[1])); + all_triangle_data[i1].segments.emplace_back(src_id, tgt_id); + all_triangle_data[i1].segment_input_triangle_ids.push_back(i2); + // + src_id=get<2>(inter_pts[0])<0?(-get<2>(inter_pts[0])-1):all_triangle_data[i2].points.size(); + if (get<2>(inter_pts[0])>=0) + all_triangle_data[i2].points.push_back(get<0>(inter_pts[0])); + tgt_id=get<2>(inter_pts[1])<0?(-get<2>(inter_pts[1])-1):all_triangle_data[i2].points.size(); + if (get<2>(inter_pts[1])>=0) + all_triangle_data[i2].points.push_back(get<0>(inter_pts[1])); + all_triangle_data[i2].segments.emplace_back(src_id, tgt_id); + all_triangle_data[i2].segment_input_triangle_ids.push_back(i1); + } break; default: + { + std::vector ipt_ids1(nbi+1), ipt_ids2(nbi+1); + all_triangle_data[i1].segment_input_triangle_ids.insert( + all_triangle_data[i1].segment_input_triangle_ids.end(), nbi, i2); + all_triangle_data[i2].segment_input_triangle_ids.insert( + all_triangle_data[i2].segment_input_triangle_ids.end(), nbi, i1); + for (std::size_t i=0;i(inter_pts[i])<0?(-get<1>(inter_pts[i])-1):all_triangle_data[i1].points.size(); + std::size_t id2=get<2>(inter_pts[i])<0?(-get<2>(inter_pts[i])-1):all_triangle_data[i2].points.size(); + if (get<1>(inter_pts[i])>=0) all_triangle_data[i1].points.push_back(get<0>(inter_pts[i])); + if (get<2>(inter_pts[i])>=0) all_triangle_data[i2].points.push_back(get<0>(inter_pts[i])); + ipt_ids1[i]=id1; + ipt_ids2[i]=id2; } + ipt_ids1.back()=ipt_ids1.front(); + ipt_ids2.back()=ipt_ids2.front(); + + for (std::size_t i=0;i>> all_segments_ids(all_segments.size()); - auto deduplicate_inserted_segments = [&](std::size_t ti) { - if (!all_segments[ti].empty()) + if (!all_triangle_data[ti].segments.empty()) { - std::map point_id_map; + std::vector& points=all_triangle_data[ti].points; + std::vector>& segments=all_triangle_data[ti].segments; + std::vector indices(points.size()-3); + std::iota(indices.begin(), indices.end(),3); - auto get_point_id = [&](const EK::Point_3& pt) - { - auto insert_res = point_id_map.insert(std::make_pair(pt, all_points[ti].size())); - if (insert_res.second) - all_points[ti].push_back(pt); - return insert_res.first->second; - }; + std::sort(indices.begin(), indices.end(), [&points](std::size_t i, std::size_t j) + { return points[i] id_map(points.size()); + id_map[0]=0; + id_map[1]=1; + id_map[2]=2; + std::vector unique_ids; + unique_ids.reserve(indices.size()); - if (!all_points[ti].empty()) + //make points unique + create mapping between indices + for (std::size_t i=0; i tmp; - tmp.swap(all_points[ti]); - for (const EPoint_3& pt : tmp) - get_point_id(pt); + std::size_t new_id=unique_ids.size()+3; + unique_ids.push_back(indices[i]); + id_map[indices[i]]=new_id; + while(i+1!=indices.size() && points[indices[i]]==points[indices[i+1]]) + { + id_map[indices[++i]]=new_id; + } } - std::size_t nbs = all_segments[ti].size(); - std::vector> filtered_segments; + //~ if (unique_ids.size()==indices.size()) + //~ // TODO: do we want to keep points sorted? if yes always swap twice + //~ return; // no duplicates + + // now make points unique + using EPoint_3 = EK::Point_3; // workaround for MSVC 2022 bug + std::vector tmp; + tmp.reserve(unique_ids.size()+3); + tmp.push_back(points[0]); + tmp.push_back(points[1]); + tmp.push_back(points[2]); + for(std::size_t i : unique_ids) + tmp.push_back(points[i]); + tmp.swap(points); + + // now make segments unique + std::size_t nbs = segments.size(); + std::vector> filtered_segments; std::vector filtered_in_triangle_ids; filtered_segments.reserve(nbs); + filtered_in_triangle_ids.reserve(nbs); std::set> segset; for (std::size_t si=0; sisegments[si].first); + CGAL_assertion(id_map.size()>segments[si].second); + std::pair seg_ids = + CGAL::make_sorted_pair(id_map[segments[si].first], id_map[segments[si].second]); + if (segset.insert(seg_ids).second) { - all_segments_ids[ti].emplace_back(src_id, tgt_id); - filtered_in_triangle_ids.push_back(all_in_triangle_ids[ti][si]); + filtered_segments.push_back(seg_ids); + filtered_in_triangle_ids.push_back(all_triangle_data[ti].segment_input_triangle_ids[si]); } } - if (all_segments_ids[ti].size()!=nbs) - filtered_in_triangle_ids.swap(all_in_triangle_ids[ti]); + filtered_in_triangle_ids.swap(all_triangle_data[ti].segment_input_triangle_ids); + filtered_segments.swap(segments); + + CGAL_assertion(points.size()==std::set(points.begin(), points.end()).size()); } }; @@ -1062,11 +1189,11 @@ void autorefine_triangle_soup(PointRange& soup_points, auto refine_triangles = [&](std::size_t ti) { - if (all_segments[ti].empty() && all_points[ti].empty()) + if (all_triangle_data[ti].points.empty()) new_triangles.push_back({triangles[ti], ti}); else { - autorefine_impl::generate_subtriangles(ti, all_segments_ids[ti], all_points[ti], all_in_triangle_ids[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); } #ifdef CGAL_AUTOREF_USE_PROGRESS_DISPLAY From dbe61d2feb3621491ae09ba8abd8a95a9187e424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 17 Jan 2024 19:33:31 +0100 Subject: [PATCH 325/520] update intersection computation code to always report the complete combinatoric of the intersection points --- .../Triangle_3_Triangle_3_intersection.h | 237 ++++++------------ 1 file changed, 76 insertions(+), 161 deletions(-) diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h index 269c86c3e606..37fc2aa19148 100644 --- a/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h @@ -39,11 +39,15 @@ struct Point_on_triangle { // triangle points are not stored in this class but are expected // to always be passed in the same order. For a triangle pqr, - // edge 0 is pq, edge 1 qr and edge 2 rp. Point 0 is p, 1 is q and 2 is r. + // edge 1 is pq, edge 2 qr and edge 3 rp. Point -1 is p, -2 is q and -3 is r. + // + // (0,-) vertex of t2 inside t1 + // (-,-) common point of t1 and t2 + // (-,0) vertex of t1 inside t2 + // (-,+) vertex of t1 on an edge of t2 + // (+,+) edge of t1 intersecting edge of t2 + // (+,-) vertex of t2 on an edge of t1 // - // (id, -1) point on t1 - // (-1, id) point on t2 - // (id1, id2) intersection of edges std::pair t1_t2_ids; boost::container::flat_set extra_t1; // store other ids of edges containing the point typename Kernel::FT alpha; // @@ -60,16 +64,16 @@ struct Point_on_triangle { switch(id) { - case 0: + case -1: return p; - case 1: + case -2: return q; default: return r; } } - Point_on_triangle(int i1, int i2=-1, typename Kernel::FT alpha = 0.) // TODO add global zero()? + Point_on_triangle(int i1, int i2, typename Kernel::FT alpha = 0.) // TODO add global zero()? : t1_t2_ids(i1,i2) , alpha(alpha) {} @@ -85,20 +89,20 @@ struct Point_on_triangle const typename Kernel::Point_3& r2, const Kernel& k) const { - if (t1_t2_ids.first!=-1) + if (t1_t2_ids.first!=0 && t1_t2_ids.second >=0) { - if (t1_t2_ids.second==-1) + if (t1_t2_ids.first<0) return (edge_id1==t1_t2_ids.first || (edge_id1+1)%3==t1_t2_ids.first) ? ZERO:POSITIVE; // it is a point on t1 // this is an intersection point if (t1_t2_ids.first==edge_id1) return ZERO; - if (t1_t2_ids.first==(edge_id1+1)%3) + if (t1_t2_ids.first-1==(edge_id1)%3) { if (alpha==0) return ZERO; return alpha>=0 ? POSITIVE:NEGATIVE; } - CGAL_assertion((t1_t2_ids.first+1)%3==edge_id1); + CGAL_assertion((t1_t2_ids.first)%3==edge_id1-1); if (alpha==1) return ZERO; return alpha<=1?POSITIVE:NEGATIVE; } @@ -111,9 +115,13 @@ struct Point_on_triangle } } + int id1() const { return t1_t2_ids.first; } int id2() const { return t1_t2_ids.second; } + void set_id1(int i) { t1_t2_ids.first=i; } + void set_id2(int i) { t1_t2_ids.second=i; } + // construct the intersection point from the info stored typename Kernel::Point_3 point(const typename Kernel::Point_3& p1, @@ -124,12 +132,13 @@ struct Point_on_triangle const typename Kernel::Point_3& r2, const Kernel& k) const { - if (t1_t2_ids.first==-1) + if (t1_t2_ids.second<0) return point_from_id(p2,q2,r2,t1_t2_ids.second); - if (t1_t2_ids.second==-1) + if (t1_t2_ids.first<0) return point_from_id(p1,q1,r1,t1_t2_ids.first); - return k.construct_barycenter_3_object()(point_from_id(p1,q1,r1,(t1_t2_ids.first+1)%3), alpha, point_from_id(p1,q1,r1,t1_t2_ids.first)) ; + return k.construct_barycenter_3_object()(point_from_id(p1,q1,r1,-(t1_t2_ids.first)%3), alpha, + point_from_id(p1,q1,r1,-t1_t2_ids.first)) ; } }; @@ -160,153 +169,50 @@ intersection(const Point_on_triangle& p, typename Kernel::Compute_alpha_for_coplanar_triangle_intersection_3 compute_alpha = k.compute_alpha_for_coplanar_triangle_intersection_3_object(); typedef Point_on_triangle Pot; - switch(p.id1()) + + auto common_edge_id = [](int pid, int qid) { - case -1: + if (pid==0 || qid==0) return 0; + if (pid<0) { - switch(q.id1()) + if (qid<0) { - case -1: // A: (-1, ip2) - (-1, iq2) - { - CGAL_assertion((p.id2()+1)%3 == q.id2() || (q.id2()+1)%3 == p.id2()); -// CGAL_assertion(p.extra_t1.empty() && q.extra_t1.empty()); // TMP to see if it's worth implementing special case -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 1\n"; -#endif - typename Kernel::FT alpha = compute_alpha(p1, q1, - Pot::point_from_id(p2, q2, r2, p.id2()), - Pot::point_from_id(p2, q2, r2, q.id2())); - int id2 = (p.id2()+1)%3 == q.id2() ? p.id2() : q.id2(); - return Point_on_triangle(edge_id_t1, id2, alpha); // intersection with an original edge of t2 - } - default: - if (q.id2()!=-1) // B: (-1, ip2) - (iq1, iq2) - { - if (p.id2() == q.id2() || p.id2() == (q.id2()+1)%3) - { -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 2\n"; -#endif - // points are on the same edge of t2 --> we shorten an already cut edge - typename Kernel::FT alpha = compute_alpha(p1, q1, - Pot::point_from_id(p2, q2, r2, q.id2()), - Pot::point_from_id(p2, q2, r2, (q.id2()+1)%3)); - - return Point_on_triangle(edge_id_t1, q.id2(), alpha); - } -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 3\n"; -#endif - // point of t1: look for an edge of t1 containing both points - CGAL_assertion( p.extra_t1.count(q.id1())!=0 || p.extra_t1.count(3-q.id1()-edge_id_t1)!=0 ); - int eid1 = p.extra_t1.count(q.id1())!=0 ? q.id1() : 3-q.id1()-edge_id_t1; - return Point_on_triangle((eid1+1)%3==edge_id_t1?edge_id_t1:(edge_id_t1+1)%3, -1); // vertex of t1 - } - // C: (-1, ip2) - (iq1, -1) - //vertex of t1, special case t1 edge passed thru a vertex of t2 - CGAL_assertion(edge_id_t1 == 2); -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 4\n"; -#endif - CGAL_assertion(q.id1()==1); - CGAL_assertion(!p.extra_t1.empty()); - return Point_on_triangle(p.extra_t1.count(0)==1?0:2,-1); + return (-pid)%3==-qid-1 ? -pid:-qid; + } + else + { + return (-pid==qid || (qid)%3==-pid-1) ? qid : 0; } } - default: + else { - switch(p.id2()) + if (qid<0) { - case -1: - { - switch(q.id1()) - { - case -1: // G: (ip1, -1) - (-1, iq2) - //vertex of t1, special case t1 edge passed thru a vertex of t2 -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 5\n"; -#endif - CGAL_assertion(edge_id_t1 == 2); - CGAL_assertion(p.id1()==1); - CGAL_assertion(!q.extra_t1.empty()); - return Point_on_triangle(q.extra_t1.count(0)==1?0:2,-1); - default: - { -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 6\n"; -#endif - CGAL_assertion(q.id2()!=-1); // I: (ip1, -1) - (iq2, -1) - //H: (ip1,-1), (iq1, iq2) - CGAL_assertion(edge_id_t1==2); - // p and q are on the same edge of t1 - CGAL_assertion(p.id1()==q.id1() || p.id1()==(q.id1()+1)%3); - return Point_on_triangle((q.id1()+1)%3==edge_id_t1?edge_id_t1:(edge_id_t1+1)%3 , -1); - } - } - } - default: - { - switch(q.id1()) - { - case -1: // D: (ip1, ip2) - (-1, iq2) - { - if (q.id2() == p.id2() || q.id2() == (p.id2()+1)%3) - { -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 7\n"; -#endif - // points are on the same edge of t2 --> we shorten an already cut edge - typename Kernel::FT alpha = compute_alpha(p1, q1, - Pot::point_from_id(p2, q2, r2, p.id2()), - Pot::point_from_id(p2, q2, r2, (p.id2()+1)%3)); - - return Point_on_triangle(edge_id_t1, p.id2(), alpha); - } -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 8\n"; -#endif - // point of t1 - //std::cout << "q.extra_t1: "; for(int qet1 : q.extra_t1) std::cout << " " << qet1; std::cout << "\n"; - CGAL_assertion( q.extra_t1.count(p.id1())!=0 || q.extra_t1.count(3-p.id1()-edge_id_t1)!=0 ); - int eid1 = q.extra_t1.count(p.id1())!=0 ? p.id1() : 3-p.id1()-edge_id_t1; - return Point_on_triangle((eid1+1)%3==edge_id_t1?edge_id_t1:(edge_id_t1+1)%3, -1); // vertex of t1 - } - default: - { - switch(q.id2()) - { - case -1: // F: (ip1, ip2) - (iq1, -1) - { - // p and q are on the same edge of t1 - CGAL_assertion(q.id1()==p.id1() || q.id1()==(p.id1()+1)%3); -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 9\n"; -#endif - return Point_on_triangle((p.id1()+1)%3==edge_id_t1?edge_id_t1:(edge_id_t1+1)%3 , -1); - } - default: // E: (ip1, ip2) - (iq1, iq2) - { - if (p.id2()==q.id2()) - { -#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " -- case 10\n"; -#endif - typename Kernel::FT alpha = compute_alpha(p1, q1, - Pot::point_from_id(p2, q2, r2, q.id2()), - Pot::point_from_id(p2, q2, r2, (q.id2()+1)%3)); - return Point_on_triangle(edge_id_t1, q.id2(), alpha); - } - // we are intersecting an edge of t1 - CGAL_assertion(p.id1()==q.id1() || edge_id_t1==2); - int eid1 = p.id1()==q.id1() ? p.id1() : 1; - return Point_on_triangle((eid1+1)%3==edge_id_t1?edge_id_t1:(edge_id_t1+1)%3, -1); // vertex of t1 - } - } - } - } - } + return (-qid==pid || (pid)%3==-qid-1) ? pid : 0; + } + else + { + return pid==qid ? pid : 0; } } + }; + + int common_eid1 = common_edge_id(p.id1(), q.id1()); + int common_eid2 = common_edge_id(p.id2(), q.id2()); + + if (common_eid1!=0) + { + CGAL_assertion( (common_eid1)%3==edge_id_t1-1 || (edge_id_t1)%3==common_eid1-1 ); + int pid1 = (common_eid1)%3==edge_id_t1-1? -edge_id_t1:-common_eid1; + return Point_on_triangle(pid1, common_eid2); + } + else + { + CGAL_assertion(common_eid2!=0); + typename Kernel::FT alpha = compute_alpha(p1, q1, + Pot::point_from_id(p2, q2, r2, -common_eid2), + Pot::point_from_id(p2, q2, r2, -(common_eid2)%3-1)); + return Point_on_triangle(edge_id_t1, common_eid2, alpha); } } @@ -336,8 +242,17 @@ void intersection_coplanar_triangles_cutoff(const typename Kernel::Point_3& p1, for (Point_on_triangle& pot : inter_pts) { orientations[ &pot ]=pot.orientation(p1,q1,r1,edge_id,p2,q2,r2,k); - if (pot.id1()==-1 && orientations[ &pot ]==COLLINEAR) - pot.extra_t1.insert(edge_id); + if (orientations[ &pot ]==COLLINEAR) + { + if (pot.id1()==0) + pot.set_id1(edge_id); + else + { + CGAL_assertion(pot.id1()>0); + CGAL_assertion((edge_id)%3==pot.id1()-1 || (pot.id1())%3==edge_id-1); + pot.set_id1( (edge_id)%3==pot.id1()-1 ? -pot.id1(): -edge_id ); + } + } } #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION @@ -411,30 +326,30 @@ intersection_coplanar_triangles(const typename K::Triangle_3& t1, r2 = t2.vertex(2); std::list> inter_pts; - inter_pts.push_back(Point_on_triangle(-1,0)); - inter_pts.push_back(Point_on_triangle(-1,1)); - inter_pts.push_back(Point_on_triangle(-1,2)); + inter_pts.push_back(Point_on_triangle(0,-1)); + inter_pts.push_back(Point_on_triangle(0,-2)); + inter_pts.push_back(Point_on_triangle(0,-3)); #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION auto print_points = [&]() { - for(auto p : inter_pts) std::cout << " (" << p.id1() << "," << p.id2() << ",[" << p.alpha << "]) "; std::cout <<"\n"; + for(auto p : inter_pts) {std::cout << " (" << p.id1() << "," << p.id2() << ",[" << p.alpha << "]) ";} std::cout <<"\n"; }; std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif //intersect t2 with the three half planes which intersection defines t1 - intersection_coplanar_triangles_cutoff(p1,q1,r1,0,p2,q2,r2,k,inter_pts); //line pq + intersection_coplanar_triangles_cutoff(p1,q1,r1,1,p2,q2,r2,k,inter_pts); //line pq #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif - intersection_coplanar_triangles_cutoff(q1,r1,p1,1,p2,q2,r2,k,inter_pts); //line qr + intersection_coplanar_triangles_cutoff(q1,r1,p1,2,p2,q2,r2,k,inter_pts); //line qr #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif - intersection_coplanar_triangles_cutoff(r1,p1,q1,2,p2,q2,r2,k,inter_pts); //line rp + intersection_coplanar_triangles_cutoff(r1,p1,q1,3,p2,q2,r2,k,inter_pts); //line rp #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); From b9232b36772dd7ebb1a3cad8773474b02ccb6d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 18 Jan 2024 10:56:36 +0100 Subject: [PATCH 326/520] fix bad indices --- .../internal/Triangle_3_Triangle_3_intersection.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h b/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h index 37fc2aa19148..471904cd97ab 100644 --- a/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h +++ b/Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h @@ -137,7 +137,7 @@ struct Point_on_triangle if (t1_t2_ids.first<0) return point_from_id(p1,q1,r1,t1_t2_ids.first); - return k.construct_barycenter_3_object()(point_from_id(p1,q1,r1,-(t1_t2_ids.first)%3), alpha, + return k.construct_barycenter_3_object()(point_from_id(p1,q1,r1,-(t1_t2_ids.first)%3-1), alpha, point_from_id(p1,q1,r1,-t1_t2_ids.first)) ; } }; @@ -164,7 +164,7 @@ intersection(const Point_on_triangle& p, #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION std::cout << " calling intersection: "; std::cout << " (" << p.id1() << "," << p.id2() << ",[" << p.alpha << "]) -"; - std::cout << " (" << q.id1() << "," << q.id2() << ",[" << q.alpha << "]) || e" << edge_id_t1; + std::cout << " (" << q.id1() << "," << q.id2() << ",[" << q.alpha << "]) || e" << edge_id_t1 << "\n"; #endif typename Kernel::Compute_alpha_for_coplanar_triangle_intersection_3 compute_alpha = k.compute_alpha_for_coplanar_triangle_intersection_3_object(); @@ -333,25 +333,22 @@ intersection_coplanar_triangles(const typename K::Triangle_3& t1, #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION auto print_points = [&]() { + std::cout << " ipts size: " << inter_pts.size() << "\n"; for(auto p : inter_pts) {std::cout << " (" << p.id1() << "," << p.id2() << ",[" << p.alpha << "]) ";} std::cout <<"\n"; }; - std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif //intersect t2 with the three half planes which intersection defines t1 intersection_coplanar_triangles_cutoff(p1,q1,r1,1,p2,q2,r2,k,inter_pts); //line pq #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif intersection_coplanar_triangles_cutoff(q1,r1,p1,2,p2,q2,r2,k,inter_pts); //line qr #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif intersection_coplanar_triangles_cutoff(r1,p1,q1,3,p2,q2,r2,k,inter_pts); //line rp #ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION - std::cout << " ipts size: " << inter_pts.size() << "\n"; print_points(); #endif From 61461b14b3981d3f2582808ddc2ef9f3c06447a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 18 Jan 2024 10:57:17 +0100 Subject: [PATCH 327/520] more debug and fix indices --- .../Polygon_mesh_processing/autorefinement.h | 95 ++++++++++++++----- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index 1cb0e3aaa4d8..50e7c5b361e5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -250,7 +250,6 @@ do_coplanar_segments_intersect(std::size_t pi, std::size_t qi, // imported from Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h -#warning TODO fill the indices correctly template void coplanar_intersections(const std::array& t1, const std::array& t2, @@ -260,18 +259,37 @@ void coplanar_intersections(const std::array& t1, const typename K::Point_3& p2 = t2[0], q2 = t2[1], r2 = t2[2]; std::list> l_inter_pts; - l_inter_pts.push_back(Intersections::internal::Point_on_triangle(-1,0)); - l_inter_pts.push_back(Intersections::internal::Point_on_triangle(-1,1)); - l_inter_pts.push_back(Intersections::internal::Point_on_triangle(-1,2)); + l_inter_pts.push_back(Intersections::internal::Point_on_triangle(0,-1)); + l_inter_pts.push_back(Intersections::internal::Point_on_triangle(0,-2)); + l_inter_pts.push_back(Intersections::internal::Point_on_triangle(0,-3)); +#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION + auto print_points = [&]() + { + std::cout << " ipts size: " << l_inter_pts.size() << "\n"; + for(auto p : l_inter_pts) {std::cout << " (" << p.id1() << "," << p.id2() << ",[" << p.alpha << "]) ";} std::cout <<"\n"; + }; +#endif //intersect t2 with the three half planes which intersection defines t1 K k; - Intersections::internal::intersection_coplanar_triangles_cutoff(p1,q1,r1,0,p2,q2,r2,k,l_inter_pts); //line p1q1 - Intersections::internal::intersection_coplanar_triangles_cutoff(q1,r1,p1,1,p2,q2,r2,k,l_inter_pts); //line q1r1 - Intersections::internal::intersection_coplanar_triangles_cutoff(r1,p1,q1,2,p2,q2,r2,k,l_inter_pts); //line r1p1 +#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION + print_points(); +#endif + Intersections::internal::intersection_coplanar_triangles_cutoff(p1,q1,r1,1,p2,q2,r2,k,l_inter_pts); //line p1q1 +#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION + print_points(); +#endif + Intersections::internal::intersection_coplanar_triangles_cutoff(q1,r1,p1,2,p2,q2,r2,k,l_inter_pts); //line q1r1 +#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION + print_points(); +#endif + Intersections::internal::intersection_coplanar_triangles_cutoff(r1,p1,q1,3,p2,q2,r2,k,l_inter_pts); //line r1p1 +#ifdef CGAL_DEBUG_COPLANAR_T3_T3_INTERSECTION + print_points(); +#endif for (const Intersections::internal::Point_on_triangle& pot : l_inter_pts) - inter_pts.emplace_back(pot.point(p1,q1,r1,p2,q2,r2,k), 0, 0); + inter_pts.emplace_back(pot.point(p1,q1,r1,p2,q2,r2,k), pot.id1(), pot.id2()); } // imported from Polygon_mesh_processing/internal/Corefinement/intersect_triangle_and_segment_3.h @@ -891,8 +909,7 @@ void autorefine_triangle_soup(PointRange& soup_points, Visitor visitor(choose_parameter(get_parameter(np, internal_np::visitor))); - //~ constexpr bool parallel_execution = std::is_same_v; - constexpr bool parallel_execution = false; + constexpr bool parallel_execution = std::is_same_v; #ifndef CGAL_LINKED_WITH_TBB static_assert (!parallel_execution, @@ -994,43 +1011,63 @@ void autorefine_triangle_soup(PointRange& soup_points, if (!inter_pts.empty()) { - auto get_ipt_id1 = [](const std::tuple& tpl) - { - if (get<1>(tpl)<0) return -get<1>(tpl)-1; - }; - auto get_ipt_id2 = [](const std::tuple& tpl) - { - if (get<2>(tpl)<0) return -get<2>(tpl)-1; - }; - - std::size_t nbi = inter_pts.size(); switch(nbi) { #warning TODO use inter pt info case 1: if (get<1>(inter_pts[0])>=0) + { all_triangle_data[i1].points.push_back(get<0>(inter_pts[0])); + CGAL_assertion(get<0>(inter_pts[0])!=t1[0]); + CGAL_assertion(get<0>(inter_pts[0])!=t1[1]); + CGAL_assertion(get<0>(inter_pts[0])!=t1[2]); + } if (get<2>(inter_pts[0])>=0) + { all_triangle_data[i2].points.push_back(get<0>(inter_pts[0])); + CGAL_assertion(get<0>(inter_pts[0])!=t2[0]); + CGAL_assertion(get<0>(inter_pts[0])!=t2[1]); + CGAL_assertion(get<0>(inter_pts[0])!=t2[2]); + } break; case 2: { std::size_t src_id=get<1>(inter_pts[0])<0?(-get<1>(inter_pts[0])-1):all_triangle_data[i1].points.size(); if (get<1>(inter_pts[0])>=0) + { all_triangle_data[i1].points.push_back(get<0>(inter_pts[0])); + CGAL_assertion(get<0>(inter_pts[0])!=t1[0]); + CGAL_assertion(get<0>(inter_pts[0])!=t1[1]); + CGAL_assertion(get<0>(inter_pts[0])!=t1[2]); + } std::size_t tgt_id=get<1>(inter_pts[1])<0?(-get<1>(inter_pts[1])-1):all_triangle_data[i1].points.size(); if (get<1>(inter_pts[1])>=0) + { all_triangle_data[i1].points.push_back(get<0>(inter_pts[1])); + CGAL_assertion(get<0>(inter_pts[1])!=t1[0]); + CGAL_assertion(get<0>(inter_pts[1])!=t1[1]); + CGAL_assertion(get<0>(inter_pts[1])!=t1[2]); + } all_triangle_data[i1].segments.emplace_back(src_id, tgt_id); all_triangle_data[i1].segment_input_triangle_ids.push_back(i2); // src_id=get<2>(inter_pts[0])<0?(-get<2>(inter_pts[0])-1):all_triangle_data[i2].points.size(); if (get<2>(inter_pts[0])>=0) + { all_triangle_data[i2].points.push_back(get<0>(inter_pts[0])); + CGAL_assertion(get<0>(inter_pts[0])!=t2[0]); + CGAL_assertion(get<0>(inter_pts[0])!=t2[1]); + CGAL_assertion(get<0>(inter_pts[0])!=t2[2]); + } tgt_id=get<2>(inter_pts[1])<0?(-get<2>(inter_pts[1])-1):all_triangle_data[i2].points.size(); if (get<2>(inter_pts[1])>=0) + { all_triangle_data[i2].points.push_back(get<0>(inter_pts[1])); + CGAL_assertion(get<0>(inter_pts[1])!=t2[0]); + CGAL_assertion(get<0>(inter_pts[1])!=t2[1]); + CGAL_assertion(get<0>(inter_pts[1])!=t2[2]); + } all_triangle_data[i2].segments.emplace_back(src_id, tgt_id); all_triangle_data[i2].segment_input_triangle_ids.push_back(i1); } @@ -1047,8 +1084,21 @@ void autorefine_triangle_soup(PointRange& soup_points, { std::size_t id1=get<1>(inter_pts[i])<0?(-get<1>(inter_pts[i])-1):all_triangle_data[i1].points.size(); std::size_t id2=get<2>(inter_pts[i])<0?(-get<2>(inter_pts[i])-1):all_triangle_data[i2].points.size(); - if (get<1>(inter_pts[i])>=0) all_triangle_data[i1].points.push_back(get<0>(inter_pts[i])); - if (get<2>(inter_pts[i])>=0) all_triangle_data[i2].points.push_back(get<0>(inter_pts[i])); + if (get<1>(inter_pts[i])>=0) + { + all_triangle_data[i1].points.push_back(get<0>(inter_pts[i])); + CGAL_assertion(get<0>(inter_pts[i])!=t1[0]); + CGAL_assertion(get<0>(inter_pts[i])!=t1[1]); + CGAL_assertion(get<0>(inter_pts[i])!=t1[2]); + } + + if (get<2>(inter_pts[i])>=0) + { + all_triangle_data[i2].points.push_back(get<0>(inter_pts[i])); + CGAL_assertion(get<0>(inter_pts[i])!=t2[0]); + CGAL_assertion(get<0>(inter_pts[i])!=t2[1]); + CGAL_assertion(get<0>(inter_pts[i])!=t2[2]); + } ipt_ids1[i]=id1; ipt_ids2[i]=id2; } @@ -1147,6 +1197,7 @@ void autorefine_triangle_soup(PointRange& soup_points, filtered_in_triangle_ids.swap(all_triangle_data[ti].segment_input_triangle_ids); filtered_segments.swap(segments); + CGAL_assertion(points.size()-3==std::set(std::next(points.begin(),3), points.end()).size()); CGAL_assertion(points.size()==std::set(points.begin(), points.end()).size()); } }; From 81d293891007be8eea4fdaf087066f7e845d2467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 18 Jan 2024 13:26:36 +0100 Subject: [PATCH 328/520] collect on-edge information and do not collect segments on the same edge --- .../Polygon_mesh_processing/autorefinement.h | 169 ++++++++++-------- 1 file changed, 93 insertions(+), 76 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index 50e7c5b361e5..400e8641de8a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -485,9 +485,58 @@ bool collect_intersections(const std::array& t1, //TODO: rename struct struct Triangle_data { - std::vector points; + using Point_3 = Exact_predicates_exact_constructions_kernel::Point_3; + std::vector points; + std::vector point_locations; std::vector> segments; std::vector segment_input_triangle_ids; + template + std::size_t add_point(const std::tuple& tpl) + { + if (get(tpl) < 0) + return -get(tpl)-1; + points.push_back(get<0>(tpl)); + point_locations.push_back(get(tpl)); + return points.size()-1; + } + +#ifndef NDEBUG + bool on_same_edge(std::size_t i, std::size_t j) +#else + bool on_same_edge_debug(std::size_t i, std::size_t j) +#endif + { + if (point_locations[i]==0 || point_locations[j]==0) return false; + if (point_locations[i]>0) + { + if (point_locations[j]>0) + return point_locations[i]==point_locations[j]; + return -point_locations[j]==point_locations[i] || point_locations[i]%3+1==-point_locations[j]; + } + if (point_locations[j]<0) + return true; + return -point_locations[i]==point_locations[j] || point_locations[j]%3+1==-point_locations[i]; + } +#ifdef NDEBUG + bool on_same_edge(std::size_t i, std::size_t j) + { + bool on = on_same_edge_debug(i,j); + if (!on) return false; + int eid = point_locations[i]>0 ? point_locations[i] : point_locations[j]; + if (eid<0) return true; + if (!(collinear(points[eid-1], points[(eid)%3], points[i]))) + { + std::cout << point_locations[i] << " " << point_locations[j] << " " << eid << "\n"; + } + if (!(collinear(points[eid-1], points[(eid)%3], points[j]))) + { + std::cout << point_locations[i] << " " << point_locations[j] << " " << eid << "\n"; + } + CGAL_assertion(collinear(points[eid-1], points[(eid)%3], points[i])); + CGAL_assertion(collinear(points[eid-1], points[(eid)%3], points[j])); + return true; + } +#endif }; template (inter_pts[0])>=0) - { - all_triangle_data[i1].points.push_back(get<0>(inter_pts[0])); - CGAL_assertion(get<0>(inter_pts[0])!=t1[0]); - CGAL_assertion(get<0>(inter_pts[0])!=t1[1]); - CGAL_assertion(get<0>(inter_pts[0])!=t1[2]); - } - if (get<2>(inter_pts[0])>=0) - { - all_triangle_data[i2].points.push_back(get<0>(inter_pts[0])); - CGAL_assertion(get<0>(inter_pts[0])!=t2[0]); - CGAL_assertion(get<0>(inter_pts[0])!=t2[1]); - CGAL_assertion(get<0>(inter_pts[0])!=t2[2]); - } + all_triangle_data[i1].add_point<1>(inter_pts[0]); + all_triangle_data[i2].add_point<2>(inter_pts[0]); break; case 2: { - std::size_t src_id=get<1>(inter_pts[0])<0?(-get<1>(inter_pts[0])-1):all_triangle_data[i1].points.size(); - if (get<1>(inter_pts[0])>=0) - { - all_triangle_data[i1].points.push_back(get<0>(inter_pts[0])); - CGAL_assertion(get<0>(inter_pts[0])!=t1[0]); - CGAL_assertion(get<0>(inter_pts[0])!=t1[1]); - CGAL_assertion(get<0>(inter_pts[0])!=t1[2]); - } - std::size_t tgt_id=get<1>(inter_pts[1])<0?(-get<1>(inter_pts[1])-1):all_triangle_data[i1].points.size(); - if (get<1>(inter_pts[1])>=0) + std::size_t src_id=all_triangle_data[i1].add_point<1>(inter_pts[0]), + tgt_id=all_triangle_data[i1].add_point<1>(inter_pts[1]); + if (!all_triangle_data[i1].on_same_edge(src_id, tgt_id)) { - all_triangle_data[i1].points.push_back(get<0>(inter_pts[1])); - CGAL_assertion(get<0>(inter_pts[1])!=t1[0]); - CGAL_assertion(get<0>(inter_pts[1])!=t1[1]); - CGAL_assertion(get<0>(inter_pts[1])!=t1[2]); + all_triangle_data[i1].segments.emplace_back(src_id, tgt_id); + all_triangle_data[i1].segment_input_triangle_ids.push_back(i2); } - all_triangle_data[i1].segments.emplace_back(src_id, tgt_id); - all_triangle_data[i1].segment_input_triangle_ids.push_back(i2); // - src_id=get<2>(inter_pts[0])<0?(-get<2>(inter_pts[0])-1):all_triangle_data[i2].points.size(); - if (get<2>(inter_pts[0])>=0) + src_id=all_triangle_data[i2].add_point<2>(inter_pts[0]); + tgt_id=all_triangle_data[i2].add_point<2>(inter_pts[1]); + if (!all_triangle_data[i2].on_same_edge(src_id, tgt_id)) { - all_triangle_data[i2].points.push_back(get<0>(inter_pts[0])); - CGAL_assertion(get<0>(inter_pts[0])!=t2[0]); - CGAL_assertion(get<0>(inter_pts[0])!=t2[1]); - CGAL_assertion(get<0>(inter_pts[0])!=t2[2]); + all_triangle_data[i2].segments.emplace_back(src_id, tgt_id); + all_triangle_data[i2].segment_input_triangle_ids.push_back(i1); } - tgt_id=get<2>(inter_pts[1])<0?(-get<2>(inter_pts[1])-1):all_triangle_data[i2].points.size(); - if (get<2>(inter_pts[1])>=0) - { - all_triangle_data[i2].points.push_back(get<0>(inter_pts[1])); - CGAL_assertion(get<0>(inter_pts[1])!=t2[0]); - CGAL_assertion(get<0>(inter_pts[1])!=t2[1]); - CGAL_assertion(get<0>(inter_pts[1])!=t2[2]); - } - all_triangle_data[i2].segments.emplace_back(src_id, tgt_id); - all_triangle_data[i2].segment_input_triangle_ids.push_back(i1); } break; default: { std::vector ipt_ids1(nbi+1), ipt_ids2(nbi+1); - all_triangle_data[i1].segment_input_triangle_ids.insert( - all_triangle_data[i1].segment_input_triangle_ids.end(), nbi, i2); - all_triangle_data[i2].segment_input_triangle_ids.insert( - all_triangle_data[i2].segment_input_triangle_ids.end(), nbi, i1); for (std::size_t i=0;i(inter_pts[i])<0?(-get<1>(inter_pts[i])-1):all_triangle_data[i1].points.size(); - std::size_t id2=get<2>(inter_pts[i])<0?(-get<2>(inter_pts[i])-1):all_triangle_data[i2].points.size(); - if (get<1>(inter_pts[i])>=0) - { - all_triangle_data[i1].points.push_back(get<0>(inter_pts[i])); - CGAL_assertion(get<0>(inter_pts[i])!=t1[0]); - CGAL_assertion(get<0>(inter_pts[i])!=t1[1]); - CGAL_assertion(get<0>(inter_pts[i])!=t1[2]); - } - - if (get<2>(inter_pts[i])>=0) - { - all_triangle_data[i2].points.push_back(get<0>(inter_pts[i])); - CGAL_assertion(get<0>(inter_pts[i])!=t2[0]); - CGAL_assertion(get<0>(inter_pts[i])!=t2[1]); - CGAL_assertion(get<0>(inter_pts[i])!=t2[2]); - } - ipt_ids1[i]=id1; - ipt_ids2[i]=id2; + ipt_ids1[i]=all_triangle_data[i1].add_point<1>(inter_pts[i]); + ipt_ids2[i]=all_triangle_data[i2].add_point<2>(inter_pts[i]); } ipt_ids1.back()=ipt_ids1.front(); ipt_ids2.back()=ipt_ids2.front(); for (std::size_t i=0;i tmp; + std::vector tmp_locations; tmp.reserve(unique_ids.size()+3); + tmp_locations.reserve(unique_ids.size()+3); tmp.push_back(points[0]); tmp.push_back(points[1]); tmp.push_back(points[2]); + tmp_locations.push_back(-1); + tmp_locations.push_back(-2); + tmp_locations.push_back(-3); for(std::size_t i : unique_ids) + { tmp.push_back(points[i]); + tmp_locations.push_back(all_triangle_data[ti].point_locations[i]); + } tmp.swap(points); + tmp_locations.swap(all_triangle_data[ti].point_locations); + CGAL_assertion(points.size() == all_triangle_data[ti].point_locations.size()); // now make segments unique std::size_t nbs = segments.size(); @@ -1240,7 +1257,7 @@ void autorefine_triangle_soup(PointRange& soup_points, auto refine_triangles = [&](std::size_t ti) { - if (all_triangle_data[ti].points.empty()) + if (all_triangle_data[ti].points.size()==3) new_triangles.push_back({triangles[ti], ti}); else { From 0095748cd7a5ab55eb82767b82f4e68e9e051ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 18 Jan 2024 14:42:47 +0100 Subject: [PATCH 329/520] insert points directly on edges --- .../Polygon_mesh_processing/autorefinement.h | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index 400e8641de8a..dcd6a0664abc 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -582,9 +582,8 @@ void generate_subtriangles(std::size_t ti, #define CGAL_AUTOREF_COUNTER_INSTRUCTION(X) #endif - #warning TODO - std::vector& points=triangle_data.points; + std::vector& point_locations=triangle_data.point_locations; std::vector>& segments=triangle_data.segments; std::vector& in_triangle_ids=triangle_data.segment_input_triangle_ids; @@ -611,7 +610,10 @@ void generate_subtriangles(std::size_t ti, { auto insert_res = point_id_map.insert(std::make_pair(pt, points.size())); if (insert_res.second) + { points.push_back(pt); + point_locations.push_back(0); + } return insert_res.first->second; }; @@ -840,8 +842,57 @@ void generate_subtriangles(std::size_t ti, vhandles[2]->set_point(t[2]); // insert points and fill vhandles - std::vector indices(triangle_data.points.size()-3); - std::iota(indices.begin(), indices.end(), 3); + + //start by points on edges + std::array, 3> indices_on_edges; + std::vector indices; + for (std::size_t i=3; i0 && point_locations[i]<4); + indices_on_edges[point_locations[i]-1].push_back(i); + } + } + + //sort points on edges and insert them + typename CDT::Vertex_handle vinf=cdt.infinite_vertex(); + for (int i=0; i<3; ++i) + { + if (indices_on_edges[i].empty()) continue; + int src_id=i, tgt_id=(i+1)%3; + //look for a sort axis + int coord = 0; + if (points[src_id].x()==points[tgt_id].x()) + { + coord=1; + if (points[src_id].y()==points[tgt_id].y()) + coord=2; + } + if (points[src_id][coord]>points[tgt_id][coord]) + std::swap(src_id, tgt_id); + + std::sort(indices_on_edges[i].begin(), indices_on_edges[i].end(), + [&](std::size_t id1, std::size_t id2) + { + return points[id1][coord]index(vinf)); + prev_id=id; + } + } + + // then points in the interior typedef typename Pointer_property_map::type Pmap; typedef Spatial_sort_traits_adapter_2 Search_traits; spatial_sort(indices.begin(), indices.end(), @@ -1067,7 +1118,6 @@ void autorefine_triangle_soup(PointRange& soup_points, std::size_t nbi = inter_pts.size(); switch(nbi) { - #warning TODO use inter pt info case 1: all_triangle_data[i1].add_point<1>(inter_pts[0]); all_triangle_data[i2].add_point<2>(inter_pts[0]); From 272a242f1b049af73bc2530e660fba4a14093142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 19 Jan 2024 10:06:03 +0100 Subject: [PATCH 330/520] restore delaunay in CDT + autoref and deduplicate identical points in no segments case --- .../Polygon_mesh_processing/autorefinement.h | 26 +++++++++++++++++-- .../Constrained_Delaunay_triangulation_2.h | 19 ++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index dcd6a0664abc..8de91af1da85 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -843,13 +843,22 @@ void generate_subtriangles(std::size_t ti, // insert points and fill vhandles +#if 0 + std::vector indices(triangle_data.points.size()-3); + std::iota(indices.begin(), indices.end(), 3); +#else //start by points on edges std::array, 3> indices_on_edges; std::vector indices; for (std::size_t i=3; i0 && point_locations[i]<4); @@ -862,7 +871,7 @@ void generate_subtriangles(std::size_t ti, for (int i=0; i<3; ++i) { if (indices_on_edges[i].empty()) continue; - int src_id=i, tgt_id=(i+1)%3; + std::size_t src_id=i, tgt_id=(i+1)%3; //look for a sort axis int coord = 0; if (points[src_id].x()==points[tgt_id].x()) @@ -879,19 +888,32 @@ void generate_subtriangles(std::size_t ti, { return points[id1][coord]index(vinf)); + cdt.restore_Delaunay(vhandles[id]); // TODO maybe not each time but one global? + CGAL_assertion(cdt.is_valid()); prev_id=id; } } - +#endif // then points in the interior typedef typename Pointer_property_map::type Pmap; typedef Spatial_sort_traits_adapter_2 Search_traits; diff --git a/Triangulation_2/include/CGAL/Constrained_Delaunay_triangulation_2.h b/Triangulation_2/include/CGAL/Constrained_Delaunay_triangulation_2.h index 52c14ea54b78..714e9295b61f 100644 --- a/Triangulation_2/include/CGAL/Constrained_Delaunay_triangulation_2.h +++ b/Triangulation_2/include/CGAL/Constrained_Delaunay_triangulation_2.h @@ -342,6 +342,25 @@ class Constrained_Delaunay_triangulation_2 return number_of_vertices() - n; } + // function usable only if no constraint has been inserted + void restore_Delaunay(Vertex_handle v) + { + if(this->dimension() <= 1) return; + + Face_handle f=v->face(); + Face_handle next; + int i; + Face_handle start(f); + do { + i = f->index(v); + next = f->neighbor(ccw(i)); // turn ccw around v + propagating_flip(f,i); + f = next; + } while(next != start); + return; + } + + #ifndef CGAL_TRIANGULATION_2_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO private: //top stands for tuple-or-pair From f520602a0dcbcefe0a28a346f11fd84b15249f34 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 19 Jan 2024 10:05:30 +0000 Subject: [PATCH 331/520] Comment #warning and qualify get() with std:: --- .../CGAL/Polygon_mesh_processing/autorefinement.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index 8de91af1da85..c38d783572b4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -468,10 +468,10 @@ bool collect_intersections(const std::array& t1, } } - #warning TODO get rid of sort and unique calls +// #warning TODO get rid of sort and unique calls // because we don't handle intersection type and can have edge-edge edge-vertex duplicates - std::sort(inter_pts.begin(), inter_pts.end(), [](auto p, auto q){return get<0>(p)(q);}); - auto last = std::unique(inter_pts.begin(), inter_pts.end(), [](auto p, auto q){return get<0>(p)==get<0>(q);}); + std::sort(inter_pts.begin(), inter_pts.end(), [](auto p, auto q){return std::get<0>(p)(q);}); + auto last = std::unique(inter_pts.begin(), inter_pts.end(), [](auto p, auto q){return std::get<0>(p)==std::get<0>(q);}); inter_pts.erase(last, inter_pts.end()); #ifdef CGAL_AUTOREF_DEBUG_DEPTH @@ -493,10 +493,10 @@ struct Triangle_data template std::size_t add_point(const std::tuple& tpl) { - if (get(tpl) < 0) - return -get(tpl)-1; - points.push_back(get<0>(tpl)); - point_locations.push_back(get(tpl)); + if (std::get(tpl) < 0) + return -std::get(tpl)-1; + points.push_back(std::get<0>(tpl)); + point_locations.push_back(std::get(tpl)); return points.size()-1; } From a783412ba77b6a0873bc62f2f4b5f09dde621548 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 9 Jan 2024 16:34:02 +0000 Subject: [PATCH 332/520] Use structural filtering for the fixed projection traits classes --- .../Kernel_23/internal/Projection_traits_3.h | 11 ++- .../include/CGAL/Triangulation_2.h | 95 +++++++++++++++---- 2 files changed, 89 insertions(+), 17 deletions(-) diff --git a/Kernel_23/include/CGAL/Kernel_23/internal/Projection_traits_3.h b/Kernel_23/include/CGAL/Kernel_23/internal/Projection_traits_3.h index dd4e24005a48..1cc09421944d 100644 --- a/Kernel_23/include/CGAL/Kernel_23/internal/Projection_traits_3.h +++ b/Kernel_23/include/CGAL/Kernel_23/internal/Projection_traits_3.h @@ -20,6 +20,7 @@ #include #include +#include namespace CGAL { @@ -1210,6 +1211,14 @@ class Projection_traits_3 }; -} } //namespace CGAL::internal + +} // namespace internal + +template +struct Triangulation_structural_filtering_traits<::CGAL::internal::Projection_traits_3 > { + typedef typename Triangulation_structural_filtering_traits::Use_structural_filtering_tag Use_structural_filtering_tag; +}; + + } //namespace CGAL #endif // CGAL_INTERNAL_PROJECTION_TRAITS_3_H diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index 14e8604640f1..c0f2c7ba50e0 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -434,6 +435,81 @@ class Triangulation_2 bool has_inexact_negative_orientation(const Point &p, const Point &q, const Point &r) const; + + +template +inline +bool +projection_traits_has_inexact_negative_orientation(const Point &p, const Point &q, const Point &r, + const T& ) const +{ + // So that this code works well with Lazy_kernel + internal::Static_filters_predicates::Get_approx get_approx; + + const double px = to_double(get_approx(p).x()); + const double py = to_double(get_approx(p).y()); + const double qx = to_double(get_approx(q).x()); + const double qy = to_double(get_approx(q).y()); + const double rx = to_double(get_approx(r).x()); + const double ry = to_double(get_approx(r).y()); + + const double pqx = qx - px; + const double pqy = qy - py; + const double prx = rx - px; + const double pry = ry - py; + + return ( determinant(pqx, pqy, prx, pry) < 0); +} + +template +inline +bool +projection_traits_has_inexact_negative_orientation(const Point &p, const Point &q, const Point &r, + const ::CGAL::internal::Projection_traits_3& ) const +{ // So that this code works well with Lazy_kernel + internal::Static_filters_predicates::Get_approx get_approx; + + const double px = to_double(get_approx(p).y()); + const double py = to_double(get_approx(p).z()); + const double qx = to_double(get_approx(q).y()); + const double qy = to_double(get_approx(q).z()); + const double rx = to_double(get_approx(r).y()); + const double ry = to_double(get_approx(r).z()); + + const double pqx = qx - px; + const double pqy = qy - py; + const double prx = rx - px; + const double pry = ry - py; + + return ( determinant(pqx, pqy, prx, pry) < 0); +} + + +template +inline +bool +projection_traits_has_inexact_negative_orientation(const Point &p, const Point &q, const Point &r, + const ::CGAL::internal::Projection_traits_3& ) const +{ // So that this code works well with Lazy_kernel + internal::Static_filters_predicates::Get_approx get_approx; + + const double px = to_double(get_approx(p).x()); + const double py = to_double(get_approx(p).z()); + const double qx = to_double(get_approx(q).x()); + const double qy = to_double(get_approx(q).z()); + const double rx = to_double(get_approx(r).x()); + const double ry = to_double(get_approx(r).z()); + + const double pqx = qx - px; + const double pqy = qy - py; + const double prx = rx - px; + const double pry = ry - py; + + return ( determinant(pqx, pqy, prx, pry) < 0); +} + + + public: Face_handle inexact_locate(const Point& p, @@ -3152,6 +3228,7 @@ inexact_locate(const Point & t, Face_handle start, int n_of_turns) const return c; } + template inline bool @@ -3159,22 +3236,8 @@ Triangulation_2:: has_inexact_negative_orientation(const Point &p, const Point &q, const Point &r) const { - // So that this code works well with Lazy_kernel - internal::Static_filters_predicates::Get_approx get_approx; - - const double px = to_double(get_approx(p).x()); - const double py = to_double(get_approx(p).y()); - const double qx = to_double(get_approx(q).x()); - const double qy = to_double(get_approx(q).y()); - const double rx = to_double(get_approx(r).x()); - const double ry = to_double(get_approx(r).y()); - - const double pqx = qx - px; - const double pqy = qy - py; - const double prx = rx - px; - const double pry = ry - py; - - return ( determinant(pqx, pqy, prx, pry) < 0); + Gt gt; + return projection_traits_has_inexact_negative_orientation(p,q,r,gt); } #endif From 6b40f5b1894e6f494435bd9221df0508edda1108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 5 Jan 2024 17:57:31 +0100 Subject: [PATCH 333/520] restore axis aligned projection traits --- .../Polygon_mesh_processing/autorefinement.h | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index c38d783572b4..94b6a9c96677 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -55,6 +55,10 @@ #endif #endif +#ifdef USE_FIXED_PROJECTION_TRAITS +#include +#endif + #include //#define CGAL_AUTOREF_USE_DEBUG_PARALLEL_TIMERS @@ -540,6 +544,9 @@ struct Triangle_data }; template void generate_subtriangles(std::size_t ti, Triangle_data& triangle_data, @@ -813,7 +820,12 @@ void generate_subtriangles(std::size_t ti, // init CDT + insert points and constraints CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer3.start();) +#ifdef USE_FIXED_PROJECTION_TRAITS + typedef ::CGAL::internal::Projection_traits_3 P_traits; +#else typedef CGAL::Projection_traits_3 P_traits; +#endif + typedef CGAL::No_constraint_intersection_tag Itag; typedef CGAL::Constrained_Delaunay_triangulation_2 CDT_2; @@ -821,7 +833,17 @@ void generate_subtriangles(std::size_t ti, typedef CDT_2 CDT; const std::array& t = triangles[ti]; + std::vector vhandles(triangle_data.points.size()); +#ifdef USE_FIXED_PROJECTION_TRAITS + P_traits cdt_traits; + bool orientation_flipped = false; + CDT cdt(cdt_traits); + // TODO: still need to figure out why I can't make the orientation_flipped correctly + vhandles[0]=cdt.insert(t[0]); + vhandles[1]=cdt.insert(t[1]); + vhandles[2]=cdt.insert(t[2]); +#else // positive triangle normal typename EK::Vector_3 n = normal(t[0], t[1], t[2]); typename EK::Point_3 o(CGAL::ORIGIN); @@ -832,17 +854,16 @@ void generate_subtriangles(std::size_t ti, n=-n; orientation_flipped = true; } - - std::vector vhandles(triangle_data.points.size()); P_traits cdt_traits(n); + CDT cdt(cdt_traits); vhandles[0]=cdt.insert_outside_affine_hull(t[0]); vhandles[1]=cdt.insert_outside_affine_hull(t[1]); vhandles[2] = cdt.tds().insert_dim_up(cdt.infinite_vertex(), orientation_flipped); vhandles[2]->set_point(t[2]); +#endif // insert points and fill vhandles - #if 0 std::vector indices(triangle_data.points.size()-3); std::iota(indices.begin(), indices.end(), 3); @@ -1333,7 +1354,24 @@ void autorefine_triangle_soup(PointRange& soup_points, new_triangles.push_back({triangles[ti], ti}); else { +#ifdef USE_FIXED_PROJECTION_TRAITS + const std::array& t = triangles[ti]; + typename EK::Vector_3 orth = CGAL::normal(t[0], t[1], t[2]); // TODO::avoid construction? + int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1; + c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c; + + if (c == 0) { + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + } + else if (c == 1) { + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + } + else if (c == 2) { + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + } +#else autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); +#endif } #ifdef CGAL_AUTOREF_USE_PROGRESS_DISPLAY From 620a78c7f0b18427654378b8f468792051111b88 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 07:25:58 +0000 Subject: [PATCH 334/520] CGAL_USE --- Property_map/test/Property_map/test_Property_container.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Property_map/test/Property_map/test_Property_container.cpp b/Property_map/test/Property_map/test_Property_container.cpp index 45698bae5848..696fd34a27ef 100644 --- a/Property_map/test/Property_map/test_Property_container.cpp +++ b/Property_map/test/Property_map/test_Property_container.cpp @@ -1,5 +1,6 @@ #include +#include using namespace CGAL::Properties::Experimental; @@ -31,6 +32,7 @@ void test_property_creation() { auto [bools, bools_created] = properties.get_or_add_property("bools", false); static_assert(std::is_same_v>>); Property_array& b = bools.get(); + CGAL_USE(b); } void test_element_access() { @@ -110,7 +112,7 @@ void test_emplace_group() { Property_container properties; auto& a = properties.add_property("a", 5); - + CGAL_USE(a); // Insert a group of 100 elements properties.emplace_group(100); assert(properties.size() == 100); @@ -257,4 +259,4 @@ int main() { test_constructors(); return 0; -} \ No newline at end of file +} From 832389a5f7a3cee6b70b25c97d08844fa08dfd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jan 2024 08:56:51 +0100 Subject: [PATCH 335/520] property handles are returned by copy --- Orthtree/test/Orthtree/test_octree_custom_properties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 97c3ee97b57b..8815a36d1e62 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -43,7 +43,7 @@ int main(void) { auto prop6 = tree.add_node_property("test2", std::string()); // Default value should be respected - auto &node_int_property = tree.add_node_property("int", 5); + auto node_int_property = tree.add_node_property("int", 5); assert(node_int_property[tree.root()] == 5); // Changes to individual nodes should be respected From f29e307684d3d484ac8cf05fadc30c3acfd822cc Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 09:02:06 +0000 Subject: [PATCH 336/520] Add Node_index to the traits concept --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 6ff355eba033..d05fa0ee7c46 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -17,7 +17,7 @@ class OrthtreeTraits /// \name Types /// @{ - + using Node_index = unspecified_type; ///< An integer type for nodes using Dimension = unspecified_type; ///< Dimension type (see `CGAL::Dimension_tag`). using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` using Point_d = unspecified_type; ///< Point type. @@ -62,7 +62,7 @@ class OrthtreeTraits * It takes no arguments, and return an instance of `Node_data`. * * Provides the operator: - * Node_data operator()()` + * `Node_data operator()()` * * Typically, the `Node_data` of the root node contains all the elements in the tree. * For a tree in which each node contains an `std::span()` this function would return the span containing all items. @@ -84,7 +84,7 @@ class OrthtreeTraits * The functor takes a node index, a tree reference, and a `Point_d` which is the center of the node. * * Provides the operator: - * void operator()(typename Tree::Node_index, Tree&, const Point_d&)` + * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` * * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` * to access the contents of the node and each of its children. From b9a25565606b7202c75c53f3c7c07848dc147e89 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 09:27:27 +0000 Subject: [PATCH 337/520] use variable --- Orthtree/test/Orthtree/test_octree_custom_properties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 8815a36d1e62..af24b5cde1d9 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -41,7 +41,7 @@ int main(void) { assert(!prop5.second); auto prop6 = tree.add_node_property("test2", std::string()); - + assert(prop6.second); // Default value should be respected auto node_int_property = tree.add_node_property("int", 5); assert(node_int_property[tree.root()] == 5); From 876865b525543ef163d3a2d1db74b0b4396b798d Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 09:30:29 +0000 Subject: [PATCH 338/520] use variable --- Orthtree/test/Orthtree/test_octree_custom_properties.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index af24b5cde1d9..7553824a8ab2 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -27,6 +27,7 @@ int main(void) { // Testing built in node properties typename Octree::Property_map data_prop = tree.get_node_property("contents"); + CGAL_USE(data_prop); auto prop2 = tree.get_or_add_node_property("test", int(0)); assert(prop2.second); @@ -41,7 +42,8 @@ int main(void) { assert(!prop5.second); auto prop6 = tree.add_node_property("test2", std::string()); - assert(prop6.second); + CGAL_USE(prop6); + // Default value should be respected auto node_int_property = tree.add_node_property("int", 5); assert(node_int_property[tree.root()] == 5); From f650c6fe2364b44f10766a675585319ad9cefb6f Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 09:32:53 +0000 Subject: [PATCH 339/520] Add typedef to base class --- Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index ae649fc87158..38b9652a3a29 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -41,6 +41,7 @@ template struct Orthtree_traits_base_for_dimension { /// \name Types /// @{ + using Node_index = std::size_t; using Kernel = K; using Dimension = DimensionTag; using FT = typename K::FT; @@ -113,6 +114,7 @@ template struct Orthtree_traits_base_for_dimension> { /// \name Types /// @{ + using Node_index = std::size_t; using Kernel = K; using Dimension = Dimension_tag<2>; using FT = typename K::FT; @@ -149,6 +151,7 @@ template struct Orthtree_traits_base_for_dimension> { /// \name Types /// @{ + using Node_index = std::size_t; using Kernel = K; using Dimension = Dimension_tag<3>; using FT = typename K::FT; From f014326c00511a3b725ecbd1a4778fa164201ea2 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 09:49:10 +0000 Subject: [PATCH 340/520] Use Traits::Node_index --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 14 +++++++++----- Orthtree/include/CGAL/Orthtree_traits_point.h | 5 +++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 0dbcbc7d8256..e078949dc41c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -51,6 +51,10 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// \name Types /// @{ + using Base = Orthtree_traits_base_for_dimension< + typename Kernel_traits::value_type>::type, + Dimension_tag<3> >; + using Node_index = typename Base::Node_index; using Self = Orthtree_traits_face_graph; using Tree = Orthtree; @@ -66,7 +70,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< using Construct_root_node_bbox = std::function; using Construct_root_node_contents = std::function; - using Distribute_node_contents = std::function; + using Distribute_node_contents = std::function; /// @} @@ -102,11 +106,11 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< } auto distribute_node_contents_object() { - return [&](typename Tree::Node_index n, Tree& tree, const Point_d& center) -> void { + return [&](Node_index n, Tree& tree, const Point_d& center) -> void { Node_data& ndata = tree.data(n); auto traits = tree.traits(); for (int i = 0; i < 8; ++i) { - typename Tree::Node_index child = tree.child(n, i); + Node_index child = tree.child(n, i); Node_data& child_data = tree.data(child); Bbox_d bbox = tree.bbox(child); for (auto f : ndata) { @@ -140,8 +144,8 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /*! \brief returns `true` if `ni` should be split, `false` otherwise. */ - template - bool operator()(Node_index ni, const Tree& tree) const { + template + bool operator()(NodeIndex ni, const Tree& tree) const { if (tree.data(ni).empty()) return false; Bbox_d bb = tree.bbox(ni); diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 6688a8e56633..488a9b360bd2 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -93,12 +93,13 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension; using Self = Orthtree_traits_point; using Tree = Orthtree; using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; - + using Node_index = typename Base::Node_index; /// @} Orthtree_traits_point( @@ -148,7 +149,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension Date: Wed, 24 Jan 2024 10:02:22 +0000 Subject: [PATCH 341/520] Do not output just the x-coord of the min of the bbox --- Orthtree/examples/Orthtree/orthtree_build.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 54d629a5916e..25abed86defe 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -31,7 +31,6 @@ int main() Orthtree orthtree(points_dd); orthtree.refine(10, 5); - std::cout << orthtree.bbox(orthtree.root()).min()[0] << std::endl; std::cout << orthtree << std::endl; return EXIT_SUCCESS; From 77d95097dd24181a3f95e0620705956cb801bb08 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 10:05:46 +0000 Subject: [PATCH 342/520] protect min/max --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index e078949dc41c..a99fcc898bca 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -151,7 +151,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< Bbox_d bb = tree.bbox(ni); for (int i = 0; i < 3; ++i) - if ((bb.max()[i] - bb.min()[i]) < 2 * m_min_extent) + if (((bb.max)()[i] - (bb.min)()[i]) < 2 * m_min_extent) return false; return true; } From 33e09bf1a006a94d3ca2dd0f43f1efb04a5ef316 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 10:38:05 +0000 Subject: [PATCH 343/520] Switch back to the wrong coordinate system --- .../CGAL/Orthtree_traits_base_for_dimension.h | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 38b9652a3a29..8ea4e6d8cf1d 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -160,6 +160,7 @@ struct Orthtree_traits_base_for_dimension> { using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; +#if 0 enum Adjacency { LEFT, RIGHT, @@ -179,6 +180,29 @@ struct Orthtree_traits_base_for_dimension> { LEFT_TOP_BACK, RIGHT_TOP_BACK }; + +#else + + enum Adjacency { + LEFT, + RIGHT, + DOWN, + UP, + BACK, + FRONT + }; + /// \cond SKIP_IN_MANUAL + enum Child { + LEFT_BOTTOM_BACK, + RIGHT_BOTTOM_BACK, + LEFT_TOP_BACK, + RIGHT_TOP_BACK, + LEFT_BOTTOM_FRONT, + RIGHT_BOTTOM_FRONT, + LEFT_TOP_FRONT, + RIGHT_TOP_FRONT + }; +#endif /// \endcond /// @} From d078a34e1f8144097ddf68ca836f7530592d4144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jan 2024 11:47:11 +0100 Subject: [PATCH 344/520] fix warnings --- Orthtree/examples/Orthtree/octree_grade.cpp | 2 -- .../examples/Orthtree/quadtree_build_manually.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 14 ++++++-------- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_grade.cpp b/Orthtree/examples/Orthtree/octree_grade.cpp index 5bb410d7e336..5f213ff1233e 100644 --- a/Orthtree/examples/Orthtree/octree_grade.cpp +++ b/Orthtree/examples/Orthtree/octree_grade.cpp @@ -14,8 +14,6 @@ int main() { // Here, our point set is a vector Point_vector points; - using IPoint = CGAL::Simple_cartesian::Point_3; - // Add a few points to the vector, most of which are in one region points.emplace_back(1, 1, 1); points.emplace_back(2, 1, -11); diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 73808cc603b1..708b680213b7 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -30,7 +30,7 @@ struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension Node_data { return {}; }; } auto distribute_node_contents_object() { - return [&](typename Tree::Node_index n, Tree& tree, const typename Self::Point_d& center) -> void {}; + return [&](typename Tree::Node_index /* n */, Tree& /* tree */, const typename Self::Point_d& /* center */) -> void {}; } private: diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 563e6c94850d..d1eac256ea3e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -232,30 +232,28 @@ class Orthtree { /// @} - /// \cond SKIP_IN_MANUAL - // copy constructor Orthtree(const Orthtree& other) : m_traits(other.m_traits), - m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(other.m_node_properties), m_node_contents(m_node_properties.get_property("contents")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), - m_node_children(m_node_properties.get_property("children")) {} + m_node_children(m_node_properties.get_property("children")), + m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth) {} // move constructor Orthtree(Orthtree&& other) : m_traits(other.m_traits), - m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth), m_node_properties(std::move(other.m_node_properties)), m_node_contents(m_node_properties.get_property("contents")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), - m_node_children(m_node_properties.get_property("children")) { - + m_node_children(m_node_properties.get_property("children")), + m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth) + { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. other.m_node_properties.emplace(); } @@ -1245,7 +1243,7 @@ class Orthtree { // Iterate over all nodes for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { // Show the depth - for (int i = 0; i < orthtree.depth(n); ++i) + for (std::size_t i = 0; i < orthtree.depth(n); ++i) os << ". "; // Print the node internal::print_orthtree_node(os, n, orthtree); diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index a99fcc898bca..d7e28bbbdc85 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -106,7 +106,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< } auto distribute_node_contents_object() { - return [&](Node_index n, Tree& tree, const Point_d& center) -> void { + return [&](Node_index n, Tree& tree, const Point_d& /* center */) -> void { Node_data& ndata = tree.data(n); auto traits = tree.traits(); for (int i = 0; i < 8; ++i) { From 5e67892ab04a1355bef48e0e2d90e2fea2bdb02a Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 13:58:05 +0000 Subject: [PATCH 345/520] If smaller is true we want to go in the 0-half space not 1 --- Orthtree/include/CGAL/Orthtree.h | 5 +++-- Orthtree/test/Orthtree/test_octree_locate.cpp | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d1eac256ea3e..475d96e57b61 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -597,8 +597,9 @@ class Orthtree { // Find the index of the correct sub-node Local_coordinates local_coords; std::size_t dimension = 0; - for (const auto& r: cartesian_range(point, center)) - local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); + for (const auto& r: cartesian_range(point, center)){ + local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r))?0:1; + } // Find the correct sub-node of the current node node_for_point = child(node_for_point, local_coords.to_ulong()); diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 708727341490..38fe842d163b 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -49,7 +49,6 @@ void test_8_points() { // Create the octree Octree octree({points, points.point_map()}); octree.refine(10, 1); - // Existing points should end up in the same place assert(octree.node(0) == octree.locate({-1, -1, -1})); assert(octree.node(1) == octree.locate({1, -1, -1})); From 0c9349064b69acb791da4fc3538c53fde0c56ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jan 2024 16:57:53 +0100 Subject: [PATCH 346/520] Revert "If smaller is true we want to go in the 0-half space not 1" This reverts commit 5e67892ab04a1355bef48e0e2d90e2fea2bdb02a. --- Orthtree/include/CGAL/Orthtree.h | 5 ++--- Orthtree/test/Orthtree/test_octree_locate.cpp | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 475d96e57b61..d1eac256ea3e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -597,9 +597,8 @@ class Orthtree { // Find the index of the correct sub-node Local_coordinates local_coords; std::size_t dimension = 0; - for (const auto& r: cartesian_range(point, center)){ - local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r))?0:1; - } + for (const auto& r: cartesian_range(point, center)) + local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); // Find the correct sub-node of the current node node_for_point = child(node_for_point, local_coords.to_ulong()); diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 38fe842d163b..708727341490 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -49,6 +49,7 @@ void test_8_points() { // Create the octree Octree octree({points, points.point_map()}); octree.refine(10, 1); + // Existing points should end up in the same place assert(octree.node(0) == octree.locate({-1, -1, -1})); assert(octree.node(1) == octree.locate({1, -1, -1})); From 91b4a7a70c646bce3880018d9b1597e2175ebe8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jan 2024 16:58:46 +0100 Subject: [PATCH 347/520] fix zip iterator changed introduced in d43432d533aa0314be6d86508640617f9bc27a4b --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d1eac256ea3e..688c61a8e461 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -597,7 +597,7 @@ class Orthtree { // Find the index of the correct sub-node Local_coordinates local_coords; std::size_t dimension = 0; - for (const auto& r: cartesian_range(point, center)) + for (const auto& r: cartesian_range(center, point)) local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); // Find the correct sub-node of the current node From 95d8d8c48d8a90144eb520af0c7fa75f2d871a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 24 Jan 2024 18:40:43 +0100 Subject: [PATCH 348/520] clean up axis and commodity enum to work in both 2D and 3D --- .../CGAL/Orthtree_traits_base_for_dimension.h | 69 +++++++------------ 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 8ea4e6d8cf1d..9e2531355a02 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -69,26 +69,27 @@ struct Orthtree_traits_base_for_dimension { The following diagram showing the 3d case may be a useful reference: - 5 * - | * 3 - | / z+ - |/ * y+ - 0 *------+------* 1 | * - /| |/ - / | +-----* x+ - 2 * | - * 4 - - This lookup table may also be helpful: - - | Direction | bitset | number | Enum | - | --------- | ------ | ------ | ----- | - | `-x` | 000 | 0 | LEFT | - | `+x` | 001 | 1 | RIGHT | - | `-y` | 010 | 2 | FRONT | - | `+y` | 011 | 3 | BACK | - | `-z` | 100 | 4 | DOWN | - | `+z` | 101 | 5 | UP | + * 3 * + * | * 4 + * | / y+ + * |/ * + * 0 *------+------* 1 | + * /| | + * / | +-----* x+ + * 5 * | / + * * 2 / + * * z+ + * + * This lookup table may also be helpful: + * + * | Direction | bitset | number | Enum | + * | --------- | ------ | ------ | ----- | + * | `-x` | 000 | 0 | LEFT | + * | `+x` | 001 | 1 | RIGHT | + * | `-y` | 010 | 2 | DOWN | + * | `+y` | 011 | 3 | UP | + * | `-z` | 100 | 4 | BACK | + * | `+z` | 101 | 5 | FRONT | */ using Adjacency = int; /// @} @@ -126,8 +127,8 @@ struct Orthtree_traits_base_for_dimension> { enum Adjacency { LEFT, RIGHT, - FRONT, - BACK + DOWN, + UP }; /// @} @@ -160,29 +161,6 @@ struct Orthtree_traits_base_for_dimension> { using Sphere_d = typename K::Sphere_3; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; -#if 0 - enum Adjacency { - LEFT, - RIGHT, - FRONT, - BACK, - DOWN, - UP - }; - /// \cond SKIP_IN_MANUAL - enum Child { - LEFT_BOTTOM_FRONT, - RIGHT_BOTTOM_FRONT, - LEFT_BOTTOM_BACK, - RIGHT_BOTTOM_BACK, - LEFT_TOP_FRONT, - RIGHT_TOP_FRONT, - LEFT_TOP_BACK, - RIGHT_TOP_BACK - }; - -#else - enum Adjacency { LEFT, RIGHT, @@ -202,7 +180,6 @@ struct Orthtree_traits_base_for_dimension> { LEFT_TOP_FRONT, RIGHT_TOP_FRONT }; -#endif /// \endcond /// @} From 58d26ed40e935749cf06342a059d87744054d1c5 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 25 Jan 2024 07:25:03 +0000 Subject: [PATCH 349/520] Make parameter const& --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 688c61a8e461..4ce1017de03e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -1086,7 +1086,7 @@ class Orthtree { \return the index of the adjacent node if it exists, nothing otherwise. */ - Maybe_node_index adjacent_node(Node_index n, Local_coordinates direction) const { + Maybe_node_index adjacent_node(Node_index n, const Local_coordinates& direction) const { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 From b5945b4219683f3d2d613607cb98854c9d7bda85 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 25 Jan 2024 07:26:33 +0000 Subject: [PATCH 350/520] Fix warning --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index d7e28bbbdc85..ad5065aa0c2a 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -108,7 +108,6 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< auto distribute_node_contents_object() { return [&](Node_index n, Tree& tree, const Point_d& /* center */) -> void { Node_data& ndata = tree.data(n); - auto traits = tree.traits(); for (int i = 0; i < 8; ++i) { Node_index child = tree.child(n, i); Node_data& child_data = tree.data(child); From 585467ab36c02a1a4712bcea1debf206c19a23d1 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 25 Jan 2024 10:39:47 +0000 Subject: [PATCH 351/520] As the bbox of nodes gets constructed from Epeck::FT we have to take the upper bounds of intervals --- Orthtree/include/CGAL/Orthtree.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 4ce1017de03e..e4eb15ca0559 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -485,9 +485,10 @@ class Orthtree { Bbox_dimensions size = m_side_per_depth[depth(n)]; for (int i = 0; i < Dimension::value; i++) { - min_corner[i] = conv(m_bbox_min[i] + int(global_coordinates(n)[i]) * size[i]); - max_corner[i] = conv(m_bbox_min[i] + int(global_coordinates(n)[i] + 1) * size[i]); + min_corner[i] = approx(m_bbox_min[i] + int(global_coordinates(n)[i]) * size[i]).inf(); + max_corner[i] = approx(m_bbox_min[i] + int(global_coordinates(n)[i] + 1) * size[i]).sup(); } + return {std::apply(m_traits.construct_point_d_object(), min_corner), std::apply(m_traits.construct_point_d_object(), max_corner)}; } From d515e42c39a179aef4b4dbe20bf789be76462e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 25 Jan 2024 10:19:47 +0100 Subject: [PATCH 352/520] fix doc warning --- Orthtree/include/CGAL/Orthtree.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e4eb15ca0559..01aaa5500f20 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -157,9 +157,6 @@ class Orthtree { using Property_map = Properties::Experimental::Property_array_handle; #endif - /// \cond SKIP_IN_MANUAL - /// \endcond - /// @} private: // data members : @@ -265,9 +262,6 @@ class Orthtree { Orthtree& operator=(Orthtree&& other) = delete; - // move constructor - /// \endcond - /// \name Tree Building /// @{ From 787fb84dc6fa96776777f2056fcaa5b517ed0a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 25 Jan 2024 14:17:36 +0100 Subject: [PATCH 353/520] pass on the user manual --- Orthtree/doc/Orthtree/Orthtree.txt | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index cb0bbbde973a..5f7b1a52c3a4 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -23,7 +23,7 @@ structures in dimensions 4 and higher. This package provides a general data structure `Orthtree` along with aliases for `Quadtree` and `Octree`. These trees can be constructed with custom contents and split predicates, and iterated on with -various traversal methods. +various traversal methods. Orthants can be orthotopes and not only hypercubes. \cgalFigureBegin{Orthtree_fig, orthtree.png} Building an %orthtree in 3D (%octree) from a point cloud. @@ -56,18 +56,11 @@ The split predicate is a user-defined functor that determines whether a node needs to be split. Custom predicates can easily be defined if the existing ones do not match users' needs. -The following example shows the construction of an Octree from a vector of points. -`octree.refine(10, 1)` uses the default split predicate, which -enforces a max-depth and a maximum number of inliers per node ("bucket size"). -Nodes are split if their depth is less than 10, and they contain more than one inlier. - -\cgalExample{Orthtree/octree_build_from_point_vector.cpp} - \subsection Section_Orthtree_Quadtree Building a Quadtree The `Orthtree` class may be templated with `Orthtree_traits_point<>` with a 2d dimension tag and thus behave as a %quadtree. -For convenience, the alias `Quadtree` is provided. +For convenience, the alias `CGAL::Quadtree` is provided. The following example shows the construction of %quadtree from a vector of `Point_2` objects. `quadtree.refine(10, 5)` uses the default split predicate, which @@ -80,7 +73,7 @@ Nodes are split if their depth is less than 10, and they contain more than 5 inl \subsection Section_Orthtree_Point_Vector Building an Octree `Orthtree_traits_point<>` can also be templated with a 3d dimension tag and thus -behave as an %octree. For convenience, the alias `Octree` is provided. +behave as an %octree. For convenience, the alias `CGAL::Octree` is provided. The following example shows how to create an %octree from a vector of `Point_3` objects. As with the %quadtree example, we use the default split predicate. @@ -141,10 +134,10 @@ number of different solutions for traversing the tree. Because our orthtree is a form of connected acyclic undirected graph, it is possible to navigate between any two nodes. What that means in practice is that given a node on the tree, it is possible to access any other node using the right set of operations. -The `Node_index` type provides a handle on a node, and the `orthree` class provides methods +The `Node_index` type provides a handle on a node, and the `orthtree` class provides methods that enable the user to retrieve the indices of each of its children as well as its parent (if they exist). -Given any node index `node`, the `n`th child of that node can be found with `orthtree.child(node, n)`. +Given any node index `nid`, the `n`th child of that node can be found with `orthtree.child(nid, n)`. For an octree, values of `n` from 0-7 provide access to the different children. For non-root nodes, it is possible to access parent nodes using the `orthtree.parent()` accessor. @@ -188,7 +181,7 @@ The following example shows how to define a custom traversal that only traverses \cgalExample{Orthtree/octree_traversal_custom.cpp} -\subsection Comparison of Traversals +\subsection Section_Orthtree_Cmp_Trav Comparison of Traversals Figure \cgalFigureRef{Orthtree_traversal_fig} shows in which order nodes are visited depending on the traversal method used. @@ -286,10 +279,15 @@ For nontrivial point counts, the naive approach's calculation time dwarfs that o A prototype code was implemented by Pierre Alliez and improved by Tong Zhao and Cédric Portaneri. From this prototype code, the package was -developed by Jackson Campolatarro as part of the Google Summer of Code -2020. Simon Giraudot, supervisor of the GSoC internship, completed and +developed by Jackson Campolattaro as part of the Google Summer of Code 2020. +Simon Giraudot, supervisor of the GSoC internship, completed and finalized the package for integration in CGAL 5.3. Pierre Alliez provided kind help and advice all the way through. +Starting with CGAL 6.0 an API improvement of the Orthtree package was released. +Most notably, the orthtree does not need to store anything. Data to be stored +by the node are managed through a mechanism of dynamic properties. +This improvement was done by Jackson Campolattaro thanks to a funding provided by +INRIA, together with help from GeometryFactory. */ From 57fbda8835b81f222097c96e78578b169f843a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 25 Jan 2024 16:06:06 +0100 Subject: [PATCH 354/520] pass on the doc but the Orthtree class --- .../CollectionPartitioningOrthtreeTraits.h | 7 +-- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 22 +++---- .../doc/Orthtree/Concepts/OrthtreeTraversal.h | 8 +-- Orthtree/doc/Orthtree/PackageDescription.txt | 1 + Orthtree/include/CGAL/Octree.h | 12 ++-- .../include/CGAL/Orthtree/Nearest_neighbors.h | 27 ++++---- .../include/CGAL/Orthtree/Split_predicates.h | 21 ++++--- Orthtree/include/CGAL/Orthtree/Traversals.h | 20 +++--- .../CGAL/Orthtree_traits_base_for_dimension.h | 62 +++++++------------ .../include/CGAL/Orthtree_traits_face_graph.h | 19 +++--- Orthtree/include/CGAL/Orthtree_traits_point.h | 21 +++---- Orthtree/include/CGAL/Quadtree.h | 6 +- 12 files changed, 97 insertions(+), 129 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 079c75ac7266..40628d8b26e4 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -2,9 +2,8 @@ \ingroup PkgOrthtreeConcepts \cgalConcept - In addition to the requirements described in the OrthtreeTraits concept, - the concept `CollectionPartitioningOrthtreeTraits` defines the requirements for the - traits class of a `CGAL::Orthtree` which supports nearest-neighbor searching. + Refinement of the OrthtreeTraits concept, adding requirements for the + traits class of a `CGAL::Orthtree` in order to supports nearest-neighbor searching. Nearest neighbor searches expect a tree with nodes which contain list types. The leaf nodes of the tree represent an exclusive partition of the elements contained in the tree. @@ -49,7 +48,7 @@ class CollectionPartitioningOrthtreeTraits { /// @{ /*! - * Function used to construct an object of type `Get_geometric_object_for_element`. + * constructs an object of type `Get_geometric_object_for_element`. */ Get_geometric_object_for_element get_geometric_object_for_element_object() const; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index d05fa0ee7c46..a3e149263d78 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -25,7 +25,7 @@ class OrthtreeTraits /*! A random access iterator type to enumerate the - %Cartesian coordinates of a point. + %Cartesian coordinates of a point of type `Point_d`. */ using Cartesian_const_iterator_d = unspecified_type; @@ -35,7 +35,7 @@ class OrthtreeTraits using Node_data = unspecified_type; /*! - * \brief Number type which can take on values indicating adjacency directions. + * \brief Integral number type which can take on values indicating adjacency directions. * * Must be able to take on values ranging from 0 to the number of faces of the (hyper)rectangle type, equivalent to 2 * D. */ @@ -59,13 +59,13 @@ class OrthtreeTraits * Each node of a tree has an associated `Node_data` value. * For most nodes, this is set by `Distribute_node_contents`, but that is not possible for the root node. * Instead, this functor initializes the `Node_data` of the root node. - * It takes no arguments, and return an instance of `Node_data`. + * It takes no arguments, and returns an instance of `Node_data`. * * Provides the operator: * `Node_data operator()()` * * Typically, the `Node_data` of the root node contains all the elements in the tree. - * For a tree in which each node contains an `std::span()` this function would return the span containing all items. + * For a tree in which each node contains a span (such as `std::span()`) this function would return the span containing all items. * */ using Construct_root_node_contents = unspecified_type; @@ -81,13 +81,11 @@ class OrthtreeTraits /*! * \brief Functor which distributes a node's contents to its children. * - * The functor takes a node index, a tree reference, and a `Point_d` which is the center of the node. - * * Provides the operator: * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` * * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` - * to access the contents of the node and each of its children. + * to access its children and the contents of the node. * It must distribute the contents of the node to each of its children. * For a tree in which each node contains a span, this may mean rearranging the contents of the original node * and producing spans containing a subset of its contents for each of its children. @@ -108,27 +106,27 @@ class OrthtreeTraits /// @{ /*! - * Function used to construct an object of type `Construct_root_node_bbox`. + * constructs an object of type `Construct_root_node_bbox`. */ Construct_root_node_bbox construct_root_node_bbox_object() const; /*! - * Function used to construct an object of type `Construct_root_node_contents`. + * constructs an object of type `Construct_root_node_contents`. */ Construct_root_node_contents construct_root_node_contents_object() const; /*! - * Function used to construct an object of type `Distribute_node_contents`. + * constructs an object of type `Distribute_node_contents`. */ Distribute_node_contents distribute_node_contents_object() const; /*! - * Function used to construct an object of type `Locate_halfspace`. + * constructs an object of type `Locate_halfspace`. */ Locate_halfspace locate_halfspace_object() const; /*! - * Function used to construct an object of type `Construct_point_d`. + * constructs an object of type `Construct_point_d`. */ Construct_point_d construct_point_d_object() const; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index 175360bda6fa..4ce5dfbfe335 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -3,11 +3,7 @@ \ingroup PkgOrthtreeConcepts \cgalConcept - \brief A traversal provides the functions needed to traverse the - nodes of an orthtree. - - A traversal is used to iterate on a tree with a user-selected order - (e.g., preorder, postorder). + \brief Requirements for defining a traversal strategie of an orthtree. \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtrees::Preorder_traversal} @@ -23,7 +19,7 @@ class OrthtreeTraversal { using Node_index = unspecified_type; ///< Index type of the orthtree to be traversed /*! - \brief returns the first node to iterate to, given the root of the orthtree. + \brief returns the first node of the traversal. */ Node_index first_index() const; diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 3502660edd39..a178656aedaf 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -42,6 +42,7 @@ Quadtree, Octree and Orthtree Reference \cgalCRPSection{Concepts} - `OrthtreeTraits` - `OrthtreeTraversal` +- `CollectionPartitioningOrthtreeTraits` \cgalCRPSection{Classes} - `CGAL::Quadtree` diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index b5b7b282b452..167d8d211014 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -22,15 +22,11 @@ namespace CGAL { /*! \ingroup PkgOrthtreeRef - \brief Alias that specializes the `Orthtree` class to a 3D octree. + \brief Alias that specializes the `Orthtree` class to a 3D octree storing 3D points. - These two types are exactly equivalent: - - `Octree` - - `Orthtree>>`. - - \tparam GeomTraits must be a model of `Kernel` - \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` - \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` + \tparam GeomTraits a model of `Kernel` + \tparam PointRange a model of `Range` whose value type is the key type of `PointMap` + \tparam PointMap a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` */ template < typename GeomTraits, diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 06c48548d2ed..8f2b47fe1edd 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -117,18 +117,15 @@ namespace Orthtrees { /*! \ingroup PkgOrthtreeNeighbors - \brief finds the `k` points within a specific radius that are - nearest to the center of the sphere `query`. + \brief finds at most `k` points within a specific radius that are + nearest to the center of the sphere `query`: if `query` does not contain + at least `k` points, only contained points will be returned. - This function guarantees that there are no closer points than the ones returned, - but it does not guarantee that it will return at least `k` points. - For a query where the search radius encloses `k` or fewer points, all enclosed points will be returned. - If the search radius is too small, no points may be returned. This function is useful when the user already knows how sparse the points are, or if they do not care about points that are too far away. Setting a small radius may have performance benefits. - \tparam Tree must be an orthtree with traits which are a model of CollectionPartitioningOrthtreeTraits + \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` \tparam OutputIterator must be a model of `OutputIterator` that accepts points \param orthtree the tree to search within @@ -173,13 +170,13 @@ OutputIterator nearest_k_neighbors_in_radius( Nearest neighbors are outputted in order of increasing distance to `query`. - \tparam Tree must be an orthtree with traits which are a model of `CollectionPartitioningOrthtreeTraits` + \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` \tparam OutputIterator a model of `OutputIterator` that accepts `Point_d` objects. \param orthtree the tree to search within - \param query query point. - \param k number of neighbors. - \param output output iterator. + \param query query point + \param k number of neighbors to find + \param output output iterator */ template OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Point& query, @@ -193,15 +190,15 @@ OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Poin \ingroup PkgOrthtreeNeighbors \brief finds the points in the sphere `query`. - Nearest neighbors are outputted in order of increasing distance to + Points are outputted in order of increasing distance to the center of the sphere. - \tparam Tree must be an orthtree with traits which are a model of `CollectionPartitioningOrthtreeTraits` + \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` \tparam OutputIterator a model of `OutputIterator` that accepts `Point_d` objects. \param orthtree the tree to search within - \param query query sphere. - \param output output iterator. + \param query query sphere + \param output output iterator */ template OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Sphere& query, OutputIterator output) { diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index 1e97bafd4205..934e599cba06 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -18,6 +18,9 @@ namespace CGAL { +template +class Orthtree; + namespace Orthtrees { /*! @@ -39,10 +42,10 @@ class Maximum_number_of_inliers { m_bucket_size(bucket_size) {} /*! - \brief returns `true` if `i` should be split, `false` otherwise. + \brief returns `true` if the node with index `i` should be split, `false` otherwise. */ - template - bool operator()(Node_index i, const Tree &tree) const { + template + bool operator()(typename Orthtree::Node_index i, const Orthtree &tree) const { return (tree.data(i).size() > m_bucket_size); } @@ -66,10 +69,10 @@ class Maximum_depth { Maximum_depth(std::size_t max_depth) : m_max_depth(max_depth) {} /*! - \brief returns `true` if `i` should be split, `false` otherwise. + \brief returns `true` if the node with index `i` should be split, `false` otherwise. */ - template - bool operator()(Node_index i, const Tree &tree) const { + template + bool operator()(typename Orthtree::Node_index i, const Orthtree &tree) const { return (tree.depth(i) < m_max_depth); } @@ -102,10 +105,10 @@ class Maximum_depth_and_maximum_number_of_inliers { m_max_depth(max_depth), m_bucket_size(bucket_size) {} /*! - \brief returns `true` if `i` should be split, `false` otherwise. + \brief returns `true` if the node with index `i` should be split, `false` otherwise. */ - template - bool operator()(Node_index i, const Tree &tree) const { + template + bool operator()(typename Orthtree::Node_index i, const Orthtree &tree) const { std::size_t num_points = tree.data(i).size(); std::size_t depth = tree.depth(i); return (num_points > m_bucket_size && depth < m_max_depth); diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index e7ac61193b3a..4f84bccb7fbd 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -22,19 +22,14 @@ namespace CGAL { -/// \cond SKIP_IN_MANUAL -// todo: is this necessary? -// Forward declaration -template -class Orthtree; -/// \endcond - namespace Orthtrees { /*! \ingroup PkgOrthtreeTraversal \brief A class used for performing a preorder traversal. + \tparam Tree an instance of `Orthtree` + A preorder traversal starts from the root towards the leaves. \cgalModels{OrthtreeTraversal} @@ -77,6 +72,8 @@ struct Preorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a postorder traversal. + \tparam Tree an instance of `Orthtree` + A postorder traversal starts from the leaves towards the root. \cgalModels{OrthtreeTraversal} @@ -106,6 +103,8 @@ struct Postorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal on leaves only. + \tparam Tree an instance of `Orthtree` + All non-leave nodes are ignored. \cgalModels{OrthtreeTraversal} @@ -142,7 +141,10 @@ struct Leaves_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal of a specific depth level. - All trees at another depth are ignored. If the selected depth is + \tparam Tree an instance of `Orthtree` + + All tree nodes at another depth are ignored. If the selected depth is + All tree nodes at another depth are ignored. If the selected depth is higher than the maximum depth of the orthtree, no node will be traversed. \cgalModels{OrthtreeTraversal} @@ -190,7 +192,7 @@ struct Level_traversal { } }; -} // Orthtree +} // Orthtrees } // CGAL #endif //CGAL_ORTHTREE_TRAVERSALS_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index 9e2531355a02..b64cfdd7506e 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -28,10 +28,8 @@ namespace CGAL { The class `Orthtree_traits_base_for_dimension` is a base class providing common choices for types and functors. The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. - \tparam K model of `Kernel`. - \tparam DimensionTag is a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. - - \cgalModels{OrthtreeTraits} + \tparam K a model of `Kernel`. + \tparam DimensionTag a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. \sa `CGAL::Orthtree_traits_point` \sa `CGAL::Orthtree_traits_face_graph` @@ -50,25 +48,25 @@ struct Orthtree_traits_base_for_dimension { using Sphere_d = typename K::Sphere_d; using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; /*! - Adjacency type. - - \note This type is used to identify adjacency directions with - easily understandable keywords (left, right, up, etc.) and is thus - mainly useful for `Dimension_tag<2>` and `Dimension_tag<3>`. In - higher dimensions, such keywords do not exist and this type is - simply an integer. Conversions from this integer to bitsets still - work but do not provide any easier API for adjacency selection. - - Two directions along each axis in %Cartesian space, relative to a node. - - Directions are mapped to numbers as 3-bit integers in the 3d case or as 2-bit integers in the 2d case. - In the 3d case the numbers 6 and 7 are not used because there are only 6 different directions. - - The first two bits indicate the axis (00 = x, 01 = y, 10 = z), - the third bit indicates the direction along that axis (0 = -, 1 = +). - - The following diagram showing the 3d case may be a useful reference: - + * Adjacency type. + * + * \note This type is used to identify adjacency directions with + * easily understandable keywords (left, right, up, down, ...) and is thus + * mainly useful for `Dimension_tag<2>` and `Dimension_tag<3>`. In + * higher dimensions, such keywords do not exist and this type is + * simply an integer. Conversions from this integer to bitsets still + * work but do not provide any user-friendly API for adjacency selection. + * + * Two directions along each axis in %Cartesian space, relative to a node. + * + * Directions are mapped to numbers as 3-bit integers in the 3d case or as 2-bit integers in the 2d case. + * In the 3d case the numbers 6 and 7 are not used because there are only 6 different directions. + * + * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), + * the third bit indicates the direction along that axis (0 = -, 1 = +). + * + * The following diagram and table showing the 3d case may be a useful reference (2d case is identical with one dimension less): + * * 3 * * | * 4 * | / y+ @@ -94,8 +92,6 @@ struct Orthtree_traits_base_for_dimension { using Adjacency = int; /// @} - /// \name Operations - /// @{ auto construct_point_d_object() const { return [](auto... Args) -> Point_d { std::initializer_list args_list{Args...}; @@ -108,13 +104,10 @@ struct Orthtree_traits_base_for_dimension { return a < b; }; } - /// @} }; template struct Orthtree_traits_base_for_dimension> { - /// \name Types - /// @{ using Node_index = std::size_t; using Kernel = K; using Dimension = Dimension_tag<2>; @@ -130,10 +123,7 @@ struct Orthtree_traits_base_for_dimension> { DOWN, UP }; - /// @} - /// \name Operations - /// @{ auto construct_point_d_object() const { return [](const FT& x, const FT& y) -> Point_d { return {x, y}; @@ -145,13 +135,10 @@ struct Orthtree_traits_base_for_dimension> { return a < b; }; } - /// @} }; template struct Orthtree_traits_base_for_dimension> { - /// \name Types - /// @{ using Node_index = std::size_t; using Kernel = K; using Dimension = Dimension_tag<3>; @@ -169,7 +156,7 @@ struct Orthtree_traits_base_for_dimension> { BACK, FRONT }; - /// \cond SKIP_IN_MANUAL + enum Child { LEFT_BOTTOM_BACK, RIGHT_BOTTOM_BACK, @@ -180,11 +167,7 @@ struct Orthtree_traits_base_for_dimension> { LEFT_TOP_FRONT, RIGHT_TOP_FRONT }; - /// \endcond - /// @} - /// \name Operations - /// @{ auto construct_point_d_object() const { return [](const FT& x, const FT& y, const FT& z) -> Point_d { return {x, y, z}; @@ -196,7 +179,6 @@ struct Orthtree_traits_base_for_dimension> { return a < b; }; } - /// @} }; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index ad5065aa0c2a..17218fa9bbe8 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -35,10 +35,7 @@ to which the minimal extend of a node should be provided. \tparam TriangleMesh a model of `FaceListGraph` with all faces being triangles \tparam VertexPointMap a property map associating points to the vertices of `TriangleMesh` -\todo check how to adapt to non regular splits (cubes vs rectangular cuboid) - \cgalModels{OrthtreeTraits} -\sa `CGAL::Orthtree_traits_base_for_dimension` */ template struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< @@ -129,15 +126,21 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// Recommended split predicate to pass to `Orthtree::refine()` function so /// that the octree is refined until a node is either empty or has an extent - /// that would be smaller after split than the value provided to the constructor. + /// that would be smaller after split than the corresponding value provided to the constructor. class Split_predicate_node_min_extent { - FT m_min_extent; + std::array m_min_extent; public: - /// constructor with `me` being the minimal value a node extent could be. - Split_predicate_node_min_extent(FT me) + /// constructor with `me` being the minimal value a node extent could be + /// (same value for all dimension). + Split_predicate_node_min_extent(const FT& me) + : m_min_extent({me, me, me}) {} + + /// constructor with `me` being the minimal value a node extent could be + /// (one value per dimension). + Split_predicate_node_min_extent(const std::array& me) : m_min_extent(me) {} /*! @@ -150,7 +153,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< Bbox_d bb = tree.bbox(ni); for (int i = 0; i < 3; ++i) - if (((bb.max)()[i] - (bb.min)()[i]) < 2 * m_min_extent) + if (((bb.max)()[i] - (bb.min)()[i]) < 2 * m_min_extent[i]) return false; return true; } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 488a9b360bd2..1bb4e4a9d361 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -62,16 +62,16 @@ void reassign_points( /*! \ingroup PkgOrthtreeTraits - The class `Orthtree_traits_point` can be used as a template parameter of - the `Orthtree` class. + Traits class for defining an orthtree of points using the class `CGAL::Orthtree`. \tparam GeomTraits model of `Kernel`. - \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` - \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` + \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` and whose iterator type is model of `RandomAccessIterator` + \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is a point type from `GeomTraits` matching the current dimension + \tparam DimensionTag a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. \warning The input point set is not copied. It is used directly and is rearranged by the `Orthtree`. Altering the point range - after creating the orthtree might leave it in an invalid state. + after creating the orthtree will leave it in an invalid state. \cgalModels{OrthtreeTraits} \sa `CGAL::Octree` @@ -89,27 +89,24 @@ template < > struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension { public: - /// \name Types /// @{ + using Node_data = boost::iterator_range; + /// @} using Base = Orthtree_traits_base_for_dimension; using Self = Orthtree_traits_point; using Tree = Orthtree; - using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_type; using Node_index = typename Base::Node_index; - /// @} + Orthtree_traits_point( PointRange& points, PointMap point_map = PointMap() ) : m_points(points), m_point_map(point_map) {} - /// \name Operations - /// @{ - auto construct_root_node_bbox_object() const { return [&]() -> typename Self::Bbox_d { @@ -161,8 +158,6 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension` - - `Orthtree>>`. + \brief Alias that specializes the `Orthtree` class to a 2D quadtree storing 2D points. \tparam GeomTraits must be a model of `Kernel` \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` From caad3b3cf0d628f85d51f6d0d0b672f70ce7a160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 25 Jan 2024 16:37:05 +0100 Subject: [PATCH 355/520] pass on Orthtree class --- Orthtree/include/CGAL/Orthtree.h | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 01aaa5500f20..ef287192865f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -140,7 +140,6 @@ class Orthtree { * \brief A model of `ConstRange` whose value type is `Node_index` and its iterator type is `ForwardIterator`. */ #ifdef DOXYGEN_RUNNING - using Node_index_range = unspecified_type; #else using Node_index_range = boost::iterator_range>; @@ -268,9 +267,7 @@ class Orthtree { /*! \brief recursively subdivides the orthtree until it meets the given criteria. - The split predicate is an `std::function` that takes a `Node_index` and an Orthtree reference, and - returns a Boolean value (where `true` implies that the corresponding node needs to - be split, `false` that the node should be a leaf). + The split predicate should return `true` if the node should be split and false` otherwise. This function may be called several times with different predicates: in that case, nodes already split are left unaltered, @@ -426,10 +423,9 @@ class Orthtree { This method allows iteration over the nodes of the tree with a user-selected order (preorder, postorder, leaves-only, etc.). - \tparam Traversal model of `OrthtreeTraversal` that provides functions - compatible with the type of the orthtree + \tparam Traversal a model of `OrthtreeTraversal` - \param traversal the instance of `Traversal` used + \param traversal class defining the traversal strategy \return a forward input iterator over the node indices of the tree */ @@ -450,8 +446,7 @@ class Orthtree { /*! \brief convenience method for using a traversal without constructing it yourself - \tparam Traversal model of `OrthtreeTraversal` that provides functions - compatible with the type of the orthtree + \tparam Traversal a model of `OrthtreeTraversal` \param args Arguments to to pass to the traversal's constructor, excluding the first (always an orthtree reference) @@ -500,7 +495,7 @@ class Orthtree { \param name the name of the new property \param default_value the default value assigned to nodes for this property - \return pair of the property map and a boolean which is True if the property needed to be created + \return pair of the property map and a boolean which is `true` if the property needed to be created */ template std::pair, bool> @@ -637,7 +632,7 @@ class Orthtree { \param rhs the other orthtree - \return boolean, True if the trees have the same topology + \return `true` if the trees have the same topology, and `false` otherwise */ bool operator==(const Self& rhs) const { @@ -658,7 +653,7 @@ class Orthtree { \param rhs the other orthtree - \return boolean, False if the trees have the same topology + \return `false` if the trees have the same topology, and `true` otherwise */ bool operator!=(const Self& rhs) const { return !operator==(rhs); @@ -674,7 +669,7 @@ class Orthtree { \param n index of the node to check. - \return true of the node is a leaf, false otherwise. + \return `true` of the node is a leaf, `false` otherwise. */ bool is_leaf(Node_index n) const { return !m_node_children[n].has_value(); @@ -685,7 +680,7 @@ class Orthtree { \param n index of the node to check. - \return True of the node is a root, False otherwise. + \return `true` if the node is a root, `false` otherwise. */ bool is_root(Node_index n) const { return n == 0; @@ -962,7 +957,7 @@ class Orthtree { } /*! - * \brief finds the center point of a node. + * \brief returns the center point of a node. * * @param n index of the node to find the center point for * @@ -1024,7 +1019,7 @@ class Orthtree { \param lhs an Orthtree \param rhs another Orthtree - \return True if lhsTree and rhsTree have the same topology + \return `true` if `lhs` and `rhs` have the same topology, and `false` otherwise */ static bool is_topology_equal(const Self& lhs, const Self& rhs) { return is_topology_equal(lhs.root(), lhs, rhs.root(), rhs); From 878d90b20ea1b8151ff1b089bf506ef5c0d04d4d Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 25 Jan 2024 17:46:41 +0000 Subject: [PATCH 356/520] Core: Use Expr::is_zero() of AST --- Number_types/include/CGAL/CORE_Expr.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Number_types/include/CGAL/CORE_Expr.h b/Number_types/include/CGAL/CORE_Expr.h index ae0b603b1edc..efeb5ad9363a 100644 --- a/Number_types/include/CGAL/CORE_Expr.h +++ b/Number_types/include/CGAL/CORE_Expr.h @@ -115,6 +115,14 @@ template <> class Algebraic_structure_traits< CORE::Expr > }; */ }; + class Is_zero + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& x ) const { + return x.isZero(); + } + }; + }; template <> class Real_embeddable_traits< CORE::Expr > From 13485521941e288a70bf427b2dea9864fb74ef47 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 25 Jan 2024 18:23:49 +0000 Subject: [PATCH 357/520] Add Is_one --- CGAL_Core/examples/Core/CMakeLists.txt | 1 + CGAL_Core/examples/Core/zero-one.cpp | 32 ++++++++++++++++++++++++++ Number_types/include/CGAL/CORE_Expr.h | 16 +++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 CGAL_Core/examples/Core/zero-one.cpp diff --git a/CGAL_Core/examples/Core/CMakeLists.txt b/CGAL_Core/examples/Core/CMakeLists.txt index d4513e22dea4..6d9e732db932 100644 --- a/CGAL_Core/examples/Core/CMakeLists.txt +++ b/CGAL_Core/examples/Core/CMakeLists.txt @@ -9,4 +9,5 @@ if(NOT CGAL_Core_FOUND) return() endif() +create_single_source_cgal_program("zero-one.cpp") create_single_source_cgal_program("delaunay.cpp") diff --git a/CGAL_Core/examples/Core/zero-one.cpp b/CGAL_Core/examples/Core/zero-one.cpp new file mode 100644 index 000000000000..cfc5a903c19b --- /dev/null +++ b/CGAL_Core/examples/Core/zero-one.cpp @@ -0,0 +1,32 @@ + +#include + +typedef CORE::Expr Real; + +int main() +{ + Real r(3.14); + + CGAL::is_zero(r); + + CGAL::is_one(r); + + r = CGAL::sqrt(r); + + + CGAL::is_zero(r); + + CGAL::is_one(r); + + r = r * r; + + CGAL::is_zero(r); + + CGAL::is_one(r); + + r = r - r; + + CGAL::is_zero(r); + + return 0; +} diff --git a/Number_types/include/CGAL/CORE_Expr.h b/Number_types/include/CGAL/CORE_Expr.h index efeb5ad9363a..3ddb4c7f78fd 100644 --- a/Number_types/include/CGAL/CORE_Expr.h +++ b/Number_types/include/CGAL/CORE_Expr.h @@ -123,6 +123,22 @@ template <> class Algebraic_structure_traits< CORE::Expr > } }; + class Is_one + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& x ) const { + double inf, sup; + x.doubleInterval(inf,sup); + if((inf > 1) || (sup < 1)){ + return false; + } + if((inf == 1) && (sup == 1)){ + return true; + } + return x.cmp(Type::getOne()); + } + }; + }; template <> class Real_embeddable_traits< CORE::Expr > From 468b81f9f29c26910b4fa8e7fb7571af4cd7dfb0 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 26 Jan 2024 08:47:16 +0100 Subject: [PATCH 358/520] Update Number_types/include/CGAL/CORE_Expr.h Co-authored-by: Marc Glisse --- Number_types/include/CGAL/CORE_Expr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Number_types/include/CGAL/CORE_Expr.h b/Number_types/include/CGAL/CORE_Expr.h index 3ddb4c7f78fd..f38681a08e24 100644 --- a/Number_types/include/CGAL/CORE_Expr.h +++ b/Number_types/include/CGAL/CORE_Expr.h @@ -135,7 +135,7 @@ template <> class Algebraic_structure_traits< CORE::Expr > if((inf == 1) && (sup == 1)){ return true; } - return x.cmp(Type::getOne()); + return x.cmp(Type::getOne()) == 0; } }; From 60328a74a7227eb3de179f9089537769ed7fa8eb Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 26 Jan 2024 08:48:28 +0100 Subject: [PATCH 359/520] Update Number_types/include/CGAL/CORE_Expr.h Co-authored-by: Marc Glisse --- Number_types/include/CGAL/CORE_Expr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Number_types/include/CGAL/CORE_Expr.h b/Number_types/include/CGAL/CORE_Expr.h index f38681a08e24..e0f72a223266 100644 --- a/Number_types/include/CGAL/CORE_Expr.h +++ b/Number_types/include/CGAL/CORE_Expr.h @@ -132,7 +132,7 @@ template <> class Algebraic_structure_traits< CORE::Expr > if((inf > 1) || (sup < 1)){ return false; } - if((inf == 1) && (sup == 1)){ + if(inf == sup){ return true; } return x.cmp(Type::getOne()) == 0; From f155ad8f521cad9d7183627b15891cf7925307c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 26 Jan 2024 09:58:26 +0100 Subject: [PATCH 360/520] please MSVC 2017 --- Property_map/include/CGAL/Property_container.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index bbc52260b245..9cb1bb9c6b25 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -43,7 +43,10 @@ class Property_array_base { virtual void copy(const Property_array_base& other) = 0; + // desactived as MSVC 2017 as an issue with that but it is not currently used. +#if 0 virtual void move(Property_array_base&& other) = 0; +#endif virtual void append(const Property_array_base& other) = 0; @@ -106,11 +109,14 @@ class Property_array : public Property_array_base { CGAL_precondition(m_active_indices.size() == m_data.size()); } +// desactived as MSVC 2017 as an issue with that but it is not currently used. +#if 0 virtual void move(Property_array_base&& other_base) override { auto&& other = static_cast&&>(other_base); m_data = std::move(other.m_data); CGAL_precondition(m_active_indices.size() == m_data.size()); } +#endif virtual void append(const Property_array_base& other_base) override { auto& other = dynamic_cast&>(other_base); From 88f01a5f1d4a48ccccd0946385501c571bd209f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 26 Jan 2024 16:06:05 +0100 Subject: [PATCH 361/520] get rid of EPECK we compute sizes approximatively and sets bboxes using those value: computation is always the same so values are the same if the rounding mode is not changed. The only exception is for the max value on the bbox of the root where the max is used instead of being computed using precomputed extent sizes --- Orthtree/include/CGAL/Orthtree.h | 50 ++++++++++++++------------------ 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ef287192865f..aac97607475e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -174,10 +173,9 @@ class Orthtree { Property_array& m_node_parents; Property_array& m_node_children; - using Bbox_dimensions = std::array; - CGAL::NT_converter conv; - Bbox_dimensions m_bbox_min; - std::vector m_side_per_depth; /* side length per node's depth */ + using Bbox_dimensions = std::array; + Bbox m_bbox; + std::vector m_side_per_depth; /* precomputed (potentially approximated) side length per node's depth */ Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ @@ -211,16 +209,15 @@ class Orthtree { m_node_properties.emplace(); // init bbox with first values found - auto bbox = m_traits.construct_root_node_bbox_object()(); + m_bbox = m_traits.construct_root_node_bbox_object()(); // Determine dimensions of the root bbox Bbox_dimensions size; - for (int i = 0; i < Dimension::value; ++i) { - m_bbox_min[i] = (bbox.min)()[i]; - size[i] = CGAL::Exact_predicates_exact_constructions_kernel::FT((bbox.max)()[i]) - m_bbox_min[i]; + for (int i = 0; i < Dimension::value; ++i) + { + size[i] = (m_bbox.max)()[i] - (m_bbox.min)()[i]; } - // save orthtree attributes m_side_per_depth.push_back(size); data(root()) = m_traits.construct_root_node_contents_object()(); @@ -237,7 +234,7 @@ class Orthtree { m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), m_node_children(m_node_properties.get_property("children")), - m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth) {} + m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) {} // move constructor Orthtree(Orthtree&& other) : @@ -248,7 +245,7 @@ class Orthtree { m_node_coordinates(m_node_properties.get_property("coordinates")), m_node_parents(m_node_properties.get_property("parents")), m_node_children(m_node_properties.get_property("children")), - m_bbox_min(other.m_bbox_min), m_side_per_depth(other.m_side_per_depth) + m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. other.m_node_properties.emplace(); @@ -468,14 +465,16 @@ class Orthtree { \return the bounding box of the node n */ Bbox bbox(Node_index n) const { - using Cartesian_coordinate = std::array; Cartesian_coordinate min_corner, max_corner; - Bbox_dimensions size = m_side_per_depth[depth(n)]; - + std::size_t node_depth = depth(n); + Bbox_dimensions size = m_side_per_depth[node_depth]; + const std::size_t last_coord = std::pow(2,node_depth)-1; for (int i = 0; i < Dimension::value; i++) { - min_corner[i] = approx(m_bbox_min[i] + int(global_coordinates(n)[i]) * size[i]).inf(); - max_corner[i] = approx(m_bbox_min[i] + int(global_coordinates(n)[i] + 1) * size[i]).sup(); + min_corner[i] = (m_bbox.min)()[i] + int(global_coordinates(n)[i]) * size[i]; + max_corner[i] = std::size_t(global_coordinates(n)[i]) == last_coord + ? (m_bbox.max)()[i] + : (m_bbox.min)()[i] + int(global_coordinates(n)[i] + 1) * size[i]; } return {std::apply(m_traits.construct_point_d_object(), min_corner), @@ -637,7 +636,7 @@ class Orthtree { bool operator==(const Self& rhs) const { // Identical trees should have the same bounding box - if (rhs.m_bbox_min != m_bbox_min || rhs.m_side_per_depth[0] != m_side_per_depth[0]) + if (rhs.m_bbox != m_bbox || rhs.m_side_per_depth[0] != m_side_per_depth[0]) return false; // Identical trees should have the same depth @@ -945,7 +944,7 @@ class Orthtree { Bbox_dimensions size = m_side_per_depth.back(); Bbox_dimensions child_size; for (int i = 0; i < Dimension::value; ++i) - child_size[i] = size[i] * 0.5; + child_size[i] = size[i] / FT(2); m_side_per_depth.push_back(child_size); } @@ -969,17 +968,12 @@ class Orthtree { Bbox_dimensions size = m_side_per_depth[depth(n)]; // Determine the location this node should be split - Bbox_dimensions bary; - + std::array bary; for (std::size_t i = 0; i < Dimension::value; i++) - bary[i] = FT(global_coordinates(n)[i]) * size[i] + size[i] / FT(2) + m_bbox_min[i]; - - // Convert that location into a point + // use the same expression as for the bbox computation + bary[i] = (m_bbox.min)()[i] + int(2 * global_coordinates(n)[i]+1) * ( size[i] / FT(2) ); - std::array tmp; - for (std::size_t i = 0; i < Dimension::value; i++) - tmp[i] = conv(bary[i]); - return std::apply(m_traits.construct_point_d_object(), tmp); + return std::apply(m_traits.construct_point_d_object(), bary); } /*! From ffb32d95a04c7d9b383eba460fd96f96f0d2eaf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 29 Jan 2024 22:58:54 +0100 Subject: [PATCH 362/520] fix dependencies --- Orthtree/package_info/Orthtree/dependencies | 3 --- .../package_info/Shape_regularization/dependencies | 2 -- 2 files changed, 5 deletions(-) diff --git a/Orthtree/package_info/Orthtree/dependencies b/Orthtree/package_info/Orthtree/dependencies index 5f485c987a3b..d8fbb010a7a2 100644 --- a/Orthtree/package_info/Orthtree/dependencies +++ b/Orthtree/package_info/Orthtree/dependencies @@ -1,18 +1,15 @@ Algebraic_foundations -Arithmetic_kernel Cartesian_kernel Circulator Distance_2 Distance_3 Filtered_kernel Hash_map -Homogeneous_kernel Installation Intersections_2 Intersections_3 Interval_support Kernel_23 -Kernel_d Modular_arithmetic Number_types Orthtree diff --git a/Shape_regularization/package_info/Shape_regularization/dependencies b/Shape_regularization/package_info/Shape_regularization/dependencies index fed80db41d63..b07b869d7157 100644 --- a/Shape_regularization/package_info/Shape_regularization/dependencies +++ b/Shape_regularization/package_info/Shape_regularization/dependencies @@ -1,5 +1,4 @@ Algebraic_foundations -Arithmetic_kernel BGL Cartesian_kernel Circulator @@ -7,7 +6,6 @@ Distance_2 Distance_3 Filtered_kernel Hash_map -Homogeneous_kernel Installation Intersections_2 Intersections_3 From 546c0c842a7a7ee1447bff0e7069d86a266cb328 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 30 Jan 2024 14:34:37 +0100 Subject: [PATCH 363/520] small doc corrections --- Orthtree/include/CGAL/Orthtree.h | 8 ++++---- Orthtree/include/CGAL/Orthtree/Traversals.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index aac97607475e..b7aa9772b21a 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -264,14 +264,14 @@ class Orthtree { /*! \brief recursively subdivides the orthtree until it meets the given criteria. - The split predicate should return `true` if the node should be split and false` otherwise. + The split predicate should return `true` if a leaf node should be split and false` otherwise. This function may be called several times with different predicates: in that case, nodes already split are left unaltered, while nodes that were not split and for which `split_predicate` returns `true` are split. - \param split_predicate determines whether or not a node needs to be subdivided. + \param split_predicate determines whether or not a leaf node needs to be subdivided. */ void refine(const Split_predicate& split_predicate) { @@ -402,10 +402,10 @@ class Orthtree { const Traits& traits() const { return m_traits; } /*! - \brief provides read-only access to the root node, and by + \brief provides access to the root node, and by extension the rest of the tree. - \return a const reference to the root node of the tree. + \return Node_index of the root node. */ Node_index root() const { return 0; } diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 4f84bccb7fbd..acd1db941f61 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -105,7 +105,7 @@ struct Postorder_traversal { \tparam Tree an instance of `Orthtree` - All non-leave nodes are ignored. + All non-leaf nodes are ignored. \cgalModels{OrthtreeTraversal} */ From 11f0a842de3566e9d180b8ee02f30a2321b3292e Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 30 Jan 2024 16:47:53 +0100 Subject: [PATCH 364/520] changing dimension and degree in orthtree(_traits) to int --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 6 +- Orthtree/examples/Orthtree/orthtree_build.cpp | 8 +-- .../Orthtree/quadtree_build_manually.cpp | 8 +-- Orthtree/include/CGAL/Octree.h | 2 +- Orthtree/include/CGAL/Orthtree.h | 63 +++++++++---------- .../include/CGAL/Orthtree/Nearest_neighbors.h | 4 +- .../CGAL/Orthtree_traits_base_for_dimension.h | 21 +++---- .../include/CGAL/Orthtree_traits_face_graph.h | 9 +-- Orthtree/include/CGAL/Orthtree_traits_point.h | 22 +++---- Orthtree/include/CGAL/Quadtree.h | 2 +- .../Shape_detection/Efficient_RANSAC/Octree.h | 2 +- 11 files changed, 71 insertions(+), 76 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index a3e149263d78..546c12116379 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -6,9 +6,9 @@ template parameter of the `CGAL::Orthtree` class. \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} - \cgalHasModels{CGAL::Orthtree_traits_base_for_dimension< K, DimensionTag >} + \cgalHasModels{CGAL::Orthtree_traits_base_for_dimension< K, dimension >} \cgalHasModelsEnd */ class OrthtreeTraits @@ -18,7 +18,7 @@ class OrthtreeTraits /// \name Types /// @{ using Node_index = unspecified_type; ///< An integer type for nodes - using Dimension = unspecified_type; ///< Dimension type (see `CGAL::Dimension_tag`). + constexpr int dimension; ///< Dimension. using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` using Point_d = unspecified_type; ///< Point type. using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` types. diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 25abed86defe..f4d6d2300f9d 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -7,12 +7,12 @@ #include // Type Declarations -using Dimension = CGAL::Dimension_tag<4>; -using Kernel = CGAL::Epick_d; +const int dimension = 4; +using Kernel = CGAL::Epick_d >; using Point_d = Kernel::Point_d; using Point_vector = std::vector; using Traits = CGAL::Orthtree_traits_point; -using Traits2 = CGAL::Orthtree_traits_point::value_type>, CGAL::Dimension_tag<4>>; +using Traits2 = CGAL::Orthtree_traits_point::value_type>, dimension>; using Orthtree = CGAL::Orthtree; int main() @@ -22,7 +22,7 @@ int main() Point_vector points_dd; for (std::size_t i = 0; i < 500; ++ i) { - std::array init{}; + std::array init{}; for (double& v : init) v = r.get_double(-1., 1.); points_dd.emplace_back (init.begin(), init.end()); diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 708b680213b7..601917bf3c97 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -13,10 +13,10 @@ namespace CGAL { struct empty_type { }; -template -struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension { +template +struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension { - using Self = Orthtree_traits_empty; + using Self = Orthtree_traits_empty; using Tree = Orthtree; using Node_data = std::array; @@ -40,7 +40,7 @@ struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension>>; +using EmptyQuadtree = CGAL::Orthtree>; int main() { diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 167d8d211014..625494bb2be1 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -33,7 +33,7 @@ template < typename PointRange, typename PointMap = Identity_property_map::value_type> > -using Octree = Orthtree>>; +using Octree = Orthtree>; } // namespace CGAL diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index b7aa9772b21a..19d9ea20b4e7 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -76,7 +75,7 @@ class Orthtree { /// \name Traits Types /// @{ - using Dimension = typename Traits::Dimension; ///< Dimension of the tree + static constexpr int dimension = Traits::dimension; ///< Dimension of the tree using Kernel = typename Traits::Kernel; ///< Kernel type. using FT = typename Traits::FT; ///< Number type. using Point = typename Traits::Point_d; ///< Point type. @@ -99,7 +98,7 @@ class Orthtree { /*! * \brief Degree of the tree (number of children of non-leaf nodes). */ - using Degree = Dimension_tag<(2 << (Dimension::value - 1))>; + static constexpr int degree = (2 << (dimension - 1)); /*! * \brief Index of a given node in the tree; the root always has index 0. @@ -119,7 +118,7 @@ class Orthtree { `z` is greater, and so on for higher dimensions if needed. Used to represent a node's relationship to the center of its parent. */ - using Local_coordinates = std::bitset; + using Local_coordinates = std::bitset; /*! \brief Coordinates representing this node's relationship @@ -128,7 +127,7 @@ class Orthtree { Each value `(x, y, z, ...)` of global coordinates is calculated by doubling the parent's global coordinates and adding the local coordinates. */ - using Global_coordinates = std::array; + using Global_coordinates = std::array; /*! * \brief A predicate that determines whether a node must be split when refining a tree. @@ -173,7 +172,7 @@ class Orthtree { Property_array& m_node_parents; Property_array& m_node_children; - using Bbox_dimensions = std::array; + using Bbox_dimensions = std::array; Bbox m_bbox; std::vector m_side_per_depth; /* precomputed (potentially approximated) side length per node's depth */ @@ -214,7 +213,7 @@ class Orthtree { // Determine dimensions of the root bbox Bbox_dimensions size; - for (int i = 0; i < Dimension::value; ++i) + for (int i = 0; i < dimension; ++i) { size[i] = (m_bbox.max)()[i] - (m_bbox.min)()[i]; } @@ -298,7 +297,7 @@ class Orthtree { if (!is_leaf(current)) { // Process each of its children - for (int i = 0; i < Degree::value; ++i) + for (int i = 0; i < degree; ++i) todo.push(child(current, i)); } } @@ -381,7 +380,7 @@ class Orthtree { split(*neighbor); // Add newly created children to the queue - for (int i = 0; i < Degree::value; ++i) { + for (int i = 0; i < degree; ++i) { leaf_nodes.push(child(*neighbor, i)); } } @@ -465,12 +464,12 @@ class Orthtree { \return the bounding box of the node n */ Bbox bbox(Node_index n) const { - using Cartesian_coordinate = std::array; + using Cartesian_coordinate = std::array; Cartesian_coordinate min_corner, max_corner; std::size_t node_depth = depth(n); Bbox_dimensions size = m_side_per_depth[node_depth]; const std::size_t last_coord = std::pow(2,node_depth)-1; - for (int i = 0; i < Dimension::value; i++) { + for (int i = 0; i < dimension; i++) { min_corner[i] = (m_bbox.min)()[i] + int(global_coordinates(n)[i]) * size[i]; max_corner[i] = std::size_t(global_coordinates(n)[i]) == last_coord ? (m_bbox.max)()[i] @@ -585,9 +584,9 @@ class Orthtree { // Find the index of the correct sub-node Local_coordinates local_coords; - std::size_t dimension = 0; + std::size_t dim = 0; for (const auto& r: cartesian_range(center, point)) - local_coords[dimension++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); + local_coords[dim++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); // Find the correct sub-node of the current node node_for_point = child(node_for_point, local_coords.to_ulong()); @@ -740,7 +739,7 @@ class Orthtree { */ Local_coordinates local_coordinates(Node_index n) const { Local_coordinates result; - for (std::size_t i = 0; i < Dimension::value; ++i) + for (std::size_t i = 0; i < dimension; ++i) result[i] = global_coordinates(n)[i] & 1; return result; } @@ -826,7 +825,7 @@ class Orthtree { std::size_t local_coords = local_coordinates(n).to_ulong(); // The last child has no more siblings - if (int(local_coords) == Degree::value - 1) + if (int(local_coords) == degree - 1) return {}; // The next sibling is the child of the parent with the following local coordinates @@ -898,7 +897,7 @@ class Orthtree { return node; if (!is_leaf(node)) - for (int i = 0; i < Degree::value; ++i) + for (int i = 0; i < degree; ++i) todo.push(child(node, i)); } @@ -910,7 +909,7 @@ class Orthtree { Only leaf nodes should be split. When a node is split it is no longer a leaf node. - A number of `Degree::value` children are constructed automatically, and their values are set. + The full set of `degree` children are constructed automatically, and their values are set. Contents of this node are _not_ propagated automatically, this is responsibility of the `distribute_node_contents_object` in the traits class. @@ -923,8 +922,8 @@ class Orthtree { // Split the node to create children using Local_coordinates = Local_coordinates; - m_node_children[n] = m_node_properties.emplace_group(Degree::value); - for (std::size_t i = 0; i < Degree::value; i++) { + m_node_children[n] = m_node_properties.emplace_group(degree); + for (std::size_t i = 0; i < degree; i++) { Node_index c = *m_node_children[n] + i; @@ -932,7 +931,7 @@ class Orthtree { CGAL_assertion(n != *m_node_children[n] + i); Local_coordinates local_coordinates{i}; - for (int i = 0; i < Dimension::value; i++) + for (int i = 0; i < dimension; i++) m_node_coordinates[c][i] = (2 * m_node_coordinates[n][i]) + local_coordinates[i]; m_node_depths[c] = m_node_depths[n] + 1; m_node_parents[c] = n; @@ -943,7 +942,7 @@ class Orthtree { // Update the side length map with the dimensions of the children Bbox_dimensions size = m_side_per_depth.back(); Bbox_dimensions child_size; - for (int i = 0; i < Dimension::value; ++i) + for (int i = 0; i < dimension; ++i) child_size[i] = size[i] / FT(2); m_side_per_depth.push_back(child_size); } @@ -968,8 +967,8 @@ class Orthtree { Bbox_dimensions size = m_side_per_depth[depth(n)]; // Determine the location this node should be split - std::array bary; - for (std::size_t i = 0; i < Dimension::value; i++) + std::array bary; + for (std::size_t i = 0; i < dimension; i++) // use the same expression as for the bbox computation bary[i] = (m_bbox.min)()[i] + int(2 * global_coordinates(n)[i]+1) * ( size[i] / FT(2) ); @@ -996,7 +995,7 @@ class Orthtree { if (!lhsTree.is_leaf(lhsNode)) { // Check all the children - for (int i = 0; i < Degree::value; ++i) { + for (int i = 0; i < degree; ++i) { // If any child cell is different, they're not the same if (!is_topology_equal(lhsTree.child(lhsNode, i), lhsTree, rhsTree.child(rhsNode, i), rhsTree)) @@ -1022,11 +1021,11 @@ class Orthtree { /*! \brief finds the directly adjacent node in a specific direction - \pre `direction.to_ulong < 2 * Dimension::value` + \pre `direction.to_ulong < 2 * dimension` Adjacent nodes are found according to several properties: - adjacent nodes may be larger than the seek node, but never smaller - - a node has at most `2 * Dimension::value` different adjacent nodes (in 3D: left, right, up, down, front, back) + - a node has at most `2 * dimension` different adjacent nodes (in 3D: left, right, up, down, front, back) - adjacent nodes are not required to be leaf nodes Here's a diagram demonstrating the concept for a quadtree: @@ -1076,7 +1075,7 @@ class Orthtree { // direction: 000 001 010 011 100 101 // Nodes only have up to 2*dim different adjacent nodes (since boxes have 6 sides) - CGAL_precondition(direction.to_ulong() < Dimension::value * 2); + CGAL_precondition(direction.to_ulong() < dimension * 2); // The root node has no adjacent nodes! if (is_root(n)) return {}; @@ -1085,16 +1084,16 @@ class Orthtree { bool sign = direction[0]; // The first two bits indicate the dimension/axis (x, y, z) - uint8_t dimension = uint8_t((direction >> 1).to_ulong()); + uint8_t dim = uint8_t((direction >> 1).to_ulong()); // Create an offset so that the bit-significance lines up with the dimension (e.g., 1, 2, 4 --> 001, 010, 100) - int8_t offset = (uint8_t) 1 << dimension; + int8_t offset = (uint8_t) 1 << dim; // Finally, apply the sign to the offset offset = (sign ? offset : -offset); // Check if this child has the opposite sign along the direction's axis - if (local_coordinates(n)[dimension] != sign) { + if (local_coordinates(n)[dim] != sign) { // This means the adjacent node is a direct sibling, the offset can be applied easily! return {child(parent(n), local_coordinates(n).to_ulong() + offset)}; } @@ -1120,7 +1119,7 @@ class Orthtree { \param adjacency which way to find the adjacent node relative to this one */ Maybe_node_index adjacent_node(Node_index n, Adjacency adjacency) const { - return adjacent_node(n, std::bitset(static_cast(adjacency))); + return adjacent_node(n, std::bitset(static_cast(adjacency))); } /// @} @@ -1157,7 +1156,7 @@ class Orthtree { } // Otherwise, each of the children need to be checked - for (int i = 0; i < Degree::value; ++i) { + for (int i = 0; i < degree; ++i) { intersected_nodes_recursive(query, child(node, i), output); } } diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 8f2b47fe1edd..63266158f4d6 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -80,10 +80,10 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Create a list to map children to their distances std::vector children_with_distances; - children_with_distances.reserve(Tree::Degree::value); + children_with_distances.reserve(Tree::degree); // Fill the list with child nodes - for (int i = 0; i < Tree::Degree::value; ++i) { + for (int i = 0; i < Tree::degree; ++i) { auto child_node = orthtree.child(node, i); // Add a child to the list, with its distance diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h index b64cfdd7506e..88043d53cdf0 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h @@ -14,7 +14,6 @@ #include -#include #include #include #include @@ -26,22 +25,22 @@ namespace CGAL { \ingroup PkgOrthtreeTraits The class `Orthtree_traits_base_for_dimension` is a base class providing common choices for types and functors. - The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. + The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. \tparam K a model of `Kernel`. - \tparam DimensionTag a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. + \tparam dim dimension of the ambient Euclidean space. - \sa `CGAL::Orthtree_traits_point` + \sa `CGAL::Orthtree_traits_point` \sa `CGAL::Orthtree_traits_face_graph` */ -template +template struct Orthtree_traits_base_for_dimension { /// \name Types /// @{ using Node_index = std::size_t; using Kernel = K; - using Dimension = DimensionTag; + static constexpr int dimension = dim; using FT = typename K::FT; using Point_d = typename K::Point_d; using Bbox_d = typename K::Iso_box_d; @@ -52,7 +51,7 @@ struct Orthtree_traits_base_for_dimension { * * \note This type is used to identify adjacency directions with * easily understandable keywords (left, right, up, down, ...) and is thus - * mainly useful for `Dimension_tag<2>` and `Dimension_tag<3>`. In + * mainly useful in 2d and 3d. In * higher dimensions, such keywords do not exist and this type is * simply an integer. Conversions from this integer to bitsets still * work but do not provide any user-friendly API for adjacency selection. @@ -107,10 +106,10 @@ struct Orthtree_traits_base_for_dimension { }; template -struct Orthtree_traits_base_for_dimension> { +struct Orthtree_traits_base_for_dimension { using Node_index = std::size_t; using Kernel = K; - using Dimension = Dimension_tag<2>; + static constexpr int dimension = 2; using FT = typename K::FT; using Point_d = typename K::Point_2; using Bbox_d = typename K::Iso_rectangle_2; @@ -138,10 +137,10 @@ struct Orthtree_traits_base_for_dimension> { }; template -struct Orthtree_traits_base_for_dimension> { +struct Orthtree_traits_base_for_dimension { using Node_index = std::size_t; using Kernel = K; - using Dimension = Dimension_tag<3>; + static constexpr int dimension = 3; using FT = typename K::FT; using Point_d = typename K::Point_3; using Bbox_d = typename K::Iso_cuboid_3; diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 17218fa9bbe8..baaebe51d70f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -39,8 +39,7 @@ to which the minimal extend of a node should be provided. */ template struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< - typename Kernel_traits::value_type>::type, - Dimension_tag<3> > { + typename Kernel_traits::value_type>::type, 3 > { Orthtree_traits_face_graph(const TriangleMesh& pm, VertexPointMap vpm) : m_pm(pm), m_vpm(vpm) {} @@ -49,14 +48,12 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// @{ using Base = Orthtree_traits_base_for_dimension< - typename Kernel_traits::value_type>::type, - Dimension_tag<3> >; + typename Kernel_traits::value_type>::type, 3 >; using Node_index = typename Base::Node_index; using Self = Orthtree_traits_face_graph; using Tree = Orthtree; using Point_d = typename Self::Point_d; - using Dimension = typename Self::Dimension; using Bbox_d = typename Self::Bbox_d; using FT = typename Self::FT; using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; @@ -77,7 +74,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< auto construct_root_node_bbox_object() const { return [&]() -> Bbox_d { - std::array min = {0.0, 0}, max = {0.0, 0}; + std::array min = {0.0, 0}, max = {0.0, 0}; if (faces(m_pm).begin() != faces(m_pm).end()) { const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); min = {p.x(), p.y(), p.z()}; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 1bb4e4a9d361..ca801cd5871d 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -27,11 +27,11 @@ template void reassign_points( Tree& tree, PointMap& point_map, typename Tree::Node_index n, const typename Tree::Point& center, typename Tree::Node_data points, - std::bitset coord = {}, std::size_t dimension = 0 + std::bitset coord = {}, std::size_t dimension = 0 ) { // Root case: reached the last dimension - if (dimension == Tree::Dimension::value) { + if (dimension == Tree::dimension) { tree.data(tree.child(n, coord.to_ulong())) = points; return; } @@ -49,12 +49,12 @@ void reassign_points( ); // Further subdivide the first side of the split - std::bitset coord_left = coord; + std::bitset coord_left = coord; coord_left[dimension] = false; reassign_points(tree, point_map, n, center, {points.begin(), split_point}, coord_left, dimension + 1); // Further subdivide the second side of the split - std::bitset coord_right = coord; + std::bitset coord_right = coord; coord_right[dimension] = true; reassign_points(tree, point_map, n, center, {split_point, points.end()}, coord_right, dimension + 1); } @@ -67,7 +67,7 @@ void reassign_points( \tparam GeomTraits model of `Kernel`. \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` and whose iterator type is model of `RandomAccessIterator` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is a point type from `GeomTraits` matching the current dimension - \tparam DimensionTag a tag representing the dimension of the ambient Euclidean space. Must be `Dimension_tag` where `d` is an integer. + \tparam dimension the dimension of the ambient Euclidean space. \warning The input point set is not copied. It is used directly and is rearranged by the `Orthtree`. Altering the point range @@ -82,20 +82,20 @@ template < typename GeomTraits, typename PointRange, typename PointMap = Identity_property_map::value_type>, - typename DimensionTag = Ambient_dimension< + int dimension = Ambient_dimension< typename std::iterator_traits::value_type, GeomTraits - > + >::value > -struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension { +struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension { public: /// \name Types /// @{ using Node_data = boost::iterator_range; /// @} - using Base = Orthtree_traits_base_for_dimension; - using Self = Orthtree_traits_point; + using Base = Orthtree_traits_base_for_dimension; + using Self = Orthtree_traits_point; using Tree = Orthtree; using Node_data_element = typename std::iterator_traits::value_type; @@ -110,7 +110,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension typename Self::Bbox_d { - std::array bbox_min, bbox_max; + std::array bbox_min, bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 3e02147073eb..5bbf2ecea8e1 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -32,7 +32,7 @@ template ::value_type> > -using Quadtree = Orthtree>>; +using Quadtree = Orthtree>; } // namespace CGAL diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h index 7971b638ad10..ceaa7fe0bda4 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h @@ -56,7 +56,7 @@ class RANSAC_octree { typedef std::vector Input_range; typedef Random_index_access_property_map Indexed_point_map; - typedef Orthtree_traits_point::type, Input_range, Indexed_point_map, Dimension_tag<3>> OTraits; + typedef Orthtree_traits_point::type, Input_range, Indexed_point_map, 3> OTraits; typedef CGAL::Orthtree Octree; From 2802d58326c26dcc18b76d293bc9eb15c35c12d9 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 30 Jan 2024 17:09:41 +0100 Subject: [PATCH 365/520] removing Maybe_node_index --- Orthtree/include/CGAL/Orthtree.h | 37 ++++++++++++++------------------ 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 19d9ea20b4e7..17a7e70a2044 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -105,11 +105,6 @@ class Orthtree { */ using Node_index = std::size_t; - /*! - * \brief Optional index of a node in the tree. - */ - using Maybe_node_index = std::optional; - /*! \brief Set of bits representing this node's relationship to its parent. @@ -169,8 +164,8 @@ class Orthtree { Property_array& m_node_contents; Property_array& m_node_depths; Property_array& m_node_coordinates; - Property_array& m_node_parents; - Property_array& m_node_children; + Property_array>& m_node_parents; + Property_array>& m_node_children; using Bbox_dimensions = std::array; Bbox m_bbox; @@ -202,8 +197,8 @@ class Orthtree { m_node_contents(m_node_properties.add_property("contents")), m_node_depths(m_node_properties.add_property("depths", 0)), m_node_coordinates(m_node_properties.add_property("coordinates")), - m_node_parents(m_node_properties.add_property("parents")), - m_node_children(m_node_properties.add_property("children")) { + m_node_parents(m_node_properties.add_property>("parents")), + m_node_children(m_node_properties.add_property>("children")) { m_node_properties.emplace(); @@ -231,8 +226,8 @@ class Orthtree { m_node_contents(m_node_properties.get_property("contents")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), - m_node_parents(m_node_properties.get_property("parents")), - m_node_children(m_node_properties.get_property("children")), + m_node_parents(m_node_properties.get_property>("parents")), + m_node_children(m_node_properties.get_property>("children")), m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) {} // move constructor @@ -242,8 +237,8 @@ class Orthtree { m_node_contents(m_node_properties.get_property("contents")), m_node_depths(m_node_properties.get_property("depths")), m_node_coordinates(m_node_properties.get_property("coordinates")), - m_node_parents(m_node_properties.get_property("parents")), - m_node_children(m_node_properties.get_property("children")), + m_node_parents(m_node_properties.get_property>("parents")), + m_node_children(m_node_properties.get_property>("children")), m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. @@ -430,7 +425,7 @@ class Orthtree { Node_index first = traversal.first_index(); - auto next = [=](const Self&, Node_index index) -> Maybe_node_index { + auto next = [=](const Self&, Node_index index) -> std::optional { return traversal.next_index(index); }; @@ -816,7 +811,7 @@ class Orthtree { \return the index of the next sibling of n if n is not the last node in its parent, otherwise nothing. */ - const Maybe_node_index next_sibling(Node_index n) const { + const std::optional next_sibling(Node_index n) const { // Root node has no siblings if (is_root(n)) return {}; @@ -840,17 +835,17 @@ class Orthtree { \return The index of the next sibling of the parent of n if n is not the root and its parent has a sibling, otherwise nothing. */ - const Maybe_node_index next_sibling_up(Node_index n) const { + const std::optional next_sibling_up(Node_index n) const { // the root node has no next sibling up if (n == 0) return {}; - auto up = Maybe_node_index{parent(n)}; + auto up = std::optional{parent(n)}; while (up) { if (next_sibling(*up)) return {next_sibling(*up)}; - up = is_root(*up) ? Maybe_node_index{} : Maybe_node_index{parent(*up)}; + up = is_root(*up) ? std::optional{} : std::optional{parent(*up)}; } return {}; @@ -884,7 +879,7 @@ class Orthtree { \return the index of the `d`th first child, nothing if the tree is not deep enough. */ - Maybe_node_index first_child_at_depth(Node_index n, std::size_t d) const { + std::optional first_child_at_depth(Node_index n, std::size_t d) const { std::queue todo; todo.push(n); @@ -1069,7 +1064,7 @@ class Orthtree { \return the index of the adjacent node if it exists, nothing otherwise. */ - Maybe_node_index adjacent_node(Node_index n, const Local_coordinates& direction) const { + std::optional adjacent_node(Node_index n, const Local_coordinates& direction) const { // Direction: LEFT RIGHT DOWN UP BACK FRONT // direction: 000 001 010 011 100 101 @@ -1118,7 +1113,7 @@ class Orthtree { \param n index of the node to find a neighbor of \param adjacency which way to find the adjacent node relative to this one */ - Maybe_node_index adjacent_node(Node_index n, Adjacency adjacency) const { + std::optional adjacent_node(Node_index n, Adjacency adjacency) const { return adjacent_node(n, std::bitset(static_cast(adjacency))); } From 434d3e95e428b0ca632e9711d120b915bb599d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 07:59:56 +0100 Subject: [PATCH 366/520] draft for an implementation of bbox corner that are consistent between nodes of different depths --- Orthtree/include/CGAL/Orthtree.h | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 17a7e70a2044..cb9575456b46 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -462,6 +462,37 @@ class Orthtree { using Cartesian_coordinate = std::array; Cartesian_coordinate min_corner, max_corner; std::size_t node_depth = depth(n); + +#if 1 + // naive implementation for now + Bbox_dimensions size = m_side_per_depth[node_depth]; + + auto get_coord = [&](int gc, int node_depth, int i) + { + // an odd coordinate will be first compute at the current depth, + // while an even coordinate has already been computed at a previous depth. + // So while the coordinate is even, we decrease the depth to end up of the first + // non-even coordinate to compute it (with particular case for bbox limits). + // Note that is depth becomes too large, we might end up with incorrect coordinates + // due to rounding errors. + if (gc == (1 << node_depth)) return (m_bbox.max)()[i]; // gc == 2^node_depth + if (gc == 0) return (m_bbox.min)()[i]; + if (gc % 2 !=0) return (m_bbox.min)()[i] + gc * size[i]; + int nd = node_depth; + do{ + --nd; + gc = gc >> 1; + } + while((gc&1)==0); // while even, shift + return (m_bbox.min)()[i] + gc * m_side_per_depth[nd][i]; + }; + + for (int i = 0; i < Dimension::value; i++) + { + min_corner[i]=get_coord(global_coordinates(n)[i], node_depth, i); + max_corner[i]=get_coord(global_coordinates(n)[i]+1, node_depth, i); + } +#else Bbox_dimensions size = m_side_per_depth[node_depth]; const std::size_t last_coord = std::pow(2,node_depth)-1; for (int i = 0; i < dimension; i++) { @@ -470,7 +501,7 @@ class Orthtree { ? (m_bbox.max)()[i] : (m_bbox.min)()[i] + int(global_coordinates(n)[i] + 1) * size[i]; } - +#endif return {std::apply(m_traits.construct_point_d_object(), min_corner), std::apply(m_traits.construct_point_d_object(), max_corner)}; } From ef1fc5227855b9620659988645d3d6175fae4043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 08:18:30 +0100 Subject: [PATCH 367/520] clean up implementation and also use it in barycenter --- Orthtree/include/CGAL/Orthtree.h | 74 +++++++++++++------------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index cb9575456b46..d77aeb8e6fa6 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -448,6 +448,28 @@ class Orthtree { return traverse(Traversal{*this, std::forward(args)...}); } + // TODO shall we document it? + FT + compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const + { + // an odd coordinate will be first compute at the current depth, + // while an even coordinate has already been computed at a previous depth. + // So while the coordinate is even, we decrease the depth to end up of the first + // non-even coordinate to compute it (with particular case for bbox limits). + // Note that is depth becomes too large, we might end up with incorrect coordinates + // due to rounding errors. + if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth + if (gc == 0) return (m_bbox.min)()[ci]; + if (gc % 2 !=0) return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[depth][ci]; + std::size_t nd = depth; + do{ + --nd; + gc = gc >> 1; + } + while((gc&1)==0); // while even, shift + return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[nd][ci]; + } + /*! \brief constructs the bounding box of a node. @@ -463,45 +485,11 @@ class Orthtree { Cartesian_coordinate min_corner, max_corner; std::size_t node_depth = depth(n); -#if 1 - // naive implementation for now - Bbox_dimensions size = m_side_per_depth[node_depth]; - - auto get_coord = [&](int gc, int node_depth, int i) - { - // an odd coordinate will be first compute at the current depth, - // while an even coordinate has already been computed at a previous depth. - // So while the coordinate is even, we decrease the depth to end up of the first - // non-even coordinate to compute it (with particular case for bbox limits). - // Note that is depth becomes too large, we might end up with incorrect coordinates - // due to rounding errors. - if (gc == (1 << node_depth)) return (m_bbox.max)()[i]; // gc == 2^node_depth - if (gc == 0) return (m_bbox.min)()[i]; - if (gc % 2 !=0) return (m_bbox.min)()[i] + gc * size[i]; - int nd = node_depth; - do{ - --nd; - gc = gc >> 1; - } - while((gc&1)==0); // while even, shift - return (m_bbox.min)()[i] + gc * m_side_per_depth[nd][i]; - }; - for (int i = 0; i < Dimension::value; i++) { - min_corner[i]=get_coord(global_coordinates(n)[i], node_depth, i); - max_corner[i]=get_coord(global_coordinates(n)[i]+1, node_depth, i); - } -#else - Bbox_dimensions size = m_side_per_depth[node_depth]; - const std::size_t last_coord = std::pow(2,node_depth)-1; - for (int i = 0; i < dimension; i++) { - min_corner[i] = (m_bbox.min)()[i] + int(global_coordinates(n)[i]) * size[i]; - max_corner[i] = std::size_t(global_coordinates(n)[i]) == last_coord - ? (m_bbox.max)()[i] - : (m_bbox.min)()[i] + int(global_coordinates(n)[i] + 1) * size[i]; + min_corner[i]=compute_cartesian_coordinate(global_coordinates(n)[i], node_depth, i); + max_corner[i]=compute_cartesian_coordinate(global_coordinates(n)[i]+1, node_depth, i); } -#endif return {std::apply(m_traits.construct_point_d_object(), min_corner), std::apply(m_traits.construct_point_d_object(), max_corner)}; } @@ -988,15 +976,11 @@ class Orthtree { * @return the center point of node n */ Point barycenter(Node_index n) const { - - // Determine the side length of this node - Bbox_dimensions size = m_side_per_depth[depth(n)]; - - // Determine the location this node should be split - std::array bary; - for (std::size_t i = 0; i < dimension; i++) - // use the same expression as for the bbox computation - bary[i] = (m_bbox.min)()[i] + int(2 * global_coordinates(n)[i]+1) * ( size[i] / FT(2) ); + std::size_t node_depth = depth(n); + // the barycenter is computed as the lower corner of the lexicographically top child node + std::array bary; + for (std::size_t i = 0; i < Dimension::value; i++) + bary[i] = compute_cartesian_coordinate(2 * global_coordinates(n)[i]+1, node_depth+1, i); return std::apply(m_traits.construct_point_d_object(), bary); } From 3763febfa8e1813c4283fe708474f57320d7083e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 17:36:41 +0100 Subject: [PATCH 368/520] fix compilation issues --- Orthtree/include/CGAL/Orthtree.h | 6 +++--- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d77aeb8e6fa6..d07af33f9a10 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -485,7 +485,7 @@ class Orthtree { Cartesian_coordinate min_corner, max_corner; std::size_t node_depth = depth(n); - for (int i = 0; i < Dimension::value; i++) + for (int i = 0; i < dimension; i++) { min_corner[i]=compute_cartesian_coordinate(global_coordinates(n)[i], node_depth, i); max_corner[i]=compute_cartesian_coordinate(global_coordinates(n)[i]+1, node_depth, i); @@ -978,8 +978,8 @@ class Orthtree { Point barycenter(Node_index n) const { std::size_t node_depth = depth(n); // the barycenter is computed as the lower corner of the lexicographically top child node - std::array bary; - for (std::size_t i = 0; i < Dimension::value; i++) + std::array bary; + for (std::size_t i = 0; i < dimension; i++) bary[i] = compute_cartesian_coordinate(2 * global_coordinates(n)[i]+1, node_depth+1, i); return std::apply(m_traits.construct_point_d_object(), bary); diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index baaebe51d70f..a0773582dc9b 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -74,7 +74,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< auto construct_root_node_bbox_object() const { return [&]() -> Bbox_d { - std::array min = {0.0, 0}, max = {0.0, 0}; + std::array min = {0.0, 0}, max = {0.0, 0}; if (faces(m_pm).begin() != faces(m_pm).end()) { const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); min = {p.x(), p.y(), p.z()}; From 85dd76867631715ff16f360d4d0815a415480901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 30 Jan 2024 17:59:33 +0100 Subject: [PATCH 369/520] handle calls to barycenter for leaf nodes --- Orthtree/include/CGAL/Orthtree.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d07af33f9a10..c91d2e01eba4 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -452,6 +452,7 @@ class Orthtree { FT compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const { + CGAL_assertion(depth <= m_side_per_depth.size()); // an odd coordinate will be first compute at the current depth, // while an even coordinate has already been computed at a previous depth. // So while the coordinate is even, we decrease the depth to end up of the first @@ -460,7 +461,13 @@ class Orthtree { // due to rounding errors. if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth if (gc == 0) return (m_bbox.min)()[ci]; - if (gc % 2 !=0) return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[depth][ci]; + if (gc % 2 !=0) + { + FT size = depth < m_side_per_depth.size() + ? m_side_per_depth[depth][ci] + : m_side_per_depth[depth-1][ci]/FT(2); + return (m_bbox.min)()[ci] + int(gc) * size; + } std::size_t nd = depth; do{ --nd; From a9a37c1d0ebe7205cf1bfcb4a03404b8887684ce Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 31 Jan 2024 07:47:43 +0000 Subject: [PATCH 370/520] Fix conversion warning --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index c91d2e01eba4..32e54c2b0512 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -986,7 +986,7 @@ class Orthtree { std::size_t node_depth = depth(n); // the barycenter is computed as the lower corner of the lexicographically top child node std::array bary; - for (std::size_t i = 0; i < dimension; i++) + for (int i = 0; i < dimension; i++) bary[i] = compute_cartesian_coordinate(2 * global_coordinates(n)[i]+1, node_depth+1, i); return std::apply(m_traits.construct_point_d_object(), bary); From ecc30d8b0f54fe2f8bd22758b871a49e7e1f0ad0 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 31 Jan 2024 16:22:40 +0100 Subject: [PATCH 371/520] removing Locate_halfspace --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 13 ------------- Orthtree/include/CGAL/Orthtree.h | 4 ++-- Orthtree/include/CGAL/Orthtree_traits_point.h | 4 +--- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 546c12116379..471ada54c719 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -70,14 +70,6 @@ class OrthtreeTraits */ using Construct_root_node_contents = unspecified_type; - /*! - * \brief Functor to locate in which halfspace a number of type `FT` is located with respect to another number of type `FT`. - * - * The functor is used by `Orthtree::locate()` to identify in which leaf node a point is located. - * `Distribute_node_contents` must use `Locate_halfspace` to guarantee consistency wich `Orthtree::locate()`. - */ - using Locate_halfspace = unspecified_type; - /*! * \brief Functor which distributes a node's contents to its children. * @@ -120,11 +112,6 @@ class OrthtreeTraits */ Distribute_node_contents distribute_node_contents_object() const; - /*! - * constructs an object of type `Locate_halfspace`. - */ - Locate_halfspace locate_halfspace_object() const; - /*! * constructs an object of type `Construct_point_d`. */ diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 32e54c2b0512..7dfb39d6ce53 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -606,8 +606,8 @@ class Orthtree { // Find the index of the correct sub-node Local_coordinates local_coords; std::size_t dim = 0; - for (const auto& r: cartesian_range(center, point)) - local_coords[dim++] = m_traits.locate_halfspace_object()(get<0>(r), get<1>(r)); + for (const auto& r : cartesian_range(center, point)) + local_coords[dim++] = (get<0>(r) <= get<1>(r)); // Find the correct sub-node of the current node node_for_point = child(node_for_point, local_coords.to_ulong()); diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index ca801cd5871d..4aa733a9660d 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -42,9 +42,7 @@ void reassign_points( auto split_point = std::partition( points.begin(), points.end(), [&](const auto& p) -> bool { - // This should be done with cartesian iterator, - // but it seems complicated to do efficiently - return traits.locate_halfspace_object()(get(point_map, p)[int(dimension)], center[int(dimension)]); + return (get(point_map, p)[int(dimension)] < center[int(dimension)]); } ); From 7bf96722263f88f03a6fd38db6ba116aad511ad4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 31 Jan 2024 16:50:47 +0100 Subject: [PATCH 372/520] spelling --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index 4ce5dfbfe335..b95cb0908f63 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -3,7 +3,7 @@ \ingroup PkgOrthtreeConcepts \cgalConcept - \brief Requirements for defining a traversal strategie of an orthtree. + \brief Requirements for defining a traversal strategy of an orthtree. \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtrees::Preorder_traversal} From c8661d0fa87b18b723f491bf5a42a264465c76b4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 1 Feb 2024 09:46:06 +0100 Subject: [PATCH 373/520] renaming Orthtree_traits_base_for_dimension to Orthtree_traits_base removing left-over Locate_halfspace --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 2 +- .../Orthtree/quadtree_build_manually.cpp | 4 +-- ...for_dimension.h => Orthtree_traits_base.h} | 32 ++++--------------- .../include/CGAL/Orthtree_traits_face_graph.h | 6 ++-- Orthtree/include/CGAL/Orthtree_traits_point.h | 8 ++--- 5 files changed, 17 insertions(+), 35 deletions(-) rename Orthtree/include/CGAL/{Orthtree_traits_base_for_dimension.h => Orthtree_traits_base.h} (85%) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 471ada54c719..f6791c13c78a 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -8,7 +8,7 @@ \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} - \cgalHasModels{CGAL::Orthtree_traits_base_for_dimension< K, dimension >} + \cgalHasModels{CGAL::Orthtree_traits_base< K, dimension >} \cgalHasModelsEnd */ class OrthtreeTraits diff --git a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp index 601917bf3c97..2e38f406c384 100644 --- a/Orthtree/examples/Orthtree/quadtree_build_manually.cpp +++ b/Orthtree/examples/Orthtree/quadtree_build_manually.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using Kernel = CGAL::Simple_cartesian; @@ -14,7 +14,7 @@ struct empty_type { }; template -struct Orthtree_traits_empty : public Orthtree_traits_base_for_dimension { +struct Orthtree_traits_empty : public Orthtree_traits_base { using Self = Orthtree_traits_empty; using Tree = Orthtree; diff --git a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h b/Orthtree/include/CGAL/Orthtree_traits_base.h similarity index 85% rename from Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h rename to Orthtree/include/CGAL/Orthtree_traits_base.h index 88043d53cdf0..a97f0f741016 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base_for_dimension.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -9,8 +9,8 @@ // // Author(s) : Jackson Campolattaro -#ifndef ORTHTREE_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H -#define ORTHTREE_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H +#ifndef ORTHTREE_ORTHTREE_TRAITS_BASE_H +#define ORTHTREE_ORTHTREE_TRAITS_BASE_H #include @@ -24,7 +24,7 @@ namespace CGAL { /*! \ingroup PkgOrthtreeTraits - The class `Orthtree_traits_base_for_dimension` is a base class providing common choices for types and functors. + The class `Orthtree_traits_base` is a base class providing common choices for types and functors. The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. \tparam K a model of `Kernel`. @@ -35,7 +35,7 @@ namespace CGAL { */ template -struct Orthtree_traits_base_for_dimension { +struct Orthtree_traits_base { /// \name Types /// @{ using Node_index = std::size_t; @@ -97,16 +97,10 @@ struct Orthtree_traits_base_for_dimension { return Point_d{static_cast(args_list.size()), args_list.begin(), args_list.end()}; }; } - - auto locate_halfspace_object() const { - return [](const FT& a, const FT& b) -> bool { - return a < b; - }; - } }; template -struct Orthtree_traits_base_for_dimension { +struct Orthtree_traits_base { using Node_index = std::size_t; using Kernel = K; static constexpr int dimension = 2; @@ -128,16 +122,10 @@ struct Orthtree_traits_base_for_dimension { return {x, y}; }; } - - auto locate_halfspace_object() const { - return [](const FT& a, const FT& b) -> bool { - return a < b; - }; - } }; template -struct Orthtree_traits_base_for_dimension { +struct Orthtree_traits_base { using Node_index = std::size_t; using Kernel = K; static constexpr int dimension = 3; @@ -172,14 +160,8 @@ struct Orthtree_traits_base_for_dimension { return {x, y, z}; }; } - - auto locate_halfspace_object() const { - return [](const FT& a, const FT& b) -> bool { - return a < b; - }; - } }; } -#endif //ORTHTREE_ORTHTREE_TRAITS_BASE_FOR_DIMENSION_H +#endif //ORTHTREE_ORTHTREE_TRAITS_BASE_H diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index a0773582dc9b..80fed2fbe95d 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -15,7 +15,7 @@ #include -#include +#include #include #include @@ -38,7 +38,7 @@ to which the minimal extend of a node should be provided. \cgalModels{OrthtreeTraits} */ template -struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< +struct Orthtree_traits_face_graph : public Orthtree_traits_base< typename Kernel_traits::value_type>::type, 3 > { Orthtree_traits_face_graph(const TriangleMesh& pm, VertexPointMap vpm) @@ -47,7 +47,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base_for_dimension< /// \name Types /// @{ - using Base = Orthtree_traits_base_for_dimension< + using Base = Orthtree_traits_base< typename Kernel_traits::value_type>::type, 3 >; using Node_index = typename Base::Node_index; using Self = Orthtree_traits_face_graph; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 4aa733a9660d..0e6c3f98d844 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -19,7 +19,7 @@ #include #include -#include +#include namespace CGAL { @@ -74,7 +74,7 @@ void reassign_points( \cgalModels{OrthtreeTraits} \sa `CGAL::Octree` \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_base_for_dimension` + \sa `CGAL::Orthtree_traits_base` */ template < typename GeomTraits, @@ -85,14 +85,14 @@ template < GeomTraits >::value > -struct Orthtree_traits_point : public Orthtree_traits_base_for_dimension { +struct Orthtree_traits_point : public Orthtree_traits_base { public: /// \name Types /// @{ using Node_data = boost::iterator_range; /// @} - using Base = Orthtree_traits_base_for_dimension; + using Base = Orthtree_traits_base; using Self = Orthtree_traits_point; using Tree = Orthtree; From 019be3fc3b931cf421b5e95319dc7d3f6d2e56de Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 1 Feb 2024 14:28:30 +0100 Subject: [PATCH 374/520] some updates on documentation --- .../Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h | 2 +- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 2 +- Orthtree/doc/Orthtree/Orthtree.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 40628d8b26e4..4fce7b17ce5a 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -5,7 +5,7 @@ Refinement of the OrthtreeTraits concept, adding requirements for the traits class of a `CGAL::Orthtree` in order to supports nearest-neighbor searching. - Nearest neighbor searches expect a tree with nodes which contain list types. + Nearest neighbor searches expect a tree where `Node_data` is a model of `ForwardRange`. The leaf nodes of the tree represent an exclusive partition of the elements contained in the tree. This means that no element should be contained by more than one node. diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index f6791c13c78a..b94a9a9107df 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -85,7 +85,7 @@ class OrthtreeTraits using Distribute_node_contents = unspecified_type; /*! - * \brief Functor with an operator to construct a `Point_d` from an appropriate number of x, y, z etc.\ `FT` arguments. + * \brief Functor with an operator to construct a `Point_d` from an initializer list. * * For trees which use a different kernel for the bounding box type, * the return type of this functor must match the kernel used by the bounding box type and not that of the contents. diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 5f7b1a52c3a4..15c2a3e3505f 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -10,9 +10,9 @@ namespace CGAL { \section Section_Orthtree_Introduction Introduction Quadtrees are tree data structures in which each node encloses a -square section of space, and each internal node has exactly 4 +rectangular section of space, and each internal node has exactly 4 children. Octrees are a similar data structure in 3D in which each -node encloses a cubic section of space, and each internal node has +node encloses a cuboid section of space, and each internal node has exactly 8 children. We call the generalization of such data structure "orthtrees", as From 834b405a0981644e519ab574d6764b4fa2de3d0f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 1 Feb 2024 16:05:08 +0100 Subject: [PATCH 375/520] adding surface mesh to examples --- Orthtree/doc/Orthtree/examples.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Orthtree/doc/Orthtree/examples.txt b/Orthtree/doc/Orthtree/examples.txt index ff327b8272fd..7d6ccf98a35f 100644 --- a/Orthtree/doc/Orthtree/examples.txt +++ b/Orthtree/doc/Orthtree/examples.txt @@ -3,6 +3,7 @@ \example Orthtree/octree_build_from_point_vector.cpp \example Orthtree/octree_build_with_custom_split.cpp \example Orthtree/octree_find_nearest_neighbor.cpp +\example Orthtree/octree_surface_mesh.cpp \example Orthtree/octree_traversal_manual.cpp \example Orthtree/octree_traversal_preorder.cpp \example Orthtree/octree_traversal_custom.cpp From 3c55548967230d28a8e3fc0879f4cab53b1456a3 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 09:32:29 +0100 Subject: [PATCH 376/520] traversals are now templated by OrthtreeTraits --- .../examples/Orthtree/octree_surface_mesh.cpp | 2 +- .../Orthtree/octree_traversal_preorder.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 67 +++++++++---------- Orthtree/include/CGAL/Orthtree/Traversals.h | 40 +++++------ 4 files changed, 52 insertions(+), 59 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index cda6ad2b0240..9843dc6cd08f 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -15,7 +15,7 @@ using Octree = CGAL::Orthtree; void dump_as_polylines(const Octree& ot) { std::ofstream out("octree.polylines.txt"); - for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) + for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) { if (!ot.is_leaf(node)) continue; diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index 5ae7cc0a657e..1c07578d2d15 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -12,7 +12,7 @@ using Point = Kernel::Point_3; using Point_set = CGAL::Point_set_3; using Point_map = Point_set::Point_map; using Octree = CGAL::Octree; -using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; int main(int argc, char **argv) { diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 7dfb39d6ce53..9b37f7007c02 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -333,7 +333,7 @@ class Orthtree { // Collect all the leaf nodes std::queue leaf_nodes; - for (Node_index leaf: traverse(Orthtrees::Leaves_traversal(*this))) { + for (Node_index leaf: traverse(Orthtrees::Leaves_traversal(*this))) { leaf_nodes.push(leaf); } @@ -390,16 +390,11 @@ class Orthtree { /*! * \brief provides direct read-only access to the tree traits. - * - * @return a const reference to the traits instantiation. */ const Traits& traits() const { return m_traits; } /*! - \brief provides access to the root node, and by - extension the rest of the tree. - - \return Node_index of the root node. + \brief provides access to the root node, and by extension the rest of the tree. */ Node_index root() const { return 0; } @@ -448,35 +443,6 @@ class Orthtree { return traverse(Traversal{*this, std::forward(args)...}); } - // TODO shall we document it? - FT - compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const - { - CGAL_assertion(depth <= m_side_per_depth.size()); - // an odd coordinate will be first compute at the current depth, - // while an even coordinate has already been computed at a previous depth. - // So while the coordinate is even, we decrease the depth to end up of the first - // non-even coordinate to compute it (with particular case for bbox limits). - // Note that is depth becomes too large, we might end up with incorrect coordinates - // due to rounding errors. - if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth - if (gc == 0) return (m_bbox.min)()[ci]; - if (gc % 2 !=0) - { - FT size = depth < m_side_per_depth.size() - ? m_side_per_depth[depth][ci] - : m_side_per_depth[depth-1][ci]/FT(2); - return (m_bbox.min)()[ci] + int(gc) * size; - } - std::size_t nd = depth; - do{ - --nd; - gc = gc >> 1; - } - while((gc&1)==0); // while even, shift - return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[nd][ci]; - } - /*! \brief constructs the bounding box of a node. @@ -1143,6 +1109,33 @@ class Orthtree { private: // functions : + FT compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const + { + CGAL_assertion(depth <= m_side_per_depth.size()); + // an odd coordinate will be first compute at the current depth, + // while an even coordinate has already been computed at a previous depth. + // So while the coordinate is even, we decrease the depth to end up of the first + // non-even coordinate to compute it (with particular case for bbox limits). + // Note that is depth becomes too large, we might end up with incorrect coordinates + // due to rounding errors. + if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth + if (gc == 0) return (m_bbox.min)()[ci]; + if (gc % 2 != 0) + { + FT size = depth < m_side_per_depth.size() + ? m_side_per_depth[depth][ci] + : m_side_per_depth[depth - 1][ci] / FT(2); + return (m_bbox.min)()[ci] + int(gc) * size; + } + std::size_t nd = depth; + do { + --nd; + gc = gc >> 1; + } while ((gc & 1) == 0); // while even, shift + return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[nd][ci]; + } + + Node_index recursive_descendant(Node_index node, std::size_t i) { return child(node, i); } template @@ -1241,7 +1234,7 @@ class Orthtree { friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { // Iterate over all nodes - for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { + for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { // Show the depth for (std::size_t i = 0; i < orthtree.depth(n); ++i) os << ". "; diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index acd1db941f61..f0c1abfb358a 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -28,23 +28,23 @@ namespace Orthtrees { \ingroup PkgOrthtreeTraversal \brief A class used for performing a preorder traversal. - \tparam Tree an instance of `Orthtree` + \tparam GeomTraits must be a model of `OrthtreeTraits` A preorder traversal starts from the root towards the leaves. \cgalModels{OrthtreeTraversal} */ -template +template struct Preorder_traversal { private: - const Tree& m_orthtree; + const Orthtree& m_orthtree; public: - using Node_index = typename Tree::Node_index; + using Node_index = typename Orthtree::Node_index; - Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + Preorder_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.root(); @@ -72,23 +72,23 @@ struct Preorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a postorder traversal. - \tparam Tree an instance of `Orthtree` + \tparam GeomTraits must be a model of `OrthtreeTraits` A postorder traversal starts from the leaves towards the root. \cgalModels{OrthtreeTraversal} */ -template +template struct Postorder_traversal { private: - const Tree& m_orthtree; + const Orthtree& m_orthtree; public: - using Node_index = typename Tree::Node_index; + using Node_index = typename Orthtree::Node_index; - Postorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + Postorder_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); @@ -103,23 +103,23 @@ struct Postorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal on leaves only. - \tparam Tree an instance of `Orthtree` + \tparam GeomTraits must be a model of `OrthtreeTraits` All non-leaf nodes are ignored. \cgalModels{OrthtreeTraversal} */ -template +template struct Leaves_traversal { private: - const Tree& m_orthtree; + const Orthtree& m_orthtree; public: - using Node_index = typename Tree::Node_index; + using Node_index = typename Orthtree::Node_index; - Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} + Leaves_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); @@ -141,7 +141,7 @@ struct Leaves_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal of a specific depth level. - \tparam Tree an instance of `Orthtree` + \tparam GeomTraits must be a model of `OrthtreeTraits` All tree nodes at another depth are ignored. If the selected depth is All tree nodes at another depth are ignored. If the selected depth is @@ -149,21 +149,21 @@ struct Leaves_traversal { \cgalModels{OrthtreeTraversal} */ -template +template struct Level_traversal { private: - const Tree& m_orthtree; + const Orthtree& m_orthtree; const std::size_t m_depth; public: - using Node_index = typename Tree::Node_index; + using Node_index = typename Orthtree::Node_index; /*! constructs a `depth`-level traversal. */ - Level_traversal(const Tree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} + Level_traversal(const Orthtree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} Node_index first_index() const { // assumes the tree has at least one child at m_depth From da0441089254b9f218d4abc8470be42e7342e7e4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 09:34:36 +0100 Subject: [PATCH 377/520] shortening doc of simple functions --- Orthtree/include/CGAL/Orthtree.h | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9b37f7007c02..0422b650dfc0 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -651,10 +651,6 @@ class Orthtree { /*! \brief determines whether the node specified by index `n` is a leaf node. - - \param n index of the node to check. - - \return `true` of the node is a leaf, `false` otherwise. */ bool is_leaf(Node_index n) const { return !m_node_children[n].has_value(); @@ -662,10 +658,6 @@ class Orthtree { /*! \brief determines whether the node specified by index `n` is a root node. - - \param n index of the node to check. - - \return `true` if the node is a root, `false` otherwise. */ bool is_root(Node_index n) const { return n == 0; @@ -686,21 +678,13 @@ class Orthtree { /*! \brief retrieves a reference to the Node_data associated with the node specified by `n`. - - \param n index of the node to retrieve the contents of. - - \return a reference to the data associated with the node. */ Node_data& data(Node_index n) { return m_node_contents[n]; } /*! - \brief const version of `data()` - - \param n index of the node to retrieve the contents of. - - \return a const reference to the data associated with the node. + \brief retrieves a const reference to the Node_data associated with the node specified by `n`. */ const Node_data& data(Node_index n) const { return m_node_contents[n]; @@ -708,10 +692,6 @@ class Orthtree { /*! \brief retrieves the global coordinates of the node. - - \param n index of the node to retrieve the coordinates of. - - \return the global coordinates of the node within the tree */ Global_coordinates global_coordinates(Node_index n) const { return m_node_coordinates[n]; @@ -719,10 +699,6 @@ class Orthtree { /*! \brief retrieves the local coordinates of the node. - - \param n index of the node to retrieve the coordinates of. - - \return the local coordinates of the node within the tree */ Local_coordinates local_coordinates(Node_index n) const { Local_coordinates result; From 73bf4edf44a5c283b850ddd6c85e912273284226 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 10:30:51 +0100 Subject: [PATCH 378/520] moving definition of Node_index into traits --- Orthtree/include/CGAL/Orthtree.h | 21 ++++++++----------- Orthtree/include/CGAL/Orthtree/Traversals.h | 8 +++---- .../include/CGAL/Orthtree_traits_face_graph.h | 2 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 3 +-- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 0422b650dfc0..b0df8c8afd11 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -83,6 +83,7 @@ class Orthtree { using Sphere = typename Traits::Sphere_d; ///< Sphere type. using Adjacency = typename Traits::Adjacency; ///< Adjacency type. + using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. using Node_data = typename Traits::Node_data; /// @} @@ -100,11 +101,6 @@ class Orthtree { */ static constexpr int degree = (2 << (dimension - 1)); - /*! - * \brief Index of a given node in the tree; the root always has index 0. - */ - using Node_index = std::size_t; - /*! \brief Set of bits representing this node's relationship to its parent. @@ -155,8 +151,9 @@ class Orthtree { using Cartesian_ranges = Orthtrees::internal::Cartesian_ranges; using Node_property_container = Properties::Experimental::Property_container; - template - using Property_array = Node_property_container::Array; + + template + using Property_array = typename Properties::Experimental::Property_container::template Array; Traits m_traits; /* the tree traits */ @@ -194,11 +191,11 @@ class Orthtree { */ explicit Orthtree(Traits traits) : m_traits(traits), - m_node_contents(m_node_properties.add_property("contents")), - m_node_depths(m_node_properties.add_property("depths", 0)), - m_node_coordinates(m_node_properties.add_property("coordinates")), - m_node_parents(m_node_properties.add_property>("parents")), - m_node_children(m_node_properties.add_property>("children")) { + m_node_contents(m_node_properties.template add_property("contents")), + m_node_depths(m_node_properties.template add_property("depths", 0)), + m_node_coordinates(m_node_properties.template add_property("coordinates")), + m_node_parents(m_node_properties.template add_property>("parents")), + m_node_children(m_node_properties.template add_property>("children")) { m_node_properties.emplace(); diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index f0c1abfb358a..41c0b501043e 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -42,7 +42,7 @@ struct Preorder_traversal { public: - using Node_index = typename Orthtree::Node_index; + using Node_index = typename GeomTraits::Node_index; Preorder_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} @@ -86,7 +86,7 @@ struct Postorder_traversal { public: - using Node_index = typename Orthtree::Node_index; + using Node_index = typename GeomTraits::Node_index; Postorder_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} @@ -117,7 +117,7 @@ struct Leaves_traversal { public: - using Node_index = typename Orthtree::Node_index; + using Node_index = typename GeomTraits::Node_index; Leaves_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} @@ -158,7 +158,7 @@ struct Level_traversal { public: - using Node_index = typename Orthtree::Node_index; + using Node_index = typename GeomTraits::Node_index; /*! constructs a `depth`-level traversal. diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 80fed2fbe95d..302368da55d7 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -49,7 +49,6 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base< using Base = Orthtree_traits_base< typename Kernel_traits::value_type>::type, 3 >; - using Node_index = typename Base::Node_index; using Self = Orthtree_traits_face_graph; using Tree = Orthtree; @@ -58,6 +57,7 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base< using FT = typename Self::FT; using Cartesian_const_iterator_d = typename Self::Cartesian_const_iterator_d; + using Node_index = typename Base::Node_index; using Node_data = std::vector::face_descriptor>; using Geom_traits = typename Kernel_traits::type; diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 0e6c3f98d844..805b46dc9f35 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -96,9 +96,8 @@ struct Orthtree_traits_point : public Orthtree_traits_base; using Tree = Orthtree; - using Node_data_element = typename std::iterator_traits::value_type; using Node_index = typename Base::Node_index; - + using Node_data_element = typename std::iterator_traits::value_type; Orthtree_traits_point( PointRange& points, From 79eaf04bb86c74310bfa0ed1026eba6077925b04 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 10:51:21 +0100 Subject: [PATCH 379/520] doc split predicate with bucket_size needs Node_data with random access --- Orthtree/include/CGAL/Orthtree.h | 2 +- Orthtree/include/CGAL/Orthtree/Split_predicates.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index b0df8c8afd11..bb8ef542a728 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -311,7 +311,7 @@ class Orthtree { than `bucket_size`, it is not split. \warning This convenience method is only appropriate for trees with traits classes where - `Node_data` is a list-like type with a `size()` method. + `Node_data` is a model of `RandomAccessRange`. \param max_depth deepest a tree is allowed to be (nodes at this depth will not be split). \param bucket_size maximum number of items a node is allowed to contain. diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index 934e599cba06..b4997a7aa779 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -29,6 +29,9 @@ namespace Orthtrees { This is a bucket size predicate that considers a node should be split if it contains more than a certain number of items. + + \warning This split predicate is only appropriate for trees with traits classes where + `Node_data` is a model of `RandomAccessRange`. */ class Maximum_number_of_inliers { @@ -92,6 +95,8 @@ class Maximum_depth { at a depth smaller than `max_depth` but already has fewer inliers than `bucket_size`, it is not split. + \warning This split predicate is only appropriate for trees with traits classes where + `Node_data` is a model of `RandomAccessRange`. */ class Maximum_depth_and_maximum_number_of_inliers { From ad5807f5e66eae4bb76f1dcddf25f60ece020427 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 11:12:36 +0100 Subject: [PATCH 380/520] doc locates behaviour --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 3 ++- Orthtree/include/CGAL/Orthtree.h | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index b94a9a9107df..43d54b8f8614 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -71,7 +71,7 @@ class OrthtreeTraits using Construct_root_node_contents = unspecified_type; /*! - * \brief Functor which distributes a node's contents to its children. + * \brief functor which fills the contents of the nodes children. * * Provides the operator: * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` @@ -81,6 +81,7 @@ class OrthtreeTraits * It must distribute the contents of the node to each of its children. * For a tree in which each node contains a span, this may mean rearranging the contents of the original node * and producing spans containing a subset of its contents for each of its children. + * For compatibility with locate, the center of the node is considered to be part of the upper half. */ using Distribute_node_contents = unspecified_type; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index bb8ef542a728..5f39d07b111f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -544,9 +544,10 @@ class Orthtree { /*! \brief finds the leaf node which contains a particular point in space. - Traverses the orthtree and finds the deepest cell that has a + Traverses the orthtree and finds the leaf cell that has a domain enclosing the point passed. The point passed must be within - the region enclosed by the orthtree (bbox of the root node). + the region enclosed by the orthtree (bbox of the root node). The point is contained in the + lower cell of each direction if its coordinate is lower than the center. \param point query point. From bd11275ad19346db0f8125230eb501dff751d52f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 11:56:14 +0100 Subject: [PATCH 381/520] removing Get_geometric_object_for_element and adding Squared_distance_of_element to CollectionPartitioningOrthtreeTraits --- .../Concepts/CollectionPartitioningOrthtreeTraits.h | 11 ++++++----- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 2 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 8 ++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 4fce7b17ce5a..7164951788d0 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -36,11 +36,12 @@ class CollectionPartitioningOrthtreeTraits { using Node_data_element = unspecified_type; /*! - * \brief Functor with an operator to produce a geometric object from a `Node_data_element`. + * \brief Functor with an operator calculate the squared distance of `Node_data_element` from a point. * - * The return type of the functor must be a valid argument to `CGAL::squared_distance()`. + * Provides the operator: + * `FT operator()(const Node_data_element&, const Point_d&)` */ - using Get_geometric_object_for_element = unspecified_type; + using Squared_distance_of_element = unspecified_type; /// @} @@ -48,9 +49,9 @@ class CollectionPartitioningOrthtreeTraits { /// @{ /*! - * constructs an object of type `Get_geometric_object_for_element`. + * constructs an object of type `Squared_distance_of_element`. */ - Get_geometric_object_for_element get_geometric_object_for_element_object() const; + Squared_distance_of_element get_squared_distance_of_element_object() const; /// @} }; diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 63266158f4d6..9766e62fe84d 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -38,7 +38,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Pair that point with its distance from the search point Result current_point_with_distance = - {p, squared_distance(orthtree.traits().get_geometric_object_for_element_object()(p), search_bounds.center())}; + {p, orthtree.traits().get_squared_distance_of_element_object()(p, search_bounds.center())}; // Check if the new point is within the bounds if (current_point_with_distance.distance < search_bounds.squared_radius()) { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 805b46dc9f35..b8f436f11c68 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -149,10 +149,10 @@ struct Orthtree_traits_point : public Orthtree_traits_base typename Self::Point_d { - return get(m_point_map, index); - }; + auto get_squared_distance_of_element_object() const { + return [&](const Node_data_element& index, const Point_d& point) -> typename FT { + return CGAL::squared_distance(get(m_point_map, index), point); + }; } private: From 6e7587a863919dedbff6161b8b4e2df8579b2dde Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 11:56:29 +0100 Subject: [PATCH 382/520] fixing tests --- Orthtree/test/Orthtree/test_octree_custom_properties.cpp | 2 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 2 +- Orthtree/test/Orthtree/test_octree_traverse.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 7553824a8ab2..0b984db1f4fa 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -54,7 +54,7 @@ int main(void) { // Expanding the tree; new nodes should be assigned the default value tree.refine(10, 1); - for (auto n : tree.traverse>()) { + for (auto n : tree.traverse>()) { // Everything but the root will have the default value if (!tree.is_root(n)) assert(node_int_property[n] == 5); } diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 972b4a7168ad..6f21a91a1149 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -15,7 +15,7 @@ using Point = Kernel::Point_3; using FT = Kernel::FT; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; -using Leaves_traversal = CGAL::Orthtrees::Leaves_traversal; +using Leaves_traversal = CGAL::Orthtrees::Leaves_traversal; std::size_t count_jumps(Octree& octree) { diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 13d7527bd993..b9161d66068f 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -12,8 +12,8 @@ using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; -using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; -using Level_traversal = CGAL::Orthtrees::Level_traversal; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; +using Level_traversal = CGAL::Orthtrees::Level_traversal; bool test_preorder_1_node() { From 3f361a4eda213ee26bdb6bacc23c4307a5335c24 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 12:35:31 +0100 Subject: [PATCH 383/520] fixes for ci --- Orthtree/include/CGAL/Orthtree.h | 28 +++++++++---------- Orthtree/include/CGAL/Orthtree/Traversals.h | 16 +++++------ Orthtree/include/CGAL/Orthtree_traits_point.h | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 5f39d07b111f..60fc8e308520 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -220,22 +220,22 @@ class Orthtree { Orthtree(const Orthtree& other) : m_traits(other.m_traits), m_node_properties(other.m_node_properties), - m_node_contents(m_node_properties.get_property("contents")), - m_node_depths(m_node_properties.get_property("depths")), - m_node_coordinates(m_node_properties.get_property("coordinates")), - m_node_parents(m_node_properties.get_property>("parents")), - m_node_children(m_node_properties.get_property>("children")), + m_node_contents(m_node_properties.template get_property("contents")), + m_node_depths(m_node_properties.template get_property("depths")), + m_node_coordinates(m_node_properties.template get_property("coordinates")), + m_node_parents(m_node_properties.template get_property>("parents")), + m_node_children(m_node_properties.template get_property>("children")), m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) {} // move constructor Orthtree(Orthtree&& other) : m_traits(other.m_traits), m_node_properties(std::move(other.m_node_properties)), - m_node_contents(m_node_properties.get_property("contents")), - m_node_depths(m_node_properties.get_property("depths")), - m_node_coordinates(m_node_properties.get_property("coordinates")), - m_node_parents(m_node_properties.get_property>("parents")), - m_node_children(m_node_properties.get_property>("children")), + m_node_contents(m_node_properties.template get_property("contents")), + m_node_depths(m_node_properties.template get_property("depths")), + m_node_coordinates(m_node_properties.template get_property("coordinates")), + m_node_parents(m_node_properties.template get_property>("parents")), + m_node_children(m_node_properties.template get_property>("children")), m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) { // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. @@ -482,7 +482,7 @@ class Orthtree { template std::pair, bool> get_or_add_node_property(const std::string& name, const T default_value = T()) { - auto p = m_node_properties.get_or_add_property(name, default_value); + auto p = m_node_properties.template get_or_add_property(name, default_value); return std::pair, bool>(Property_map(p.first), p.second); } @@ -498,7 +498,7 @@ class Orthtree { */ template Property_map add_node_property(const std::string& name, const T default_value = T()) { - return m_node_properties.add_property(name, default_value); + return m_node_properties.template add_property(name, default_value); } /*! @@ -514,7 +514,7 @@ class Orthtree { */ template Property_map get_node_property(const std::string& name) { - return m_node_properties.get_property(name); + return m_node_properties.template get_property(name); } /*! @@ -529,7 +529,7 @@ class Orthtree { template std::optional> get_node_property_if_exists(const std::string& name) { - auto p = m_node_properties.get_property_if_exists(name); + auto p = m_node_properties.template get_property_if_exists(name); if (p) return std::optional >(Property_map(*p)); else diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index 41c0b501043e..f2a8ad6af37d 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -38,13 +38,13 @@ template struct Preorder_traversal { private: - const Orthtree& m_orthtree; + const CGAL::Orthtree& m_orthtree; public: using Node_index = typename GeomTraits::Node_index; - Preorder_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} + Preorder_traversal(const CGAL::Orthtree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.root(); @@ -82,13 +82,13 @@ template struct Postorder_traversal { private: - const Orthtree& m_orthtree; + const CGAL::Orthtree& m_orthtree; public: using Node_index = typename GeomTraits::Node_index; - Postorder_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} + Postorder_traversal(const CGAL::Orthtree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); @@ -113,13 +113,13 @@ template struct Leaves_traversal { private: - const Orthtree& m_orthtree; + const CGAL::Orthtree& m_orthtree; public: using Node_index = typename GeomTraits::Node_index; - Leaves_traversal(const Orthtree& orthtree) : m_orthtree(orthtree) {} + Leaves_traversal(const CGAL::Orthtree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); @@ -153,7 +153,7 @@ template struct Level_traversal { private: - const Orthtree& m_orthtree; + const CGAL::Orthtree& m_orthtree; const std::size_t m_depth; public: @@ -163,7 +163,7 @@ struct Level_traversal { /*! constructs a `depth`-level traversal. */ - Level_traversal(const Orthtree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} + Level_traversal(const CGAL::Orthtree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} Node_index first_index() const { // assumes the tree has at least one child at m_depth diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index b8f436f11c68..299a9c9edb3f 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -150,7 +150,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base typename FT { + return [&](const Node_data_element& index, const typename Self::Point_d& point) -> typename FT { return CGAL::squared_distance(get(m_point_map, index), point); }; } From f78101222eaf550ccd3491e10fb0d4b0ef51b9a9 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 13:03:16 +0100 Subject: [PATCH 384/520] fix for ci doc for locate/split predicate --- Orthtree/include/CGAL/Orthtree/Split_predicates.h | 4 ++-- Orthtree/include/CGAL/Orthtree_traits_point.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index b4997a7aa779..bc04c4b815e9 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -31,7 +31,7 @@ namespace Orthtrees { split if it contains more than a certain number of items. \warning This split predicate is only appropriate for trees with traits classes where - `Node_data` is a model of `RandomAccessRange`. + `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ class Maximum_number_of_inliers { @@ -96,7 +96,7 @@ class Maximum_depth { than `bucket_size`, it is not split. \warning This split predicate is only appropriate for trees with traits classes where - `Node_data` is a model of `RandomAccessRange`. + `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ class Maximum_depth_and_maximum_number_of_inliers { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 299a9c9edb3f..5d1fd93bfded 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -150,7 +150,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base typename FT { + return [&](const Node_data_element& index, const typename Self::Point_d& point) -> typename Self::FT { return CGAL::squared_distance(get(m_point_map, index), point); }; } From d9756dd97173ac93347bc9d90a1e86a4dc532f86 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 13:55:37 +0100 Subject: [PATCH 385/520] added missing include adding image to doc --- Orthtree/doc/Orthtree/Orthtree.txt | 8 ++++++-- Orthtree/include/CGAL/Orthtree.h | 2 +- Orthtree/include/CGAL/Orthtree/Traversals.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 15c2a3e3505f..f18a92ccf433 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -26,10 +26,9 @@ with custom contents and split predicates, and iterated on with various traversal methods. Orthants can be orthotopes and not only hypercubes. \cgalFigureBegin{Orthtree_fig, orthtree.png} -Building an %orthtree in 3D (%octree) from a point cloud. +Building an %orthtree in 3D (%octree) from a point cloud (top) and a mesh (bottom). \cgalFigureEnd - \section Section_Orthtree_Building Building A common purpose for an orthtree is to subdivide a collection of points, @@ -119,6 +118,11 @@ set as the orthtree's map type, so a map does not need to be provided. \cgalExample{Orthtree/orthtree_build.cpp} +\section Section_Orthtree_Properties Properties +The Orthtree uses a mechanism to attach properties to nodes at run-time which follows \ref sectionSurfaceMesh_properties "Surface mesh properties". Each property is identified by a string and its key type and stored in a consecutive block of memory. Contrary to surface mesh the removal of properties is not supported. + +Several properties are provided by default and used to maintain the data structure. The property \c "contents" keeps the data for each node. "parents" and "children" maintain the relation between nodes. "coordinates" and "depth" contain absolute positions of nodes. + \section Section_Orthtree_Traversal Traversal \note For simplicity, the rest of the user manual will only use diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 60fc8e308520..9e4f62fc9d44 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -311,7 +311,7 @@ class Orthtree { than `bucket_size`, it is not split. \warning This convenience method is only appropriate for trees with traits classes where - `Node_data` is a model of `RandomAccessRange`. + `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. \param max_depth deepest a tree is allowed to be (nodes at this depth will not be split). \param bucket_size maximum number of items a node is allowed to contain. diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index f2a8ad6af37d..e9876c6448ff 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -14,6 +14,7 @@ #include +#include #include #include #include From 47bbc08d8e7665bccb9b9059465c7a72a5a7dd60 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 15:08:32 +0100 Subject: [PATCH 386/520] Revert "traversals are now templated by OrthtreeTraits" This reverts commit 3c55548967230d28a8e3fc0879f4cab53b1456a3. --- .../examples/Orthtree/octree_surface_mesh.cpp | 2 +- .../Orthtree/octree_traversal_preorder.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 63 ++++++++++--------- Orthtree/include/CGAL/Orthtree/Traversals.h | 41 ++++++------ 4 files changed, 55 insertions(+), 53 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index 9843dc6cd08f..cda6ad2b0240 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -15,7 +15,7 @@ using Octree = CGAL::Orthtree; void dump_as_polylines(const Octree& ot) { std::ofstream out("octree.polylines.txt"); - for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) + for (Octree::Node_index node : ot.traverse(CGAL::Orthtrees::Leaves_traversal(ot))) { if (!ot.is_leaf(node)) continue; diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index 1c07578d2d15..5ae7cc0a657e 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -12,7 +12,7 @@ using Point = Kernel::Point_3; using Point_set = CGAL::Point_set_3; using Point_map = Point_set::Point_map; using Octree = CGAL::Octree; -using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; int main(int argc, char **argv) { diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9e4f62fc9d44..830f3b90fc64 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -330,7 +330,7 @@ class Orthtree { // Collect all the leaf nodes std::queue leaf_nodes; - for (Node_index leaf: traverse(Orthtrees::Leaves_traversal(*this))) { + for (Node_index leaf: traverse(Orthtrees::Leaves_traversal(*this))) { leaf_nodes.push(leaf); } @@ -391,7 +391,8 @@ class Orthtree { const Traits& traits() const { return m_traits; } /*! - \brief provides access to the root node, and by extension the rest of the tree. + \brief provides access to the root node, and by + extension the rest of the tree. */ Node_index root() const { return 0; } @@ -440,6 +441,35 @@ class Orthtree { return traverse(Traversal{*this, std::forward(args)...}); } + // TODO shall we document it? + FT + compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const + { + CGAL_assertion(depth <= m_side_per_depth.size()); + // an odd coordinate will be first compute at the current depth, + // while an even coordinate has already been computed at a previous depth. + // So while the coordinate is even, we decrease the depth to end up of the first + // non-even coordinate to compute it (with particular case for bbox limits). + // Note that is depth becomes too large, we might end up with incorrect coordinates + // due to rounding errors. + if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth + if (gc == 0) return (m_bbox.min)()[ci]; + if (gc % 2 !=0) + { + FT size = depth < m_side_per_depth.size() + ? m_side_per_depth[depth][ci] + : m_side_per_depth[depth-1][ci]/FT(2); + return (m_bbox.min)()[ci] + int(gc) * size; + } + std::size_t nd = depth; + do{ + --nd; + gc = gc >> 1; + } + while((gc&1)==0); // while even, shift + return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[nd][ci]; + } + /*! \brief constructs the bounding box of a node. @@ -1083,33 +1113,6 @@ class Orthtree { private: // functions : - FT compute_cartesian_coordinate(std::uint32_t gc, std::size_t depth, int ci) const - { - CGAL_assertion(depth <= m_side_per_depth.size()); - // an odd coordinate will be first compute at the current depth, - // while an even coordinate has already been computed at a previous depth. - // So while the coordinate is even, we decrease the depth to end up of the first - // non-even coordinate to compute it (with particular case for bbox limits). - // Note that is depth becomes too large, we might end up with incorrect coordinates - // due to rounding errors. - if (gc == (1u << depth)) return (m_bbox.max)()[ci]; // gc == 2^node_depth - if (gc == 0) return (m_bbox.min)()[ci]; - if (gc % 2 != 0) - { - FT size = depth < m_side_per_depth.size() - ? m_side_per_depth[depth][ci] - : m_side_per_depth[depth - 1][ci] / FT(2); - return (m_bbox.min)()[ci] + int(gc) * size; - } - std::size_t nd = depth; - do { - --nd; - gc = gc >> 1; - } while ((gc & 1) == 0); // while even, shift - return (m_bbox.min)()[ci] + int(gc) * m_side_per_depth[nd][ci]; - } - - Node_index recursive_descendant(Node_index node, std::size_t i) { return child(node, i); } template @@ -1208,7 +1211,7 @@ class Orthtree { friend std::ostream& operator<<(std::ostream& os, const Self& orthtree) { // Iterate over all nodes - for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { + for (auto n: orthtree.traverse(Orthtrees::Preorder_traversal(orthtree))) { // Show the depth for (std::size_t i = 0; i < orthtree.depth(n); ++i) os << ". "; diff --git a/Orthtree/include/CGAL/Orthtree/Traversals.h b/Orthtree/include/CGAL/Orthtree/Traversals.h index e9876c6448ff..acd1db941f61 100644 --- a/Orthtree/include/CGAL/Orthtree/Traversals.h +++ b/Orthtree/include/CGAL/Orthtree/Traversals.h @@ -14,7 +14,6 @@ #include -#include #include #include #include @@ -29,23 +28,23 @@ namespace Orthtrees { \ingroup PkgOrthtreeTraversal \brief A class used for performing a preorder traversal. - \tparam GeomTraits must be a model of `OrthtreeTraits` + \tparam Tree an instance of `Orthtree` A preorder traversal starts from the root towards the leaves. \cgalModels{OrthtreeTraversal} */ -template +template struct Preorder_traversal { private: - const CGAL::Orthtree& m_orthtree; + const Tree& m_orthtree; public: - using Node_index = typename GeomTraits::Node_index; + using Node_index = typename Tree::Node_index; - Preorder_traversal(const CGAL::Orthtree& orthtree) : m_orthtree(orthtree) {} + Preorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.root(); @@ -73,23 +72,23 @@ struct Preorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a postorder traversal. - \tparam GeomTraits must be a model of `OrthtreeTraits` + \tparam Tree an instance of `Orthtree` A postorder traversal starts from the leaves towards the root. \cgalModels{OrthtreeTraversal} */ -template +template struct Postorder_traversal { private: - const CGAL::Orthtree& m_orthtree; + const Tree& m_orthtree; public: - using Node_index = typename GeomTraits::Node_index; + using Node_index = typename Tree::Node_index; - Postorder_traversal(const CGAL::Orthtree& orthtree) : m_orthtree(orthtree) {} + Postorder_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); @@ -104,23 +103,23 @@ struct Postorder_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal on leaves only. - \tparam GeomTraits must be a model of `OrthtreeTraits` + \tparam Tree an instance of `Orthtree` All non-leaf nodes are ignored. \cgalModels{OrthtreeTraversal} */ -template +template struct Leaves_traversal { private: - const CGAL::Orthtree& m_orthtree; + const Tree& m_orthtree; public: - using Node_index = typename GeomTraits::Node_index; + using Node_index = typename Tree::Node_index; - Leaves_traversal(const CGAL::Orthtree& orthtree) : m_orthtree(orthtree) {} + Leaves_traversal(const Tree& orthtree) : m_orthtree(orthtree) {} Node_index first_index() const { return m_orthtree.deepest_first_child(m_orthtree.root()); @@ -142,7 +141,7 @@ struct Leaves_traversal { \ingroup PkgOrthtreeTraversal \brief A class used for performing a traversal of a specific depth level. - \tparam GeomTraits must be a model of `OrthtreeTraits` + \tparam Tree an instance of `Orthtree` All tree nodes at another depth are ignored. If the selected depth is All tree nodes at another depth are ignored. If the selected depth is @@ -150,21 +149,21 @@ struct Leaves_traversal { \cgalModels{OrthtreeTraversal} */ -template +template struct Level_traversal { private: - const CGAL::Orthtree& m_orthtree; + const Tree& m_orthtree; const std::size_t m_depth; public: - using Node_index = typename GeomTraits::Node_index; + using Node_index = typename Tree::Node_index; /*! constructs a `depth`-level traversal. */ - Level_traversal(const CGAL::Orthtree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} + Level_traversal(const Tree& orthtree, std::size_t depth) : m_orthtree(orthtree), m_depth(depth) {} Node_index first_index() const { // assumes the tree has at least one child at m_depth From 6f8b3ef55c0fb52c7aa1c564ab0e50a67cfb9732 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 15:08:47 +0100 Subject: [PATCH 387/520] add surface mesh to doc dependencies --- Orthtree/doc/Orthtree/dependencies | 1 + 1 file changed, 1 insertion(+) diff --git a/Orthtree/doc/Orthtree/dependencies b/Orthtree/doc/Orthtree/dependencies index 1e72914f7008..703f46d89264 100644 --- a/Orthtree/doc/Orthtree/dependencies +++ b/Orthtree/doc/Orthtree/dependencies @@ -8,3 +8,4 @@ Property_map STL_Extension Spatial_searching Stream_support +Surface_mesh From b265ee90a8e9da34ff778666322fc48d9a255be8 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 15:45:17 +0100 Subject: [PATCH 388/520] fixing tests --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- Orthtree/include/CGAL/Orthtree.h | 4 ++-- Orthtree/test/Orthtree/test_octree_custom_properties.cpp | 2 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 2 +- Orthtree/test/Orthtree/test_octree_traverse.cpp | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index f18a92ccf433..615042dd51d8 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -121,7 +121,7 @@ set as the orthtree's map type, so a map does not need to be provided. \section Section_Orthtree_Properties Properties The Orthtree uses a mechanism to attach properties to nodes at run-time which follows \ref sectionSurfaceMesh_properties "Surface mesh properties". Each property is identified by a string and its key type and stored in a consecutive block of memory. Contrary to surface mesh the removal of properties is not supported. -Several properties are provided by default and used to maintain the data structure. The property \c "contents" keeps the data for each node. "parents" and "children" maintain the relation between nodes. "coordinates" and "depth" contain absolute positions of nodes. +Several properties are provided by default and used to maintain the data structure. The property \c "contents" keeps the data for each node. \c "parents" and \c "children" maintain the relation between nodes. \c "coordinates" and \c "depth" contain absolute positions of nodes. \section Section_Orthtree_Traversal Traversal diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 830f3b90fc64..c4ab90a4539a 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -512,7 +512,7 @@ class Orthtree { template std::pair, bool> get_or_add_node_property(const std::string& name, const T default_value = T()) { - auto p = m_node_properties.template get_or_add_property(name, default_value); + auto p = m_node_properties.get_or_add_property(name, default_value); return std::pair, bool>(Property_map(p.first), p.second); } @@ -528,7 +528,7 @@ class Orthtree { */ template Property_map add_node_property(const std::string& name, const T default_value = T()) { - return m_node_properties.template add_property(name, default_value); + return m_node_properties.add_property(name, default_value); } /*! diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 0b984db1f4fa..7553824a8ab2 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -54,7 +54,7 @@ int main(void) { // Expanding the tree; new nodes should be assigned the default value tree.refine(10, 1); - for (auto n : tree.traverse>()) { + for (auto n : tree.traverse>()) { // Everything but the root will have the default value if (!tree.is_root(n)) assert(node_int_property[n] == 5); } diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 6f21a91a1149..972b4a7168ad 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -15,7 +15,7 @@ using Point = Kernel::Point_3; using FT = Kernel::FT; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; -using Leaves_traversal = CGAL::Orthtrees::Leaves_traversal; +using Leaves_traversal = CGAL::Orthtrees::Leaves_traversal; std::size_t count_jumps(Octree& octree) { diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index b9161d66068f..13d7527bd993 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -12,8 +12,8 @@ using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; -using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; -using Level_traversal = CGAL::Orthtrees::Level_traversal; +using Preorder_traversal = CGAL::Orthtrees::Preorder_traversal; +using Level_traversal = CGAL::Orthtrees::Level_traversal; bool test_preorder_1_node() { From eb24ac1c0309b46beb2336c6c49a1430c4cab42b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 2 Feb 2024 18:14:33 +0100 Subject: [PATCH 389/520] Update Orthtree/doc/Orthtree/PackageDescription.txt Co-authored-by: Andreas Fabri --- Orthtree/doc/Orthtree/PackageDescription.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index a178656aedaf..a451ff36b526 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -51,7 +51,7 @@ Quadtree, Octree and Orthtree Reference \cgalCRPSection{Traits} - `CGAL::Orthtree_traits_point` -- `CGAL::Orthtree_traits_base_for_dimension` +- `CGAL::Orthtree_traits_base` - `CGAL::Orthtree_traits_face_graph` \cgalCRPSection{Split Predicates} From cde61a5c49da93e4fc78b56d61a8291fcc5bf418 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 5 Feb 2024 11:43:35 +0100 Subject: [PATCH 390/520] changed property interface of Orthtree to be closer to Surface mesh added listing of existing properties added removal of properties --- .../octree_build_with_custom_split.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 54 ++++++++----------- .../test_octree_custom_properties.cpp | 26 +++++---- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp index 0d25907cbeeb..b3cbac974f54 100644 --- a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp +++ b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp @@ -15,7 +15,7 @@ using Point_map = Point_set::Point_map; using Octree = CGAL::Octree; // Split Predicate -// The predicate is a functor which returns a boolean value, whether a node needs to be split or not +// The predicate is a functor which returns a Boolean value, whether a node needs to be split or not struct Split_by_ratio { std::size_t ratio; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index c4ab90a4539a..dc322ee80642 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -507,63 +507,53 @@ class Orthtree { \param name the name of the new property \param default_value the default value assigned to nodes for this property - \return pair of the property map and a boolean which is `true` if the property needed to be created + \return pair of the property map and a Boolean which is `true` if the property needed to be created */ template std::pair, bool> - get_or_add_node_property(const std::string& name, const T default_value = T()) { + add_node_property(const std::string& name, const T default_value = T()) { auto p = m_node_properties.get_or_add_property(name, default_value); return std::pair, bool>(Property_map(p.first), p.second); } /*! - \brief adds a property for nodes. + \brief gets a property of the nodes if it exists. - \tparam T the type of the property to add + \tparam T the type of the property to retrieve - \param name the name of the new property - \param default_value the default value assigned to nodes for this property + \param name the name of the property - \return the created property map + \return an optional containing the property map if it exists */ template - Property_map add_node_property(const std::string& name, const T default_value = T()) { - return m_node_properties.add_property(name, default_value); + std::optional> + node_property(const std::string& name) { + auto p = m_node_properties.template get_property_if_exists(name); + if (p) + return std::optional >(Property_map(*p)); + else + return std::nullopt; } /*! - \brief gets a property of the tree nodes. - - The property to be retrieved must exist in the tree. - - \tparam T the type of the property to retrieve - - \param name the name of the property - - \return the property map + \brief returns a vector of all property names. */ - template - Property_map get_node_property(const std::string& name) { - return m_node_properties.template get_property(name); + std::vector properties() const { + return m_node_properties.properties(); } /*! - \brief gets a property of the tree nodes if it exists. + \brief removes the node property from the tree. - \tparam T the type of the property to retrieve + \tparam T the type of the property to remove - \param name the name of the property + \param property the property to be removed from the tree. - \return an optional containing the property map if it exists + \return true if property was a valid property of the tree. */ template - std::optional> - get_node_property_if_exists(const std::string& name) { - auto p = m_node_properties.template get_property_if_exists(name); - if (p) - return std::optional >(Property_map(*p)); - else - return std::nullopt; + bool remove_node_property(Property_map property) { + return m_node_properties.remove_property(property.array()); } /// @} diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 7553824a8ab2..5a8ccb3329a2 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -26,26 +26,32 @@ int main(void) { Octree tree({points, points.point_map()}); // Testing built in node properties - typename Octree::Property_map data_prop = tree.get_node_property("contents"); + typename Octree::Property_map data_prop = *tree.node_property("contents"); CGAL_USE(data_prop); - auto prop2 = tree.get_or_add_node_property("test", int(0)); + // list of properties + std::size_t num = tree.properties().size(); + assert(num == 5); + auto prop2 = tree.add_node_property("test", int(0)); assert(prop2.second); + assert(tree.properties().size() == 6); - auto prop3 = tree.get_node_property_if_exists("test"); + auto prop5 = tree.add_node_property("test", int(0)); + assert(!prop5.second); + + auto prop3 = tree.node_property("test"); assert(prop3.has_value()); - auto prop4 = tree.get_node_property_if_exists("test"); + auto prop4 = tree.node_property("test"); assert(!prop4.has_value()); - auto prop5 = tree.get_or_add_node_property("test", int(0)); - assert(!prop5.second); - - auto prop6 = tree.add_node_property("test2", std::string()); - CGAL_USE(prop6); + // removal of properties + num = tree.properties().size(); + tree.remove_node_property(*prop3); + assert(tree.properties().size() == (num - 1)); // Default value should be respected - auto node_int_property = tree.add_node_property("int", 5); + auto node_int_property = tree.add_node_property("int", 5).first; assert(node_int_property[tree.root()] == 5); // Changes to individual nodes should be respected From 33105852285076278dd2143de077a7ec35c44564 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 5 Feb 2024 18:06:39 +0100 Subject: [PATCH 391/520] added Pair_optional_adaptor (WIP) --- Orthtree/include/CGAL/Orthtree.h | 3 +- .../test_octree_custom_properties.cpp | 18 +++++- .../include/CGAL/Pair_optional_adaptor.h | 60 +++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 STL_Extension/include/CGAL/Pair_optional_adaptor.h diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index dc322ee80642..923885fe2762 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -526,7 +527,7 @@ class Orthtree { \return an optional containing the property map if it exists */ template - std::optional> + Pair_optional_adaptor> node_property(const std::string& name) { auto p = m_node_properties.template get_property_if_exists(name); if (p) diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 5a8ccb3329a2..8b9a79275132 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -39,15 +39,27 @@ int main(void) { auto prop5 = tree.add_node_property("test", int(0)); assert(!prop5.second); - auto prop3 = tree.node_property("test"); - assert(prop3.has_value()); + auto a1 = tree.node_property("test"); + std::pair, bool> p1 = tree.node_property("test"); + std::optional> o1 = tree.node_property("test"); + auto f = a1.first; + auto pf1 = p1.first; + auto of1 = o1.value(); + auto fo1 = a1.value(); + std::cout << f.size() << std::endl; + + auto a2 = tree.node_property("test"); + std::pair, bool> p2 = tree.node_property("test"); + std::optional> o2 = tree.node_property("test"); + + //assert(prop3.has_value()); auto prop4 = tree.node_property("test"); assert(!prop4.has_value()); // removal of properties num = tree.properties().size(); - tree.remove_node_property(*prop3); + //tree.remove_node_property(*prop3); assert(tree.properties().size() == (num - 1)); // Default value should be respected diff --git a/STL_Extension/include/CGAL/Pair_optional_adaptor.h b/STL_Extension/include/CGAL/Pair_optional_adaptor.h new file mode 100644 index 000000000000..e7b35ce2da0b --- /dev/null +++ b/STL_Extension/include/CGAL/Pair_optional_adaptor.h @@ -0,0 +1,60 @@ +// Copyright (c) 2024 GeometryFactory Sarl (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) : Sven Oesau + +#ifndef CGAL_PAIR_OPTIONAL_ADAPTOR +#define CGAL_PAIR_OPTIONAL_ADAPTOR + +namespace CGAL { + +// T is supposed to be a handle +template +class Pair_optional_adaptor : public std::optional { +public: + Pair_optional_adaptor(std::optional& obj) : std::optional(obj), second(obj.has_value()), first(u.t) { + std::cout << "optional constructor" << std::endl; + if (obj.has_value()) + u.t = *obj; + } + + Pair_optional_adaptor(const std::nullopt_t& obj) : std::optional(std::nullopt), second(false), first(u.t) { + std::cout << "nullopt constructor" << std::endl; + } + + Pair_optional_adaptor(std::pair& p) : std::optional(b ? p.first : std::nullopt), first(p.first), second(b) { + std::cout << "pair constructor" << std::endl; + } + + operator std::pair() { + return std::pair(first, second); + } + + operator std::optional() { + if (second) + return std::optional(first); + else return std::nullopt; + } + + T &first; + bool second; + +private: + union U { + T t; + int i; + U() : i(0) {} + U(T t) : t(t) {} + } u; +}; + +} // CGAL + +#endif \ No newline at end of file From e4686a21a921e6ae222a6e19fa8d4405376bcee2 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 5 Feb 2024 18:11:01 +0100 Subject: [PATCH 392/520] removed _node from Orthtree property API --- Orthtree/include/CGAL/Orthtree.h | 6 ++--- .../test_octree_custom_properties.cpp | 22 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 923885fe2762..ca7bec5de3db 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -512,7 +512,7 @@ class Orthtree { */ template std::pair, bool> - add_node_property(const std::string& name, const T default_value = T()) { + add_property(const std::string& name, const T default_value = T()) { auto p = m_node_properties.get_or_add_property(name, default_value); return std::pair, bool>(Property_map(p.first), p.second); } @@ -528,7 +528,7 @@ class Orthtree { */ template Pair_optional_adaptor> - node_property(const std::string& name) { + property(const std::string& name) { auto p = m_node_properties.template get_property_if_exists(name); if (p) return std::optional >(Property_map(*p)); @@ -553,7 +553,7 @@ class Orthtree { \return true if property was a valid property of the tree. */ template - bool remove_node_property(Property_map property) { + bool remove_property(Property_map property) { return m_node_properties.remove_property(property.array()); } diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 8b9a79275132..e75837a03127 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -26,35 +26,35 @@ int main(void) { Octree tree({points, points.point_map()}); // Testing built in node properties - typename Octree::Property_map data_prop = *tree.node_property("contents"); + typename Octree::Property_map data_prop = *tree.property("contents"); CGAL_USE(data_prop); // list of properties std::size_t num = tree.properties().size(); assert(num == 5); - auto prop2 = tree.add_node_property("test", int(0)); + auto prop2 = tree.add_property("test", int(0)); assert(prop2.second); assert(tree.properties().size() == 6); - auto prop5 = tree.add_node_property("test", int(0)); + auto prop5 = tree.add_property("test", int(0)); assert(!prop5.second); - auto a1 = tree.node_property("test"); - std::pair, bool> p1 = tree.node_property("test"); - std::optional> o1 = tree.node_property("test"); + auto a1 = tree.property("test"); + std::pair, bool> p1 = tree.property("test"); + std::optional> o1 = tree.property("test"); auto f = a1.first; auto pf1 = p1.first; auto of1 = o1.value(); auto fo1 = a1.value(); std::cout << f.size() << std::endl; - auto a2 = tree.node_property("test"); - std::pair, bool> p2 = tree.node_property("test"); - std::optional> o2 = tree.node_property("test"); + auto a2 = tree.property("test"); + std::pair, bool> p2 = tree.property("test"); + std::optional> o2 = tree.property("test"); //assert(prop3.has_value()); - auto prop4 = tree.node_property("test"); + auto prop4 = tree.property("test"); assert(!prop4.has_value()); // removal of properties @@ -63,7 +63,7 @@ int main(void) { assert(tree.properties().size() == (num - 1)); // Default value should be respected - auto node_int_property = tree.add_node_property("int", 5).first; + auto node_int_property = tree.add_property("int", 5).first; assert(node_int_property[tree.root()] == 5); // Changes to individual nodes should be respected From 59ab39997ef3c8dbca542f1256f7424f0f7db712 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 6 Feb 2024 08:35:09 +0100 Subject: [PATCH 393/520] license fix (GPL -> LGPL) --- STL_Extension/include/CGAL/Pair_optional_adaptor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/STL_Extension/include/CGAL/Pair_optional_adaptor.h b/STL_Extension/include/CGAL/Pair_optional_adaptor.h index e7b35ce2da0b..1620edbf3b28 100644 --- a/STL_Extension/include/CGAL/Pair_optional_adaptor.h +++ b/STL_Extension/include/CGAL/Pair_optional_adaptor.h @@ -5,7 +5,7 @@ // // $URL$ // $Id$ -// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Sven Oesau From 4c1cf8d9c4269346f2b7b0a67d9ac4ed3c084eda Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 6 Feb 2024 10:18:35 +0100 Subject: [PATCH 394/520] working version of Pair_optional_adaptor --- Orthtree/include/CGAL/Orthtree.h | 2 +- .../test_octree_custom_properties.cpp | 8 ++++++ .../include/CGAL/Pair_optional_adaptor.h | 27 ++++++------------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ca7bec5de3db..dc2e41555d1b 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -511,7 +511,7 @@ class Orthtree { \return pair of the property map and a Boolean which is `true` if the property needed to be created */ template - std::pair, bool> + Pair_optional_adaptor> add_property(const std::string& name, const T default_value = T()) { auto p = m_node_properties.get_or_add_property(name, default_value); return std::pair, bool>(Property_map(p.first), p.second); diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index e75837a03127..0e14a51fbbcc 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -39,6 +39,14 @@ int main(void) { auto prop5 = tree.add_property("test", int(0)); assert(!prop5.second); + auto a3 = tree.add_property("test1", int(0)); + std::pair, bool> p3 = tree.add_property("test2", int(0)); + std::optional> o3 = tree.add_property("test3", int(0)); + + auto a4 = tree.add_property("test", int(0)); + std::pair, bool> p4 = tree.add_property("test", int(0)); + std::optional> o4 = tree.add_property("test", int(0)); + auto a1 = tree.property("test"); std::pair, bool> p1 = tree.property("test"); std::optional> o1 = tree.property("test"); diff --git a/STL_Extension/include/CGAL/Pair_optional_adaptor.h b/STL_Extension/include/CGAL/Pair_optional_adaptor.h index 1620edbf3b28..49c5c076dfda 100644 --- a/STL_Extension/include/CGAL/Pair_optional_adaptor.h +++ b/STL_Extension/include/CGAL/Pair_optional_adaptor.h @@ -19,40 +19,29 @@ namespace CGAL { template class Pair_optional_adaptor : public std::optional { public: - Pair_optional_adaptor(std::optional& obj) : std::optional(obj), second(obj.has_value()), first(u.t) { - std::cout << "optional constructor" << std::endl; + Pair_optional_adaptor(std::optional& obj) : std::optional(obj), second(obj.has_value()), first(t_storage.t) { if (obj.has_value()) - u.t = *obj; + t_storage.t = *obj; } - Pair_optional_adaptor(const std::nullopt_t& obj) : std::optional(std::nullopt), second(false), first(u.t) { - std::cout << "nullopt constructor" << std::endl; - } + Pair_optional_adaptor(const std::nullopt_t& obj) : std::optional(std::nullopt), second(false), first(t_storage.t) {} - Pair_optional_adaptor(std::pair& p) : std::optional(b ? p.first : std::nullopt), first(p.first), second(b) { - std::cout << "pair constructor" << std::endl; - } + Pair_optional_adaptor(std::pair& p) : std::optional(p.second ? p.first : std::optional()), first(t_storage.t), second(p.second), t_storage(p.first) {} operator std::pair() { return std::pair(first, second); } - operator std::optional() { - if (second) - return std::optional(first); - else return std::nullopt; - } - T &first; bool second; private: - union U { + union T_value { T t; int i; - U() : i(0) {} - U(T t) : t(t) {} - } u; + T_value() : i(0) {} + T_value(T t) : t(t) {} + } t_storage; }; } // CGAL From a0ad1829b83a9d3e86f793c0802d5313cd55d583 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 14:39:24 +0000 Subject: [PATCH 395/520] Fixes after Guillaume's review --- .../doc/Polygon_repair/Polygon_repair.txt | 17 +++++++++-------- .../include/CGAL/Polygon_repair/Even_odd_rule.h | 1 + .../CGAL/Polygon_repair/Polygon_repair.h | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 54edf8f9898b..746f5874c75e 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -17,11 +17,12 @@ or hole), and reconstructs the polygon(s) represented by the arrangement. The method returns valid output stored in a multipolygon with holes. Different arrangement and labelling heuristics are possible, but -currently only the even-odd heuristic is implemented in the package. -This heuristic results in areas that are alternately assigned as polygon +currently only the even-odd rule is implemented in this package. +This rule results in areas that are alternately assigned as polygon interiors and exterior/holes each time that an input edge is passed. It does not distinguish between edges that are part of outer boundaries -from those of inner boundaries. +from those of inner boundaries. In a next version we will add the +winding number rule. \section SectionPolygonRepair_Definitions Definitions @@ -104,7 +105,7 @@ or multipolygon is merely used as a container of input line segments. These line segments are added to the arrangement as edges. Internally, this is done using a constrained triangulation where they are added as constraints. -With the even-odd heuristic, only the edges that are present an odd number of +With the even-odd rule, only the edges that are present an odd number of times in the input will be edges in the final arrangement. When these edges are only partially overlapping, only the parts that overlap an odd number of times will be edges in the final arrangement. @@ -120,7 +121,7 @@ First, the polygon exterior is labeled. For this, all of the faces that can be accessed from the exterior without passing through an edge are labeled as exterior faces. -Then, all other faces are labeled. For the even-odd heuristic, the label applied +Then, all other faces are labeled. For the even-odd rule, the label applied alternates between polygon interior and hole every time that an edge is passed. \subsection SubsectionPolygonRepair_Reconstruction Reconstruction of the Multipolygon @@ -175,14 +176,14 @@ since all of these intersections need to be calculated in the overlay. The polygon repair method as originally developed is described by Ledoux et al. \cgalCite{ledoux2014triangulation} and implemented in the -prepair software. +prepair software. This package is a reimplementation of the method with a new approach to label and reconstruct the multipolygons. It also incorporates improvements later -added to prepair, such as the application of the even-odd counting heuristic +added to prepair, such as the application of the even-odd counting heuristics to edges, which enables correct counting even on partially overlapping edges. Ken Arroyo Ohori developed this package during the Google Summer of -Code 2023 under the mentorship of Sébastien Loriot and Andreas Fabri. +Code 2023 mentored by Sébastien Loriot and Andreas Fabri. */ } /* namespace CGAL */ diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h index 769ad2640388..619d3b7fd8b9 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h @@ -21,6 +21,7 @@ namespace Polygon_repair { /*! \addtogroup PkgPolygonRepairRef + Tag class to select the even odd rule when calling `CGAL::Polygon_repair::repair()`. */ struct Even_odd_rule {}; diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index b3453d6146a4..4e12f0b11f0b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -33,7 +33,10 @@ class Polygon_repair; #endif /// \ingroup PkgPolygonRepairFunctions -/// Repair a polygon without holes using +/// repairs a polygon without holes using the given rule +/// \tparam Kernel parameter of the input and output polygons +/// \tparam Container parameter of the input and output polygons +/// \tparam Rule must be `Even_odd_rule` template Multipolygon_with_holes_2 repair(const Polygon_2& , Rule rule) { CGAL_assertion(false); // rule not implemented @@ -51,7 +54,10 @@ Multipolygon_with_holes_2 repair(const Polygon_2 Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule rule) { CGAL_assertion(false); // rule not implemented @@ -69,7 +75,10 @@ Multipolygon_with_holes_2 repair(const Polygon_with_holes_2 Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp, Rule rule) { CGAL_assertion(false); // rule not implemented From 458c52eb7cbef00a517848b0dcbe4f915a4c6720 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 15:03:52 +0000 Subject: [PATCH 396/520] Fixes after Guillaume's review --- Polygon_repair/doc/Polygon_repair/PackageDescription.txt | 5 +++-- Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt index 6aeb0392d074..0bd41658ad43 100644 --- a/Polygon_repair/doc/Polygon_repair/PackageDescription.txt +++ b/Polygon_repair/doc/Polygon_repair/PackageDescription.txt @@ -7,14 +7,15 @@ /*! \addtogroup PkgPolygonRepairRef -\todo check generated documentation \cgalPkgDescriptionBegin{2D Polygon Repair,PkgPolygonRepair} \cgalPkgPicture{Polygon_repair-small.png} \cgalPkgSummaryBegin \cgalPkgAuthors{Ken Arroyo Ohori} -\cgalPkgDesc{The package provides algorithms to repair 2D (multi)polygons. } +\cgalPkgDesc{This package provides algorithms to repair 2D polygons, polygons with holes, +and multipolygons with holes, by selecting faces of the arrangement of the input based on a selection rule. +Currently, only the even-odd rule is provided. } \cgalPkgManuals{Chapter_2D_Polygon_repair,PkgPolygonRepairRef} \cgalPkgSummaryEnd diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h index 619d3b7fd8b9..7c640bda7ab5 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h @@ -17,7 +17,7 @@ namespace CGAL { namespace Polygon_repair { - +/// @{ /*! \addtogroup PkgPolygonRepairRef @@ -25,6 +25,8 @@ namespace Polygon_repair { */ struct Even_odd_rule {}; +///@} + } // namespace Polygon_repair } // namespace CGAL From edc721e89433b3b51e84b30757c563c809b5153f Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 15:49:49 +0000 Subject: [PATCH 397/520] wording --- .../include/CGAL/Polygon_repair/Polygon_repair.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h index 4e12f0b11f0b..cdeda55d4878 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h @@ -33,12 +33,12 @@ class Polygon_repair; #endif /// \ingroup PkgPolygonRepairFunctions -/// repairs a polygon without holes using the given rule +/// repairs polygon `p` using the given rule /// \tparam Kernel parameter of the input and output polygons /// \tparam Container parameter of the input and output polygons /// \tparam Rule must be `Even_odd_rule` template -Multipolygon_with_holes_2 repair(const Polygon_2& , Rule rule) { +Multipolygon_with_holes_2 repair(const Polygon_2& p , Rule rule) { CGAL_assertion(false); // rule not implemented return Multipolygon_with_holes_2(); } @@ -54,7 +54,7 @@ Multipolygon_with_holes_2 repair(const Polygon_2 repair(const Polygon_with_holes_2 -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp, Rule rule) { +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule rule) { CGAL_assertion(false); // rule not implemented return Multipolygon_with_holes_2(); } From 653bc3616ba29831f4e999f7aff258de7066cbf7 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 16:04:41 +0000 Subject: [PATCH 398/520] wording --- Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h index 7c640bda7ab5..fc4351aa7dcd 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h @@ -17,10 +17,11 @@ namespace CGAL { namespace Polygon_repair { + +\addtogroup PkgPolygonRepairRef /// @{ - /*! - \addtogroup PkgPolygonRepairRef +/*! Tag class to select the even odd rule when calling `CGAL::Polygon_repair::repair()`. */ struct Even_odd_rule {}; From 5e0191494e75b5d6e3d6c855a2effe3678307218 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 17:00:59 +0000 Subject: [PATCH 399/520] C++ comment doxygen command --- Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h index fc4351aa7dcd..b2dc51bfe369 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/Even_odd_rule.h @@ -18,7 +18,7 @@ namespace CGAL { namespace Polygon_repair { -\addtogroup PkgPolygonRepairRef +/// \addtogroup PkgPolygonRepairRef /// @{ /*! From 1c31cfdcbb0d77ddc65456f213da3d415c7be3c5 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 7 Feb 2024 11:04:52 +0000 Subject: [PATCH 400/520] Fix path --- Polygon_repair/test/Polygon_repair/validate_wkt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp index ac92bfd9140e..a5584e6745cc 100644 --- a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp +++ b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp @@ -18,7 +18,7 @@ using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; int main(int argc, char* argv[]) { // std::string folder = "/Users/ken/Downloads/big polygons/"; - std::string folder = "/Users/ken/Versioned/cgal-public-dev/Polygon_repair/test/Polygon_repair/data/in"; + std::string folder = "data/in"; for (const auto& file: std::filesystem::directory_iterator(folder)) { if (file.path().filename().extension() != ".wkt") continue; From 5333c6f07292ab39d5abaa4cbfddbceafd21751e Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 7 Feb 2024 11:10:17 +0000 Subject: [PATCH 401/520] #define CGAL_NO_CDT_2_WARNING --- Polygon_repair/test/Polygon_repair/exact_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp index bfa34ab36c97..265c4b6fd751 100644 --- a/Polygon_repair/test/Polygon_repair/exact_test.cpp +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -1,3 +1,5 @@ +#define CGAL_NO_CDT_2_WARNING + #include #include #include From aa6ff65c02b1dabdf95c12ac0a6f4e1fcd118b27 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 7 Feb 2024 11:11:53 +0000 Subject: [PATCH 402/520] Change #include order to check dependencies --- .../test/Polygon_repair/repair_polygon_2_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 055e8c15a0b4..7150090da045 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -1,12 +1,12 @@ +#include +#include +#include + #include #include #include #include -#include -#include -#include - using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Point_2 = Kernel::Point_2; using Polygon_2 = CGAL::Polygon_2; From 80075364eba5c17613e2f70b29a70e68320d3e87 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 7 Feb 2024 12:03:42 +0000 Subject: [PATCH 403/520] Only compile some of the test cases so that we can have it in a testsuite --- .../test/Polygon_repair/CMakeLists.txt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/CMakeLists.txt b/Polygon_repair/test/Polygon_repair/CMakeLists.txt index 6075ee3222ba..faced1f88058 100644 --- a/Polygon_repair/test/Polygon_repair/CMakeLists.txt +++ b/Polygon_repair/test/Polygon_repair/CMakeLists.txt @@ -7,13 +7,17 @@ project(Polygon_repair_Tests) find_package(CGAL REQUIRED OPTIONAL_COMPONENTS Qt6) # create a target per cppfile -file( - GLOB cppfiles - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -foreach(cppfile ${cppfiles}) - create_single_source_cgal_program("${cppfile}") -endforeach() +#file( +# GLOB cppfiles +# RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} +# ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +#foreach(cppfile ${cppfiles}) +# create_single_source_cgal_program("${cppfile}") +#endforeach() + +create_single_source_cgal_program( "draw_test_polygons.cpp" ) +create_single_source_cgal_program( "exact_test.cpp") +create_single_source_cgal_program( "repair_polygon_2_test.cpp" ) if(CGAL_Qt6_FOUND) target_link_libraries(draw_test_polygons PUBLIC CGAL::CGAL_Basic_viewer) From 75d1519e26ae1614f3649ea6b08da8e12975be0f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 7 Feb 2024 15:18:36 +0100 Subject: [PATCH 404/520] fixed test --- Orthtree/include/CGAL/Orthtree.h | 6 +- Orthtree/test/Orthtree/test_node_adjacent.cpp | 2 +- Orthtree/test/Orthtree/test_node_index.cpp | 2 +- Orthtree/test/Orthtree/test_octree_bbox.cpp | 4 +- .../test_octree_copy_move_constructors.cpp | 4 +- .../test_octree_custom_properties.cpp | 73 +++++++------------ .../test/Orthtree/test_octree_equality.cpp | 2 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 4 +- .../Orthtree/test_octree_intersecting.cpp | 4 +- .../test/Orthtree/test_octree_kernels.cpp | 4 +- Orthtree/test/Orthtree/test_octree_locate.cpp | 4 +- .../Orthtree/test_octree_nearest_neighbor.cpp | 6 +- Orthtree/test/Orthtree/test_octree_refine.cpp | 4 +- .../test/Orthtree/test_octree_traverse.cpp | 4 +- 14 files changed, 49 insertions(+), 74 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index dc2e41555d1b..09159a3024d8 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -511,8 +511,7 @@ class Orthtree { \return pair of the property map and a Boolean which is `true` if the property needed to be created */ template - Pair_optional_adaptor> - add_property(const std::string& name, const T default_value = T()) { + std::pair, bool> add_property(const std::string& name, const T default_value = T()) { auto p = m_node_properties.get_or_add_property(name, default_value); return std::pair, bool>(Property_map(p.first), p.second); } @@ -527,8 +526,7 @@ class Orthtree { \return an optional containing the property map if it exists */ template - Pair_optional_adaptor> - property(const std::string& name) { + std::optional> property(const std::string& name) { auto p = m_node_properties.template get_property_if_exists(name); if (p) return std::optional >(Property_map(*p)); diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 2f79a63008b2..3655fb2f2c31 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index 8e045174de15..f2270251e340 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 2c944f7080a5..c6be3ecfacfd 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -1,10 +1,10 @@ #include +#include #include -#include #include +#include -#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index 9b8ae6ffbc84..8a76ac514bb4 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -2,12 +2,12 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include #include -#include #include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 0e14a51fbbcc..3c69724bd8a5 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -1,12 +1,12 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include #include -#include #include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; @@ -30,65 +30,42 @@ int main(void) { CGAL_USE(data_prop); // list of properties - std::size_t num = tree.properties().size(); - assert(num == 5); - auto prop2 = tree.add_property("test", int(0)); - assert(prop2.second); + assert(tree.properties().size() == 5); + auto prop1 = tree.add_property("test", int(5)); + assert(prop1.second); + // One property added assert(tree.properties().size() == 6); + // Default value should be respected + assert(prop1.first[tree.root()] == 5); + // Changes to individual nodes should be respected + prop1.first[tree.root()] = 0; + assert(prop1.first[tree.root()] == 0); - auto prop5 = tree.add_property("test", int(0)); - assert(!prop5.second); - - auto a3 = tree.add_property("test1", int(0)); - std::pair, bool> p3 = tree.add_property("test2", int(0)); - std::optional> o3 = tree.add_property("test3", int(0)); - - auto a4 = tree.add_property("test", int(0)); - std::pair, bool> p4 = tree.add_property("test", int(0)); - std::optional> o4 = tree.add_property("test", int(0)); - - auto a1 = tree.property("test"); - std::pair, bool> p1 = tree.property("test"); - std::optional> o1 = tree.property("test"); - auto f = a1.first; - auto pf1 = p1.first; - auto of1 = o1.value(); - auto fo1 = a1.value(); - std::cout << f.size() << std::endl; - - auto a2 = tree.property("test"); - std::pair, bool> p2 = tree.property("test"); - std::optional> o2 = tree.property("test"); - - //assert(prop3.has_value()); - - auto prop4 = tree.property("test"); - assert(!prop4.has_value()); + auto prop2 = tree.add_property("test", int(0)); + assert(!prop2.second); + assert(tree.properties().size() == 6); - // removal of properties - num = tree.properties().size(); - //tree.remove_node_property(*prop3); - assert(tree.properties().size() == (num - 1)); + auto prop3 = tree.add_property("test2", std::string()); + assert(prop3.second); + assert(tree.properties().size() == 7); - // Default value should be respected - auto node_int_property = tree.add_property("int", 5).first; - assert(node_int_property[tree.root()] == 5); + auto prop4 = tree.property("test"); + assert(prop4.has_value()); - // Changes to individual nodes should be respected - node_int_property[tree.root()] = 0; - assert(node_int_property[tree.root()] == 0); + auto prop5 = tree.property("test"); + assert(!prop5.has_value()); // Expanding the tree; new nodes should be assigned the default value tree.refine(10, 1); for (auto n : tree.traverse>()) { // Everything but the root will have the default value - if (!tree.is_root(n)) assert(node_int_property[n] == 5); + if (!tree.is_root(n)) assert(prop1.first[n] == 5); } // The root should have preserved its custom value - assert(node_int_property[tree.root()] == 0); - - + assert(prop1.first[tree.root()] == 0); + tree.remove_property(prop1.first); + assert(tree.properties().size() == 6); return EXIT_SUCCESS; } diff --git a/Orthtree/test/Orthtree/test_octree_equality.cpp b/Orthtree/test/Orthtree/test_octree_equality.cpp index fc71eb647ec0..db1c18a9a124 100644 --- a/Orthtree/test/Orthtree/test_octree_equality.cpp +++ b/Orthtree/test/Orthtree/test_octree_equality.cpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 972b4a7168ad..226aa29b10f8 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -2,13 +2,13 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include #include #include -#include #include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 6b246bb89476..ae40a16037e2 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -1,12 +1,12 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include #include #include -#include -#include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_kernels.cpp b/Orthtree/test/Orthtree/test_octree_kernels.cpp index 3b2353738b66..7cd6fb0de8bb 100644 --- a/Orthtree/test/Orthtree/test_octree_kernels.cpp +++ b/Orthtree/test/Orthtree/test_octree_kernels.cpp @@ -1,9 +1,9 @@ #include +#include +#include #include #include #include -#include -#include template void test() diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 708727341490..77a243d7bc66 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -2,11 +2,11 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include #include -#include #include -#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index 18ae90b40058..e558ebbfe6d7 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -2,17 +2,17 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include +#include #include #include -#include #include #include #include #include #include -#include -#include +#include using namespace std::chrono; diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 64ef05648a81..f61b6141e53e 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -2,11 +2,11 @@ #define CGAL_TRACE_STREAM std::cerr #include +#include #include -#include #include -#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 13d7527bd993..4d96f88442cf 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -1,12 +1,12 @@ #define CGAL_TRACE_STREAM std::cerr +#include #include #include +#include #include -#include -#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; From a5aba5bc2f1ef38d8f641a3c4933cff55f1b854e Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 7 Feb 2024 15:22:57 +0100 Subject: [PATCH 405/520] removing Pair_optional_adaptor --- Orthtree/include/CGAL/Orthtree.h | 1 - .../include/CGAL/Pair_optional_adaptor.h | 49 ------------------- 2 files changed, 50 deletions(-) delete mode 100644 STL_Extension/include/CGAL/Pair_optional_adaptor.h diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 09159a3024d8..39ddbf542e7e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/STL_Extension/include/CGAL/Pair_optional_adaptor.h b/STL_Extension/include/CGAL/Pair_optional_adaptor.h deleted file mode 100644 index 49c5c076dfda..000000000000 --- a/STL_Extension/include/CGAL/Pair_optional_adaptor.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2024 GeometryFactory Sarl (France). -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org). -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial -// -// -// Author(s) : Sven Oesau - -#ifndef CGAL_PAIR_OPTIONAL_ADAPTOR -#define CGAL_PAIR_OPTIONAL_ADAPTOR - -namespace CGAL { - -// T is supposed to be a handle -template -class Pair_optional_adaptor : public std::optional { -public: - Pair_optional_adaptor(std::optional& obj) : std::optional(obj), second(obj.has_value()), first(t_storage.t) { - if (obj.has_value()) - t_storage.t = *obj; - } - - Pair_optional_adaptor(const std::nullopt_t& obj) : std::optional(std::nullopt), second(false), first(t_storage.t) {} - - Pair_optional_adaptor(std::pair& p) : std::optional(p.second ? p.first : std::optional()), first(t_storage.t), second(p.second), t_storage(p.first) {} - - operator std::pair() { - return std::pair(first, second); - } - - T &first; - bool second; - -private: - union T_value { - T t; - int i; - T_value() : i(0) {} - T_value(T t) : t(t) {} - } t_storage; -}; - -} // CGAL - -#endif \ No newline at end of file From 7b5e2be0cf3cc4fb976735b09b494bd1b6f0a4a0 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 8 Feb 2024 11:45:48 +0100 Subject: [PATCH 406/520] switching Property_container to multimap to allow properties that share the same name but have different types --- Orthtree/include/CGAL/Orthtree.h | 10 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 2 - .../include/CGAL/Property_container.h | 175 ++++++++++-------- .../Property_map/test_Property_container.cpp | 7 +- 4 files changed, 102 insertions(+), 92 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 39ddbf542e7e..fe5530eeecc6 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -191,11 +191,11 @@ class Orthtree { */ explicit Orthtree(Traits traits) : m_traits(traits), - m_node_contents(m_node_properties.template add_property("contents")), - m_node_depths(m_node_properties.template add_property("depths", 0)), - m_node_coordinates(m_node_properties.template add_property("coordinates")), - m_node_parents(m_node_properties.template add_property>("parents")), - m_node_children(m_node_properties.template add_property>("children")) { + m_node_contents(m_node_properties.template get_or_add_property("contents").first), + m_node_depths(m_node_properties.template get_or_add_property("depths", 0).first), + m_node_coordinates(m_node_properties.template get_or_add_property("coordinates").first), + m_node_parents(m_node_properties.template get_or_add_property>("parents").first), + m_node_children(m_node_properties.template get_or_add_property>("children").first) { m_node_properties.emplace(); diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 5d1fd93bfded..3eca22fd8ec1 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -36,8 +36,6 @@ void reassign_points( return; } - auto traits = tree.traits(); - // Split the point collection around the center point on this dimension auto split_point = std::partition( points.begin(), points.end(), diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 9cb1bb9c6b25..79ea9905f8e3 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -109,7 +109,7 @@ class Property_array : public Property_array_base { CGAL_precondition(m_active_indices.size() == m_data.size()); } -// desactived as MSVC 2017 as an issue with that but it is not currently used. +// deactived as MSVC 2017 as an issue with that but it is not currently used. #if 0 virtual void move(Property_array_base&& other_base) override { auto&& other = static_cast&&>(other_base); @@ -246,7 +246,7 @@ class Property_array_handle { template class Property_container { - std::map>> m_property_arrays{}; + std::multimap>> m_properties; std::vector m_active_indices{}; public: @@ -258,9 +258,10 @@ class Property_container { Property_container(const Property_container& other) { m_active_indices = other.m_active_indices; - for (auto [name, array]: other.m_property_arrays) { + + for (auto [name, array] : other.m_properties) { // todo: this could probably be made faster using emplace_hint - m_property_arrays.emplace( + m_properties.emplace( name, array->clone(m_active_indices) ); @@ -269,49 +270,45 @@ class Property_container { Property_container(Property_container&& other) { *this = std::move(other); } - // todo: maybe this could be implemented in terms of the move assignment operator? + // This is not exactly an assignment as existing unique properties are kept. Property_container& operator=(const Property_container& other) { m_active_indices = other.m_active_indices; - for (auto [name, other_array]: other.m_property_arrays) { - - // If this container has a property by the same name - auto it = m_property_arrays.find(name); - if (it != m_property_arrays.end()) { - auto [_, this_array] = *it; - - // No naming collisions with different types allowed - CGAL_precondition(typeid(*this_array) == typeid(*other_array)); - - // Copy the data from the other array - this_array->copy(*other_array); - } else { - // Adds the new property - m_property_arrays.emplace(name, other_array->clone(m_active_indices)); + for (auto [name, array] : other.m_properties) { + // search if property already exists + auto range = m_properties.equal_range(name); + auto it = range.first; + for (; it != range.second; it++) { + if (typeid(*array) == typeid((*it->second))) + break; } + + if (it != range.second) + it->second->copy(*array); + else + m_properties.emplace(name, array->clone(m_active_indices)); } + return *this; } - + + // This is not exactly an assignment as existing unique properties are kept. Property_container& operator=(Property_container&& other) { m_active_indices = std::move(other.m_active_indices); - for (auto [name, other_array]: other.m_property_arrays) { - // If this container has a property by the same name - auto it = m_property_arrays.find(name); - if (it != m_property_arrays.end()) { - auto [_, this_array] = *it; - - // No naming collisions with different types allowed - CGAL_precondition(typeid(*this_array) == typeid(*other_array)); - - // Copy the data from the other array - this_array->copy(std::move(*other_array)); - - } else { - // Adds the new property - m_property_arrays.emplace(name, other_array->clone(m_active_indices)); + for (auto [name, array] : other.m_properties) { + // search if property already exists + auto range = m_properties.equal_range(name); + auto it = range.first; + for (; it != range.second; it++) { + if (typeid(*array) == typeid((*it->second))) + break; } + + if (it != range.second) + it->second->copy(std::move(*array)); + else + m_properties.emplace(name, array->clone(m_active_indices)); } // The moved-from property map should retain all of its properties, but contain 0 elements @@ -322,16 +319,22 @@ class Property_container { template std::pair>, bool> get_or_add_property(const std::string& name, const T default_value = T()) { - auto [it, created] = m_property_arrays.emplace( + auto range = m_properties.equal_range(name); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return { {*typed_array_ptr}, false }; + } + + auto it = m_properties.emplace( name, std::make_shared>( m_active_indices, default_value ) ); - auto [key, array] = *it; - auto& typed_array = dynamic_cast&>(*array); - return {{typed_array}, created}; + + return {{*dynamic_cast*>(it->second.get())}, true}; } template @@ -342,65 +345,66 @@ class Property_container { return array.get(); } +/* // todo: misleading name, maybe it could be add_same_properties? void copy_properties(const Property_container& other) { - for (auto [name, other_array]: other.m_property_arrays) { + for (auto [name, other_array]: other.m_properties) { // If this container doesn't have any property by this name, add it (with the same type as in other) if (!property_exists(name)) m_property_arrays.emplace(name, other_array->empty_clone(m_active_indices)); } - } + }*/ template const Property_array& get_property(const std::string& name) const { - CGAL_precondition(m_property_arrays.count(name) != 0); - return dynamic_cast&>(*m_property_arrays.at(name)); + return *(get_property_if_exists(name)); } template Property_array& get_property(const std::string& name) { - CGAL_precondition(m_property_arrays.count(name) != 0); - return dynamic_cast&>(*m_property_arrays.at(name)); + return *(get_property_if_exists(name)); } template - std::optional>> get_property_if_exists(const std::string& name) { - auto it = m_property_arrays.find(name); - if (it == m_property_arrays.end()) return {}; - auto [_, array] = *it; - if (typeid(*array) != typeid(Property_array)) return {}; - return dynamic_cast&>(*m_property_arrays.at(name)); + std::optional>> get_property_if_exists(const std::string& name) const { + auto range = m_properties.equal_range(name); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return *typed_array_ptr; + } + + return {}; } template bool property_exists(const std::string& name) const { - auto it = m_property_arrays.find(name); - if (it == m_property_arrays.end()) return false; - auto [_, array] = *it; - if (typeid(*array) != typeid(Property_array)) return false; - return true; - } + auto range = m_properties.equal_range(name); - // todo: maybe the non-type-strict version is useful? - bool property_exists(const std::string& name) const { - auto it = m_property_arrays.find(name); - return (it != m_property_arrays.end()); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return true; + } + + return false; } /*! - * Removes a property array from the container + * Removes all properties with the name from the container. * * @param name - * @return True if a container with this name existed, false otherwise + * @return number of removed properties. */ - bool remove_property(const std::string& name) { return m_property_arrays.erase(name) == 1; } + std::size_t remove_properties(const std::string& name) { return m_properties.erase(name); } template bool remove_property(const Property_array& arrayToRemove) { - for (auto it = m_property_arrays.begin(); it != m_property_arrays.end(); ++it) { + const Property_array_base* ref = dynamic_cast*>(&arrayToRemove); + for (auto it = m_properties.begin(); it != m_properties.end(); it++) { auto const& [name, array] = *it; - if (array.get() == dynamic_cast*>(&arrayToRemove)) { - m_property_arrays.erase(it); + if (array.get() == ref) { + m_properties.erase(it); return true; } } @@ -421,25 +425,26 @@ class Property_container { std::vector properties() const { std::vector property_names{}; - for (auto const& [name, _]: m_property_arrays) + for (auto const& [name, _]: m_properties) property_names.emplace_back(name); return property_names; } - std::size_t num_properties() const { return m_property_arrays.size(); } + std::size_t num_properties() const { return m_properties.size(); } +/* Deactivated as there may be several Property_maps with different types but the same name. const std::type_info& property_type(const std::string& name) const { if (auto it = m_property_arrays.find(name); it != m_property_arrays.end()) return it->second->type(); else return typeid(void); - } + }*/ public: void reserve(std::size_t n) { m_active_indices.resize(n); - for (auto [name, array]: m_property_arrays) + for (auto [name, array]: m_properties) array->reserve(n); } @@ -528,18 +533,18 @@ class Property_container { } void swap(Index a, Index b) { - for (auto [name, array]: m_property_arrays) + for (auto [name, array]: m_properties) array->swap(a, b); } void reset(Index i) { - for (auto [name, array]: m_property_arrays) + for (auto [name, array]: m_properties) array->reset(i); } void erase(Index i) { m_active_indices[i] = false; - for (auto [name, array]: m_property_arrays) + for (auto [name, array]: m_properties) array->reset(i); } @@ -571,7 +576,7 @@ class Property_container { } void shrink_to_fit() { - for (auto [name, array]: m_property_arrays) + for (auto [name, array]: m_properties) array->shrink_to_fit(); } @@ -589,15 +594,23 @@ class Property_container { void append(const Property_container& other) { m_active_indices.insert(m_active_indices.end(), other.m_active_indices.begin(), other.m_active_indices.end()); - for (auto [name, array]: m_property_arrays) { - auto it = other.m_property_arrays.find(name); - if (it != other.m_property_arrays.end()) - array->append(*it->second); + for (auto [name, array]: m_properties) { + + auto range = other.m_properties.equal_range(name); + auto it = range.first; + for (; it != range.second; it++) { + if (typeid(array.get()) == typeid((it->second.get()))) + break; + } + + if (it != range.second) + array->append(*it->second.get()); else array->reserve(m_active_indices.size()); } } +/* // todo: maybe should be renamed to transfer_from, but I'd rather remove this functionality entirely void transfer(const Property_container& other, Index other_index, Index this_index) { CGAL_precondition(other.m_property_arrays.size() == m_property_arrays.size()); @@ -605,7 +618,7 @@ class Property_container { auto other_array = other.m_property_arrays.at(name); array->transfer_from(*other_array, other_index, this_index); } - } + }*/ // todo: maybe a compress() method? }; diff --git a/Property_map/test/Property_map/test_Property_container.cpp b/Property_map/test/Property_map/test_Property_container.cpp index 696fd34a27ef..1efe25f4001a 100644 --- a/Property_map/test/Property_map/test_Property_container.cpp +++ b/Property_map/test/Property_map/test_Property_container.cpp @@ -23,13 +23,13 @@ void test_property_creation() { assert(floats.get() == properties.get_property("float")); // remove() should delete a property array & return if it existed - assert(!properties.remove_property("not-a-real-property")); - auto removed = properties.remove_property("integer"); + assert(!properties.remove_properties("not-a-real-property")); + auto removed = properties.remove_property(integers); assert(removed); assert(properties.num_properties() == 1); // Add a new property - auto [bools, bools_created] = properties.get_or_add_property("bools", false); + auto [bools, bools_created] = properties.get_or_add_property("bools", false); static_assert(std::is_same_v>>); Property_array& b = bools.get(); CGAL_USE(b); @@ -179,7 +179,6 @@ void test_append() { // Additional properties in the first group should have expanded too, and been filled with defaults // note: the property array must be const, because non const operator[] doesn't work for vector! assert(std::as_const(properties_a).get_property("bools")[12] == true); - } void test_constructors() { From 9c93b606406e63d7f7147dbe172ef1316702faab Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 8 Feb 2024 11:49:29 +0100 Subject: [PATCH 407/520] removing trailing whitespaces --- Property_map/include/CGAL/Property_container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 79ea9905f8e3..f7d2c4a0156e 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -291,7 +291,7 @@ class Property_container { return *this; } - + // This is not exactly an assignment as existing unique properties are kept. Property_container& operator=(Property_container&& other) { m_active_indices = std::move(other.m_active_indices); From 8f106bf8bbd8946c1d7c9b136130e02849cb02da Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 8 Feb 2024 12:07:42 +0100 Subject: [PATCH 408/520] removed old occurrence of m_property_arrays --- Property_map/include/CGAL/Property_container.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index f7d2c4a0156e..739f652b3184 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -414,10 +414,10 @@ class Property_container { void remove_all_properties_except(const std::vector& preserved_names) { // todo: if this is used often, it should take a parameter pack instead of a vector // A fold expression could then be used in place of std::find for better performance - for (auto it = m_property_arrays.begin(); it != m_property_arrays.end();) { + for (auto it = m_properties.begin(); it != m_properties.end();) { auto const& [name, array] = *it; if (std::find(preserved_names.begin(), preserved_names.end(), name) == preserved_names.end()) - it = m_property_arrays.erase(it); + it = m_properties.erase(it); else it++; } From 009791f4f8700279a1ea222f78db29621fe3cf2e Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 13 Feb 2024 12:33:48 +0100 Subject: [PATCH 409/520] adaptation of Orthtree interface to allow OrthtreeTraits without data adding Orthtree_traits_without_data template specializations for Property_array, Property_array_handle and Property_container to allow for void as data type extending test_octree_copy_move_constructors to include traits without data --- Orthtree/include/CGAL/Orthtree.h | 70 ++++++++-- .../include/CGAL/Orthtree/Split_predicates.h | 4 +- .../CGAL/Orthtree_traits_without_data.h | 61 ++++++++ .../test_octree_copy_move_constructors.cpp | 52 ++++--- .../include/CGAL/Property_container.h | 130 +++++++++++++++++- 5 files changed, 281 insertions(+), 36 deletions(-) create mode 100644 Orthtree/include/CGAL/Orthtree_traits_without_data.h diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index fe5530eeecc6..0f1d9db0be03 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -42,9 +42,16 @@ #include #include #include +#include + +#include namespace CGAL { +namespace internal { +BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) +} + /*! \ingroup PkgOrthtreeRef @@ -65,13 +72,26 @@ namespace CGAL { */ template class Orthtree { +private: + template + struct Node_data_selector {}; -public: + template + struct Node_data_selector> { + using type = typename A::Node_data; + }; + template + struct Node_data_selector> { + using type = void; + }; + +public: /// \name Template Types /// @{ using Traits = GeomTraits; ///< Geometry traits /// @} + using Has_data = boost::mpl::bool_::value>; /// \name Traits Types /// @{ @@ -84,7 +104,7 @@ class Orthtree { using Adjacency = typename Traits::Adjacency; ///< Adjacency type. using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. - using Node_data = typename Traits::Node_data; + using Node_data = typename Node_data_selector::type; /// @} @@ -153,16 +173,16 @@ class Orthtree { using Node_property_container = Properties::Experimental::Property_container; template - using Property_array = typename Properties::Experimental::Property_container::template Array; + using Property_array = typename Properties::Experimental::Property_array_handle; Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - Property_array& m_node_contents; - Property_array& m_node_depths; - Property_array& m_node_coordinates; - Property_array>& m_node_parents; - Property_array>& m_node_children; + Property_array m_node_contents; + Property_array m_node_depths; + Property_array m_node_coordinates; + Property_array> m_node_parents; + Property_array> m_node_children; using Bbox_dimensions = std::array; Bbox m_bbox; @@ -170,6 +190,18 @@ class Orthtree { Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ + template + void init_data(); + + template<> + void init_data>() { + data(root()) = m_traits.construct_root_node_contents_object()(); + } + + template<> + void init_data>() { + } + public: /// \name Constructor @@ -211,7 +243,8 @@ class Orthtree { } // save orthtree attributes m_side_per_depth.push_back(size); - data(root()) = m_traits.construct_root_node_contents_object()(); + + init_data(); } /// @} @@ -695,14 +728,16 @@ class Orthtree { /*! \brief retrieves a reference to the Node_data associated with the node specified by `n`. */ - Node_data& data(Node_index n) { + template + auto data(Node_index n) -> std::enable_if_t::value, Dummy&>{ return m_node_contents[n]; } /*! \brief retrieves a const reference to the Node_data associated with the node specified by `n`. */ - const Node_data& data(Node_index n) const { + template + auto data(Node_index n) const -> std::enable_if_t::value, const Dummy&> { return m_node_contents[n]; } @@ -930,7 +965,7 @@ class Orthtree { Point center = barycenter(n); // Add the node's contents to its children - m_traits.distribute_node_contents_object()(n, *this, center); + distribute_node_contents(n, center); } /*! @@ -1138,6 +1173,17 @@ class Orthtree { return output; } + template + void distribute_node_contents(Node_index n, const Point& center); + + template<> + void distribute_node_contents>(Node_index n, const Point& center) { + m_traits.distribute_node_contents_object()(n, *this, center); + } + + template<> + void distribute_node_contents>(Node_index n, const Point& center) {} + public: /// \cond SKIP_IN_MANUAL diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index bc04c4b815e9..cf3ad5221486 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -31,7 +31,7 @@ namespace Orthtrees { split if it contains more than a certain number of items. \warning This split predicate is only appropriate for trees with traits classes where - `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. + `Node_data` is present and a model of `Range`. `RandomAccessRange` is suggested for performance. */ class Maximum_number_of_inliers { @@ -96,7 +96,7 @@ class Maximum_depth { than `bucket_size`, it is not split. \warning This split predicate is only appropriate for trees with traits classes where - `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. + `Node_data` is present and a model of `Range`. `RandomAccessRange` is suggested for performance. */ class Maximum_depth_and_maximum_number_of_inliers { diff --git a/Orthtree/include/CGAL/Orthtree_traits_without_data.h b/Orthtree/include/CGAL/Orthtree_traits_without_data.h new file mode 100644 index 000000000000..608c058407f7 --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_without_data.h @@ -0,0 +1,61 @@ +// Copyright (c) 2023 INRIA (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) : Sven Oesau + +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H + +#include + +#include +#include +#include +#include + +#include + +namespace CGAL { + +/*! + \ingroup PkgOrthtreeTraits + + Traits class for defining an orthtree using the class `CGAL::Orthtree` without storing data in the nodes. + + \tparam GeomTraits model of `Kernel`. + \tparam dimension the dimension of the ambient Euclidean space. + + \cgalModels{OrthtreeTraits} + \sa `CGAL::Octree` + \sa `CGAL::Quadtree` + \sa `CGAL::Orthtree_traits_base` +*/ +template +struct Orthtree_traits_without_data: public Orthtree_traits_base { +public: + using Base = Orthtree_traits_base; + using Self = Orthtree_traits_without_data; + using Tree = Orthtree; + + using Node_index = typename Base::Node_index; + + Orthtree_traits_without_data() {} + + auto construct_root_node_bbox_object() const { + return [&]() -> typename Self::Bbox_d { + return {std::apply(Self::construct_point_d_object(), std::array{-1.0, -1.0, -1.0}), + std::apply(Self::construct_point_d_object(), std::array{1.0, 1.0, 1.0})}; + }; + } +}; + +} + + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index 8a76ac514bb4..3716036ede24 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -9,44 +9,56 @@ #include #include +#include +#include + using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; using FT = Kernel::FT; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; +using Octree_without_data = CGAL::Orthtree>; -int main(void) +template +int test(Tree &tree) { - std::size_t nb_pts = 100; - Point_set points; - CGAL::Random_points_in_cube_3 generator; - points.reserve(nb_pts); - for (std::size_t i = 0; i < nb_pts; ++i) - points.insert(*(generator++)); - - Octree base ({points, points.point_map()}); - assert (base.is_leaf(base.root())); // base is not refined yet + assert (tree.is_leaf(tree.root())); // tree is not refined yet - Octree copy1 (base); + Tree copy1 (tree); assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either - assert (base == copy1); // base should be equal to copy1 + assert (tree == copy1); // tree should be equal to copy1 - base.refine(); - assert (!base.is_leaf(base.root())); // base is now refined + tree.refine(CGAL::Orthtrees::Maximum_depth(5)); + assert (!tree.is_leaf(tree.root())); // tree is now refined assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined - assert (base != copy1); // base should be different from copy1 + assert (tree != copy1); // tree should be different from copy1 - Octree copy2 (base); + Tree copy2 (tree); assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined - assert (base == copy2); // base should be equal to copy2 + assert (tree == copy2); // tree should be equal to copy2 - Octree move (std::move(base)); + Tree move (std::move(tree)); assert (!move.is_leaf(move.root())); // move should be refined - // fixme: my linter isn't happy about use-after-move - assert (base.is_leaf(base.root())); // base should be back to init state (unrefined) + + assert (tree.is_leaf(tree.root())); // tree should be back to init state (unrefined) assert (copy1.is_leaf(copy1.root())); // copy1 still unaffected and still unrefined assert (!copy2.is_leaf(copy2.root())); // copy2 unaffected by move and still refined assert (move == copy2); // move should be equal to copy2 return EXIT_SUCCESS; } + +void main() { + std::size_t nb_pts = 100; + Point_set points; + CGAL::Random_points_in_cube_3 generator; + points.reserve(nb_pts); + for (std::size_t i = 0; i < nb_pts; ++i) + points.insert(*(generator++)); + + Octree base({ points, points.point_map() }); + test(base); + + Octree_without_data base2({}); + test(base2); +} diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 739f652b3184..16a44ff1dc2d 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -191,6 +191,43 @@ class Property_array : public Property_array_base { }; +template +class Property_array : public Property_array_base { +public: + Property_array() {} + + virtual std::shared_ptr> empty_clone(const std::vector& active_indices) { + return std::make_shared>(); + } + + virtual std::shared_ptr> clone(const std::vector& active_indices) { + return std::make_shared>(); + } + + virtual void copy(const Property_array_base& other) {} + + // desactived as MSVC 2017 as an issue with that but it is not currently used. +#if 0 + virtual void move(Property_array_base&& other) = 0; +#endif + + virtual void append(const Property_array_base& other) {} + + virtual void reserve(std::size_t n) {} + + virtual void shrink_to_fit() {} + + virtual void swap(Index a, Index b) {} + + virtual void reset(Index i) {} + + virtual const std::type_info& type() const { + return typeid(void); + } + + virtual void transfer_from(const Property_array_base& other_base, Index other_index, Index this_index) {} +}; + // todo: property maps/array handles should go in their own file // todo: add const/read-only handle @@ -243,6 +280,56 @@ class Property_array_handle { }; +// void specialization +template +class Property_array_handle { + + std::reference_wrapper> m_array; + std::vector m_dummy; + +public: + + // Necessary for use as a boost::property_type + using key_type = Index; + using value_type = void; + using reference = void; + using const_reference = void; + using category = boost::lvalue_property_map_tag; + + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + + Property_array_handle(Property_array& array) : m_array(array) {} + + [[nodiscard]] std::size_t size() const { return 0; } + + [[nodiscard]] std::size_t capacity() const { return 0; } + + Property_array& array() const { return m_array.get(); } + + // todo: This might not be needed, if the other operator[] is made const + const_reference operator[](Index i) const { } + + reference operator[](Index i) { } + + // todo: maybe these can be const, in an lvalue property map? + iterator begin() { return m_dummy.begin(); } + + iterator end() { return m_dummy.end(); } + + const_iterator begin() const { return m_dummy.begin(); } + + const_iterator end() const { return m_dummy.end(); } + + bool operator==(const Property_array_handle& other) const { return true; } + + bool operator!=(const Property_array_handle& other) const { return false; } + + inline friend reference get(Property_array_handle p, const Index& i) {} + + //inline friend void put(Property_array_handle p, const Index& i, const T& v) {} +}; + template class Property_container { @@ -318,7 +405,46 @@ class Property_container { template std::pair>, bool> - get_or_add_property(const std::string& name, const T default_value = T()) { + get_or_add_property(const std::string& name) { + auto range = m_properties.equal_range(name); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return { {*typed_array_ptr}, false }; + } + + auto it = m_properties.emplace( + name, + std::make_shared>( + m_active_indices, + T() + ) + ); + + return { {*dynamic_cast*>(it->second.get())}, true }; + } + + template<> + std::pair>, bool> + get_or_add_property(const std::string& name) { + auto range = m_properties.equal_range(name); + for (auto it = range.first; it != range.second; it++) { + Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return { {*typed_array_ptr}, false }; + } + + auto it = m_properties.emplace( + name, + std::make_shared>() + ); + + return { {*dynamic_cast*>(it->second.get())}, true }; + } + + template + std::pair>, bool> + get_or_add_property(const std::string& name, const T default_value) { auto range = m_properties.equal_range(name); for (auto it = range.first; it != range.second; it++) { Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); @@ -334,7 +460,7 @@ class Property_container { ) ); - return {{*dynamic_cast*>(it->second.get())}, true}; + return { {*dynamic_cast*>(it->second.get())}, true }; } template From ae18495c5696b548badfe8d70a3b3cab24f338e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 13 Feb 2024 16:51:54 +0100 Subject: [PATCH 410/520] simplify implementation of no data case to make it work with non MSVC compilers surprisingly tests are broken --- Orthtree/include/CGAL/Orthtree.h | 55 +++----- .../test_octree_copy_move_constructors.cpp | 23 ++-- .../include/CGAL/Property_container.h | 130 +----------------- 3 files changed, 35 insertions(+), 173 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 0f1d9db0be03..03a18ac0c4f0 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -72,26 +72,26 @@ BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) */ template class Orthtree { -private: - template + template struct Node_data_selector {}; template - struct Node_data_selector> { + struct Node_data_selector { using type = typename A::Node_data; }; template - struct Node_data_selector> { + struct Node_data_selector { using type = void; }; + static inline constexpr bool has_data = internal::has_Node_data::value; + public: /// \name Template Types /// @{ using Traits = GeomTraits; ///< Geometry traits /// @} - using Has_data = boost::mpl::bool_::value>; /// \name Traits Types /// @{ @@ -104,7 +104,7 @@ class Orthtree { using Adjacency = typename Traits::Adjacency; ///< Adjacency type. using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. - using Node_data = typename Node_data_selector::type; + using Node_data = typename Node_data_selector::type; /// @} @@ -178,7 +178,7 @@ class Orthtree { Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - Property_array m_node_contents; + std::conditional_t, void*> m_node_contents; Property_array m_node_depths; Property_array m_node_coordinates; Property_array> m_node_parents; @@ -190,17 +190,13 @@ class Orthtree { Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ - template - void init_data(); - - template<> - void init_data>() { - data(root()) = m_traits.construct_root_node_contents_object()(); + template> + auto init_node_contents(std::true_type) -> std::enable_if_t + { + return Property_array(m_node_properties.template get_or_add_property("contents").first); } - template<> - void init_data>() { - } + void* init_node_contents(std::false_type) { return nullptr; } public: @@ -223,7 +219,7 @@ class Orthtree { */ explicit Orthtree(Traits traits) : m_traits(traits), - m_node_contents(m_node_properties.template get_or_add_property("contents").first), + m_node_contents(init_node_contents(std::bool_constant())), m_node_depths(m_node_properties.template get_or_add_property("depths", 0).first), m_node_coordinates(m_node_properties.template get_or_add_property("coordinates").first), m_node_parents(m_node_properties.template get_or_add_property>("parents").first), @@ -244,7 +240,8 @@ class Orthtree { // save orthtree attributes m_side_per_depth.push_back(size); - init_data(); + if constexpr (has_data) + m_traits.construct_root_node_contents_object()(); } /// @} @@ -253,7 +250,7 @@ class Orthtree { Orthtree(const Orthtree& other) : m_traits(other.m_traits), m_node_properties(other.m_node_properties), - m_node_contents(m_node_properties.template get_property("contents")), + m_node_contents(init_node_contents(std::bool_constant())), m_node_depths(m_node_properties.template get_property("depths")), m_node_coordinates(m_node_properties.template get_property("coordinates")), m_node_parents(m_node_properties.template get_property>("parents")), @@ -264,7 +261,7 @@ class Orthtree { Orthtree(Orthtree&& other) : m_traits(other.m_traits), m_node_properties(std::move(other.m_node_properties)), - m_node_contents(m_node_properties.template get_property("contents")), + m_node_contents(init_node_contents(std::bool_constant())), m_node_depths(m_node_properties.template get_property("depths")), m_node_coordinates(m_node_properties.template get_property("coordinates")), m_node_parents(m_node_properties.template get_property>("parents")), @@ -729,7 +726,7 @@ class Orthtree { \brief retrieves a reference to the Node_data associated with the node specified by `n`. */ template - auto data(Node_index n) -> std::enable_if_t::value, Dummy&>{ + auto data(Node_index n) -> std::enable_if_t{ return m_node_contents[n]; } @@ -737,7 +734,7 @@ class Orthtree { \brief retrieves a const reference to the Node_data associated with the node specified by `n`. */ template - auto data(Node_index n) const -> std::enable_if_t::value, const Dummy&> { + auto data(Node_index n) const -> std::enable_if_t { return m_node_contents[n]; } @@ -965,7 +962,8 @@ class Orthtree { Point center = barycenter(n); // Add the node's contents to its children - distribute_node_contents(n, center); + if constexpr (has_data) + m_traits.distribute_node_contents_object()(n, *this, center); } /*! @@ -1173,17 +1171,6 @@ class Orthtree { return output; } - template - void distribute_node_contents(Node_index n, const Point& center); - - template<> - void distribute_node_contents>(Node_index n, const Point& center) { - m_traits.distribute_node_contents_object()(n, *this, center); - } - - template<> - void distribute_node_contents>(Node_index n, const Point& center) {} - public: /// \cond SKIP_IN_MANUAL diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index 3716036ede24..467ad272ff42 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -48,17 +48,18 @@ int test(Tree &tree) return EXIT_SUCCESS; } -void main() { - std::size_t nb_pts = 100; - Point_set points; - CGAL::Random_points_in_cube_3 generator; - points.reserve(nb_pts); - for (std::size_t i = 0; i < nb_pts; ++i) - points.insert(*(generator++)); +int main() +{ + std::size_t nb_pts = 100; + Point_set points; + CGAL::Random_points_in_cube_3 generator; + points.reserve(nb_pts); + for (std::size_t i = 0; i < nb_pts; ++i) + points.insert(*(generator++)); - Octree base({ points, points.point_map() }); - test(base); + Octree base({ points, points.point_map() }); + test(base); - Octree_without_data base2({}); - test(base2); + Octree_without_data base2({}); + test(base2); } diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 16a44ff1dc2d..739f652b3184 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -191,43 +191,6 @@ class Property_array : public Property_array_base { }; -template -class Property_array : public Property_array_base { -public: - Property_array() {} - - virtual std::shared_ptr> empty_clone(const std::vector& active_indices) { - return std::make_shared>(); - } - - virtual std::shared_ptr> clone(const std::vector& active_indices) { - return std::make_shared>(); - } - - virtual void copy(const Property_array_base& other) {} - - // desactived as MSVC 2017 as an issue with that but it is not currently used. -#if 0 - virtual void move(Property_array_base&& other) = 0; -#endif - - virtual void append(const Property_array_base& other) {} - - virtual void reserve(std::size_t n) {} - - virtual void shrink_to_fit() {} - - virtual void swap(Index a, Index b) {} - - virtual void reset(Index i) {} - - virtual const std::type_info& type() const { - return typeid(void); - } - - virtual void transfer_from(const Property_array_base& other_base, Index other_index, Index this_index) {} -}; - // todo: property maps/array handles should go in their own file // todo: add const/read-only handle @@ -280,56 +243,6 @@ class Property_array_handle { }; -// void specialization -template -class Property_array_handle { - - std::reference_wrapper> m_array; - std::vector m_dummy; - -public: - - // Necessary for use as a boost::property_type - using key_type = Index; - using value_type = void; - using reference = void; - using const_reference = void; - using category = boost::lvalue_property_map_tag; - - using iterator = typename std::vector::iterator; - using const_iterator = typename std::vector::const_iterator; - - Property_array_handle(Property_array& array) : m_array(array) {} - - [[nodiscard]] std::size_t size() const { return 0; } - - [[nodiscard]] std::size_t capacity() const { return 0; } - - Property_array& array() const { return m_array.get(); } - - // todo: This might not be needed, if the other operator[] is made const - const_reference operator[](Index i) const { } - - reference operator[](Index i) { } - - // todo: maybe these can be const, in an lvalue property map? - iterator begin() { return m_dummy.begin(); } - - iterator end() { return m_dummy.end(); } - - const_iterator begin() const { return m_dummy.begin(); } - - const_iterator end() const { return m_dummy.end(); } - - bool operator==(const Property_array_handle& other) const { return true; } - - bool operator!=(const Property_array_handle& other) const { return false; } - - inline friend reference get(Property_array_handle p, const Index& i) {} - - //inline friend void put(Property_array_handle p, const Index& i, const T& v) {} -}; - template class Property_container { @@ -405,46 +318,7 @@ class Property_container { template std::pair>, bool> - get_or_add_property(const std::string& name) { - auto range = m_properties.equal_range(name); - for (auto it = range.first; it != range.second; it++) { - Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); - if (typed_array_ptr != nullptr) - return { {*typed_array_ptr}, false }; - } - - auto it = m_properties.emplace( - name, - std::make_shared>( - m_active_indices, - T() - ) - ); - - return { {*dynamic_cast*>(it->second.get())}, true }; - } - - template<> - std::pair>, bool> - get_or_add_property(const std::string& name) { - auto range = m_properties.equal_range(name); - for (auto it = range.first; it != range.second; it++) { - Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); - if (typed_array_ptr != nullptr) - return { {*typed_array_ptr}, false }; - } - - auto it = m_properties.emplace( - name, - std::make_shared>() - ); - - return { {*dynamic_cast*>(it->second.get())}, true }; - } - - template - std::pair>, bool> - get_or_add_property(const std::string& name, const T default_value) { + get_or_add_property(const std::string& name, const T default_value = T()) { auto range = m_properties.equal_range(name); for (auto it = range.first; it != range.second; it++) { Property_array* typed_array_ptr = dynamic_cast*>(it->second.get()); @@ -460,7 +334,7 @@ class Property_container { ) ); - return { {*dynamic_cast*>(it->second.get())}, true }; + return {{*dynamic_cast*>(it->second.get())}, true}; } template From 6fd4a023c5968edc528643718d8b19a0a1539e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 13 Feb 2024 17:18:03 +0100 Subject: [PATCH 411/520] use a wrapper for node data tests are still failing... --- Orthtree/include/CGAL/Orthtree.h | 92 ++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 03a18ac0c4f0..d2fa0b3e0df7 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -48,9 +48,52 @@ namespace CGAL { -namespace internal { +namespace Orthtree_impl { + BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) -} + +template +struct Node_data_wrapper; + +template +struct Node_data_wrapper +{ + using Node_index = typename GT::Node_index; + using Node_data = typename GT::Node_data; + Properties::Experimental::Property_array_handle m_node_contents; + + template + Node_data_wrapper(Property_container& node_properties) + : m_node_contents(node_properties.template get_or_add_property("contents").first) + {} + + const Node_data& operator[](Node_index n) const + { + return m_node_contents[n]; + } + + Node_data& operator[](Node_index n) + { + return m_node_contents[n]; + } +}; + +template +struct Node_data_wrapper +{ + using Node_index = typename GT::Node_index; + using Node_data = void*; + + template + Node_data_wrapper(Property_container&) {} + + void* operator[](Node_index) const + { + return nullptr; + } +}; + +} // end of Orthtree_impl namespace /*! \ingroup PkgOrthtreeRef @@ -72,20 +115,7 @@ BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) */ template class Orthtree { - template - struct Node_data_selector {}; - - template - struct Node_data_selector { - using type = typename A::Node_data; - }; - - template - struct Node_data_selector { - using type = void; - }; - - static inline constexpr bool has_data = internal::has_Node_data::value; + static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; public: /// \name Template Types @@ -104,7 +134,7 @@ class Orthtree { using Adjacency = typename Traits::Adjacency; ///< Adjacency type. using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. - using Node_data = typename Node_data_selector::type; + using Node_data = typename Orthtree_impl::Node_data_wrapper::Node_data; /// @} @@ -178,7 +208,7 @@ class Orthtree { Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - std::conditional_t, void*> m_node_contents; + Orthtree_impl::Node_data_wrapper m_node_contents; Property_array m_node_depths; Property_array m_node_coordinates; Property_array> m_node_parents; @@ -190,14 +220,6 @@ class Orthtree { Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ - template> - auto init_node_contents(std::true_type) -> std::enable_if_t - { - return Property_array(m_node_properties.template get_or_add_property("contents").first); - } - - void* init_node_contents(std::false_type) { return nullptr; } - public: /// \name Constructor @@ -219,7 +241,7 @@ class Orthtree { */ explicit Orthtree(Traits traits) : m_traits(traits), - m_node_contents(init_node_contents(std::bool_constant())), + m_node_contents(m_node_properties), m_node_depths(m_node_properties.template get_or_add_property("depths", 0).first), m_node_coordinates(m_node_properties.template get_or_add_property("coordinates").first), m_node_parents(m_node_properties.template get_or_add_property>("parents").first), @@ -250,7 +272,7 @@ class Orthtree { Orthtree(const Orthtree& other) : m_traits(other.m_traits), m_node_properties(other.m_node_properties), - m_node_contents(init_node_contents(std::bool_constant())), + m_node_contents(m_node_properties), m_node_depths(m_node_properties.template get_property("depths")), m_node_coordinates(m_node_properties.template get_property("coordinates")), m_node_parents(m_node_properties.template get_property>("parents")), @@ -261,7 +283,7 @@ class Orthtree { Orthtree(Orthtree&& other) : m_traits(other.m_traits), m_node_properties(std::move(other.m_node_properties)), - m_node_contents(init_node_contents(std::bool_constant())), + m_node_contents(m_node_properties), m_node_depths(m_node_properties.template get_property("depths")), m_node_coordinates(m_node_properties.template get_property("coordinates")), m_node_parents(m_node_properties.template get_property>("parents")), @@ -723,18 +745,18 @@ class Orthtree { } /*! - \brief retrieves a reference to the Node_data associated with the node specified by `n`. + \brief retrieves a reference to the `Node_data` associated with the node specified by `n` if + `GeomTraits` is a model of `OrthtreeTraitswithData`, and `nullptr` otherwise. */ - template - auto data(Node_index n) -> std::enable_if_t{ + auto data(Node_index n){ return m_node_contents[n]; } /*! - \brief retrieves a const reference to the Node_data associated with the node specified by `n`. + \brief retrieves a const reference to the `Node_data` associated with the node specified by `n` if + `GeomTraits` is a model of `OrthtreeTraitswithData`, and `nullptr` otherwise. */ - template - auto data(Node_index n) const -> std::enable_if_t { + auto data(Node_index n) const{ return m_node_contents[n]; } From 1c6fdbd13db4f3a6a7352daba923813a47230c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 13 Feb 2024 17:39:00 +0100 Subject: [PATCH 412/520] undo some changes from b241bc8594ada289b1ed1b3f239dfb5bdc0cee31 --- Orthtree/include/CGAL/Orthtree.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index d2fa0b3e0df7..08a5dff1128e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -60,7 +60,7 @@ struct Node_data_wrapper { using Node_index = typename GT::Node_index; using Node_data = typename GT::Node_data; - Properties::Experimental::Property_array_handle m_node_contents; + typename CGAL::Properties::Experimental::Property_container::template Array& m_node_contents; template Node_data_wrapper(Property_container& node_properties) @@ -203,16 +203,16 @@ class Orthtree { using Node_property_container = Properties::Experimental::Property_container; template - using Property_array = typename Properties::Experimental::Property_array_handle; + using Property_array = typename Properties::Experimental::Property_container::template Array; Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; Orthtree_impl::Node_data_wrapper m_node_contents; - Property_array m_node_depths; - Property_array m_node_coordinates; - Property_array> m_node_parents; - Property_array> m_node_children; + Property_array& m_node_depths; + Property_array& m_node_coordinates; + Property_array>& m_node_parents; + Property_array>& m_node_children; using Bbox_dimensions = std::array; Bbox m_bbox; From 83d0f632e78f3e54c00bdf5f4597893452da8df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 13 Feb 2024 17:47:20 +0100 Subject: [PATCH 413/520] add missing assignment --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 08a5dff1128e..dbce58c51207 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -263,7 +263,7 @@ class Orthtree { m_side_per_depth.push_back(size); if constexpr (has_data) - m_traits.construct_root_node_contents_object()(); + data(root()) = m_traits.construct_root_node_contents_object()(); } /// @} From 446d39664fe3c5af16514d9f2f258d610e68311f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 13 Feb 2024 17:50:38 +0100 Subject: [PATCH 414/520] return data by ref --- Orthtree/include/CGAL/Orthtree.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index dbce58c51207..e1cac145205f 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -748,7 +748,7 @@ class Orthtree { \brief retrieves a reference to the `Node_data` associated with the node specified by `n` if `GeomTraits` is a model of `OrthtreeTraitswithData`, and `nullptr` otherwise. */ - auto data(Node_index n){ + std::conditional_t& data(Node_index n){ return m_node_contents[n]; } @@ -756,7 +756,7 @@ class Orthtree { \brief retrieves a const reference to the `Node_data` associated with the node specified by `n` if `GeomTraits` is a model of `OrthtreeTraitswithData`, and `nullptr` otherwise. */ - auto data(Node_index n) const{ + std::conditional_t data(Node_index n) const{ return m_node_contents[n]; } From c04b584ce6acef8af3e247a3f7eba445a14bd38b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 13 Feb 2024 13:15:15 +0100 Subject: [PATCH 415/520] added concept for orthtree without data --- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 8 +- .../Concepts/OrthtreeTraitsWithoutData.h | 76 +++++++++++++++++++ Orthtree/doc/Orthtree/PackageDescription.txt | 4 +- Orthtree/include/CGAL/Orthtree_traits_base.h | 48 ++++++------ Orthtree/include/CGAL/Orthtree_traits_point.h | 2 +- 5 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 43d54b8f8614..81cff58c7f36 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -3,12 +3,14 @@ \cgalConcept The concept `OrthtreeTraits` defines the requirements for the - template parameter of the `CGAL::Orthtree` class. + template parameter of the `CGAL::Orthtree` class for a node type that stores data. + + \cgalRefines{OrthtreeTraitsWithoutData} \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} - \cgalHasModels{CGAL::Orthtree_traits_base< K, dimension >} + \cgalHasModels{CGAL::Orthtree_traits_base} \cgalHasModelsEnd */ class OrthtreeTraits diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h new file mode 100644 index 000000000000..ab93b8db89f8 --- /dev/null +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h @@ -0,0 +1,76 @@ +/*! + \ingroup PkgOrthtreeConcepts + \cgalConcept + + The concept `OrthtreeTraitsWithoutData` defines the requirements for the + template parameter of the `CGAL::Orthtree` class. + + \cgalHasModelsBegin + \cgalHasModels{CGAL::Orthtree_traits_without_data} + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_face_graph} + \cgalHasModels{CGAL::Orthtree_traits_base} + \cgalHasModelsEnd +*/ +class OrthtreeTraitsWithoutData +{ +public: + + /// \name Types + /// @{ + using Node_index = unspecified_type; ///< An integer type for nodes + constexpr int dimension; ///< Dimension. + using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` + using Point_d = unspecified_type; ///< Point type. + using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` types. + + /*! + A random access iterator type to enumerate the + %Cartesian coordinates of a point of type `Point_d`. + */ + using Cartesian_const_iterator_d = unspecified_type; + + /*! + * \brief Integral number type which can take on values indicating adjacency directions. + * + * Must be able to take on values ranging from 0 to the number of faces of the (hyper)rectangle type, equivalent to 2 * D. + */ + using Adjacency = unspecified_type; ///< Specify the adjacency directions + + /*! + * \brief Functor with an operator to create the bounding box of the root node. + * + * Provides the operator: + * `Bbox_d operator()()` + * + * The bounding box must enclose all elements contained by the tree. + * It may be tight-fitting. The orthtree constructor produces a bounding box surrounding this region. + * For traits which assign no data to each node, this can be defined to return a fixed region. + */ + using Construct_root_node_bbox = unspecified_type; + + /*! + * \brief Functor with an operator to construct a `Point_d` from an initializer list. + * + * For trees which use a different kernel for the bounding box type, + * the return type of this functor must match the kernel used by the bounding box type and not that of the contents. + */ + using Construct_point_d = unspecified_type; + + /// @} + + /// \name Operations + /// @{ + + /*! + * constructs an object of type `Construct_root_node_bbox`. + */ + Construct_root_node_bbox construct_root_node_bbox_object() const; + + /*! + * constructs an object of type `Construct_point_d`. + */ + Construct_point_d construct_point_d_object() const; + + /// @} +}; diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index a451ff36b526..efb42fd47b54 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -40,6 +40,7 @@ Quadtree, Octree and Orthtree Reference \cgalClassifedRefPages \cgalCRPSection{Concepts} +- `OrthtreeTraitsWithoutData` - `OrthtreeTraits` - `OrthtreeTraversal` - `CollectionPartitioningOrthtreeTraits` @@ -50,7 +51,8 @@ Quadtree, Octree and Orthtree Reference - `CGAL::Orthtree` \cgalCRPSection{Traits} -- `CGAL::Orthtree_traits_point` +- `CGAL::Orthtree_traits_without_data` +- `CGAL::Orthtree_traits_point` - `CGAL::Orthtree_traits_base` - `CGAL::Orthtree_traits_face_graph` diff --git a/Orthtree/include/CGAL/Orthtree_traits_base.h b/Orthtree/include/CGAL/Orthtree_traits_base.h index a97f0f741016..6a55b4f6193d 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -27,25 +27,25 @@ namespace CGAL { The class `Orthtree_traits_base` is a base class providing common choices for types and functors. The base class is extended by `CGAL::Orthtree_traits_point` and by `CGAL::Orthtree_traits_face_graph`. - \tparam K a model of `Kernel`. + \tparam GeomTraits a model of `Kernel`. \tparam dim dimension of the ambient Euclidean space. \sa `CGAL::Orthtree_traits_point` \sa `CGAL::Orthtree_traits_face_graph` */ -template +template struct Orthtree_traits_base { /// \name Types /// @{ using Node_index = std::size_t; - using Kernel = K; + using Kernel = GeomTraits; static constexpr int dimension = dim; - using FT = typename K::FT; - using Point_d = typename K::Point_d; - using Bbox_d = typename K::Iso_box_d; - using Sphere_d = typename K::Sphere_d; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_d; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_d; + using Bbox_d = typename GeomTraits::Iso_box_d; + using Sphere_d = typename GeomTraits::Sphere_d; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_d; /*! * Adjacency type. * @@ -99,16 +99,16 @@ struct Orthtree_traits_base { } }; -template -struct Orthtree_traits_base { +template +struct Orthtree_traits_base { using Node_index = std::size_t; - using Kernel = K; + using Kernel = GeomTraits; static constexpr int dimension = 2; - using FT = typename K::FT; - using Point_d = typename K::Point_2; - using Bbox_d = typename K::Iso_rectangle_2; - using Sphere_d = typename K::Circle_2; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_2; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_2; + using Bbox_d = typename GeomTraits::Iso_rectangle_2; + using Sphere_d = typename GeomTraits::Circle_2; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_2; enum Adjacency { LEFT, @@ -124,16 +124,16 @@ struct Orthtree_traits_base { } }; -template -struct Orthtree_traits_base { +template +struct Orthtree_traits_base { using Node_index = std::size_t; - using Kernel = K; + using Kernel = GeomTraits; static constexpr int dimension = 3; - using FT = typename K::FT; - using Point_d = typename K::Point_3; - using Bbox_d = typename K::Iso_cuboid_3; - using Sphere_d = typename K::Sphere_3; - using Cartesian_const_iterator_d = typename K::Cartesian_const_iterator_3; + using FT = typename GeomTraits::FT; + using Point_d = typename GeomTraits::Point_3; + using Bbox_d = typename GeomTraits::Iso_cuboid_3; + using Sphere_d = typename GeomTraits::Sphere_3; + using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_3; enum Adjacency { LEFT, diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 3eca22fd8ec1..e992a03960c3 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -72,7 +72,7 @@ void reassign_points( \cgalModels{OrthtreeTraits} \sa `CGAL::Octree` \sa `CGAL::Quadtree` - \sa `CGAL::Orthtree_traits_base` + \sa `CGAL::Orthtree_traits_base` */ template < typename GeomTraits, From d3fdd5ec5340b0ccc040ff1110c91cd6a5ba4ba1 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 14 Feb 2024 11:21:47 +0100 Subject: [PATCH 416/520] update doc of nearest neighbors search --- .../include/CGAL/Orthtree/Nearest_neighbors.h | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 9766e62fe84d..3970a7235e23 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -32,26 +32,26 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Base case: the node has no children - // Loop through each of the points contained by the node + // Loop through each of the elements contained by the node // Note: there might be none, and that should be fine! - for (auto& p: orthtree.data(node)) { + for (auto& e: orthtree.data(node)) { - // Pair that point with its distance from the search point - Result current_point_with_distance = - {p, orthtree.traits().get_squared_distance_of_element_object()(p, search_bounds.center())}; + // Pair that element with its distance from the search point + Result current_element_with_distance = + {e, orthtree.traits().get_squared_distance_of_element_object()(e, search_bounds.center())}; - // Check if the new point is within the bounds - if (current_point_with_distance.distance < search_bounds.squared_radius()) { + // Check if the new element is within the bounds + if (current_element_with_distance.distance < search_bounds.squared_radius()) { // Check if the results list is full if (results.size() == results.capacity()) { - // Delete a point if we need to make room + // Delete a element if we need to make room results.pop_back(); } - // Add the new point - results.push_back(current_point_with_distance); + // Add the new element + results.push_back(current_element_with_distance); // Sort the list std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { @@ -117,21 +117,21 @@ namespace Orthtrees { /*! \ingroup PkgOrthtreeNeighbors - \brief finds at most `k` points within a specific radius that are + \brief finds at most `k` elements within a specific radius that are nearest to the center of the sphere `query`: if `query` does not contain - at least `k` points, only contained points will be returned. + at least `k` elements, only contained points will be returned. - This function is useful when the user already knows how sparse the points are, - or if they do not care about points that are too far away. + This function is useful when the user already knows how sparse the elements are, + or if they do not care about elements that are too far away. Setting a small radius may have performance benefits. \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` - \tparam OutputIterator must be a model of `OutputIterator` that accepts points + \tparam OutputIterator must be a model of `OutputIterator` that accepts `GT::Node_data_element` \param orthtree the tree to search within \param query the region to search within - \param k the number of points to find - \param output the output iterator to add the found points to (in order of increasing distance) + \param k the number of elements to find + \param output the output iterator to add the found elements to (in order of increasing distance) */ template OutputIterator nearest_k_neighbors_in_radius( @@ -143,21 +143,21 @@ OutputIterator nearest_k_neighbors_in_radius( // todo: this type is over-constrained, this must be made more generic struct Node_element_with_distance { - typename Tree::Traits::Node_data_element point; + typename Tree::Traits::Node_data_element element; typename Tree::FT distance; }; - // Create an empty list of points - std::vector points_list; + // Create an empty list of elements + std::vector element_list; if (k != (std::numeric_limits::max)()) - points_list.reserve(k); + element_list.reserve(k); - // Invoking the recursive function adds those points to the vector (passed by reference) - CGAL::internal::nearest_k_neighbors_recursive(orthtree, query, orthtree.root(), points_list); + // Invoking the recursive function adds those elements to the vector (passed by reference) + CGAL::internal::nearest_k_neighbors_recursive(orthtree, query, orthtree.root(), element_list); // Add all the points found to the output - for (auto& item: points_list) - *output++ = item.point; + for (auto& item: element_list) + *output++ = item.element; return output; } @@ -171,7 +171,7 @@ OutputIterator nearest_k_neighbors_in_radius( `query`. \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` - \tparam OutputIterator a model of `OutputIterator` that accepts `Point_d` objects. + \tparam OutputIterator a model of `OutputIterator` that accepts `GT::Node_data_element` objects. \param orthtree the tree to search within \param query query point @@ -188,13 +188,13 @@ OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Poin /*! \ingroup PkgOrthtreeNeighbors - \brief finds the points in the sphere `query`. + \brief finds the elements in the sphere `query`. - Points are outputted in order of increasing distance to + Elements are outputted in order of increasing distance to the center of the sphere. \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` - \tparam OutputIterator a model of `OutputIterator` that accepts `Point_d` objects. + \tparam OutputIterator a model of `OutputIterator` that accepts `GT::Node_data_element` objects. \param orthtree the tree to search within \param query query sphere From d5a92a422166b70f65cd9f600c9d1fab03abd8bb Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 14 Feb 2024 14:52:25 +0100 Subject: [PATCH 417/520] renaming OrthtreeTraitsWithoutData and OrthtreeTraits doc + some cleaning up --- .../CollectionPartitioningOrthtreeTraits.h | 4 +- .../doc/Orthtree/Concepts/OrthtreeTraits.h | 53 +------------ .../Concepts/OrthtreeTraitsWithData.h | 75 ++++++++++++++++++ .../Concepts/OrthtreeTraitsWithoutData.h | 76 ------------------- Orthtree/doc/Orthtree/PackageDescription.txt | 2 +- Orthtree/include/CGAL/Orthtree.h | 7 +- .../include/CGAL/Orthtree/Split_predicates.h | 8 +- .../include/CGAL/Orthtree_traits_face_graph.h | 2 +- Orthtree/include/CGAL/Orthtree_traits_point.h | 2 +- 9 files changed, 91 insertions(+), 138 deletions(-) create mode 100644 Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h delete mode 100644 Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 7164951788d0..523408e63e8b 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -2,14 +2,14 @@ \ingroup PkgOrthtreeConcepts \cgalConcept - Refinement of the OrthtreeTraits concept, adding requirements for the + Refinement of the OrthtreeTraitsWithData concept, adding requirements for the traits class of a `CGAL::Orthtree` in order to supports nearest-neighbor searching. Nearest neighbor searches expect a tree where `Node_data` is a model of `ForwardRange`. The leaf nodes of the tree represent an exclusive partition of the elements contained in the tree. This means that no element should be contained by more than one node. - \cgalRefines{OrthtreeTraits} + \cgalRefines{OrthtreeTraitsWithData} \cgalHasModelsBegin \cgalHasModels{CGAL::Orthtree_traits_point} diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 81cff58c7f36..ee889f0776f7 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -3,11 +3,10 @@ \cgalConcept The concept `OrthtreeTraits` defines the requirements for the - template parameter of the `CGAL::Orthtree` class for a node type that stores data. - - \cgalRefines{OrthtreeTraitsWithoutData} + template parameter of the `CGAL::Orthtree` class. \cgalHasModelsBegin + \cgalHasModels{CGAL::Orthtree_traits_with_data} \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} \cgalHasModels{CGAL::Orthtree_traits_base} @@ -31,11 +30,6 @@ class OrthtreeTraits */ using Cartesian_const_iterator_d = unspecified_type; - /*! - * \brief The data type contained by each node. - */ - using Node_data = unspecified_type; - /*! * \brief Integral number type which can take on values indicating adjacency directions. * @@ -56,39 +50,10 @@ class OrthtreeTraits using Construct_root_node_bbox = unspecified_type; /*! - * \brief Functor which initializes the contained elements for the root node. - * - * Each node of a tree has an associated `Node_data` value. - * For most nodes, this is set by `Distribute_node_contents`, but that is not possible for the root node. - * Instead, this functor initializes the `Node_data` of the root node. - * It takes no arguments, and returns an instance of `Node_data`. - * - * Provides the operator: - * `Node_data operator()()` - * - * Typically, the `Node_data` of the root node contains all the elements in the tree. - * For a tree in which each node contains a span (such as `std::span()`) this function would return the span containing all items. - * - */ - using Construct_root_node_contents = unspecified_type; - - /*! - * \brief functor which fills the contents of the nodes children. + * \brief Functor with an operator to construct a `Point_d` from an initializer list of type `FT`. * * Provides the operator: - * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` - * - * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` - * to access its children and the contents of the node. - * It must distribute the contents of the node to each of its children. - * For a tree in which each node contains a span, this may mean rearranging the contents of the original node - * and producing spans containing a subset of its contents for each of its children. - * For compatibility with locate, the center of the node is considered to be part of the upper half. - */ - using Distribute_node_contents = unspecified_type; - - /*! - * \brief Functor with an operator to construct a `Point_d` from an initializer list. + * `Point_d operator()(arg1, arg2,...)` * * For trees which use a different kernel for the bounding box type, * the return type of this functor must match the kernel used by the bounding box type and not that of the contents. @@ -105,16 +70,6 @@ class OrthtreeTraits */ Construct_root_node_bbox construct_root_node_bbox_object() const; - /*! - * constructs an object of type `Construct_root_node_contents`. - */ - Construct_root_node_contents construct_root_node_contents_object() const; - - /*! - * constructs an object of type `Distribute_node_contents`. - */ - Distribute_node_contents distribute_node_contents_object() const; - /*! * constructs an object of type `Construct_point_d`. */ diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h new file mode 100644 index 000000000000..c67a1af8dc89 --- /dev/null +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -0,0 +1,75 @@ +/*! + \ingroup PkgOrthtreeConcepts + \cgalConcept + + The concept `OrthtreeTraitsWithData` defines the requirements for the + template parameter of the `CGAL::Orthtree` class for a node type that stores data. + + \cgalRefines{OrthtreeTraits} + + \cgalHasModelsBegin + \cgalHasModels{CGAL::Orthtree_traits_point} + \cgalHasModels{CGAL::Orthtree_traits_face_graph} + \cgalHasModels{CGAL::Orthtree_traits_base} + \cgalHasModelsEnd +*/ +class OrthtreeTraitsWithData +{ +public: + + /// \name Types + /// @{ + /*! + * \brief The data type contained by each node. + */ + using Node_data = unspecified_type; + + /*! + * \brief Functor which initializes the contained elements for the root node. + * + * Each node of a tree has an associated `Node_data` value. + * For most nodes, this is set by `Distribute_node_contents`, but that is not possible for the root node. + * Instead, this functor initializes the `Node_data` of the root node. + * It takes no arguments, and returns an instance of `Node_data`. + * + * Provides the operator: + * `Node_data operator()()` + * + * Typically, the `Node_data` of the root node contains all the elements in the tree. + * For a tree in which each node contains a span (such as `std::span()`) this function would return the span containing all items. + * + */ + using Construct_root_node_contents = unspecified_type; + + /*! + * \brief functor which fills the contents of the nodes children. + * + * Provides the operator: + * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` + * + * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` + * to access its children and the contents of the node. + * It must distribute the contents of the node to each of its children. + * For a tree in which each node contains a span, this may mean rearranging the contents of the original node + * and producing spans containing a subset of its contents for each of its children. + * For compatibility with locate, the center of the node is considered to be part of the upper half. + */ + using Distribute_node_contents = unspecified_type; + + /// @} + + /// \name Operations + /// @{ + + /*! + * constructs an object of type `Construct_root_node_contents`. + */ + Construct_root_node_contents construct_root_node_contents_object() const; + + /*! + * constructs an object of type `Distribute_node_contents`. + */ + Distribute_node_contents distribute_node_contents_object() const; + + /// @} +}; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h deleted file mode 100644 index ab93b8db89f8..000000000000 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithoutData.h +++ /dev/null @@ -1,76 +0,0 @@ -/*! - \ingroup PkgOrthtreeConcepts - \cgalConcept - - The concept `OrthtreeTraitsWithoutData` defines the requirements for the - template parameter of the `CGAL::Orthtree` class. - - \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_without_data} - \cgalHasModels{CGAL::Orthtree_traits_point} - \cgalHasModels{CGAL::Orthtree_traits_face_graph} - \cgalHasModels{CGAL::Orthtree_traits_base} - \cgalHasModelsEnd -*/ -class OrthtreeTraitsWithoutData -{ -public: - - /// \name Types - /// @{ - using Node_index = unspecified_type; ///< An integer type for nodes - constexpr int dimension; ///< Dimension. - using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` - using Point_d = unspecified_type; ///< Point type. - using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` types. - - /*! - A random access iterator type to enumerate the - %Cartesian coordinates of a point of type `Point_d`. - */ - using Cartesian_const_iterator_d = unspecified_type; - - /*! - * \brief Integral number type which can take on values indicating adjacency directions. - * - * Must be able to take on values ranging from 0 to the number of faces of the (hyper)rectangle type, equivalent to 2 * D. - */ - using Adjacency = unspecified_type; ///< Specify the adjacency directions - - /*! - * \brief Functor with an operator to create the bounding box of the root node. - * - * Provides the operator: - * `Bbox_d operator()()` - * - * The bounding box must enclose all elements contained by the tree. - * It may be tight-fitting. The orthtree constructor produces a bounding box surrounding this region. - * For traits which assign no data to each node, this can be defined to return a fixed region. - */ - using Construct_root_node_bbox = unspecified_type; - - /*! - * \brief Functor with an operator to construct a `Point_d` from an initializer list. - * - * For trees which use a different kernel for the bounding box type, - * the return type of this functor must match the kernel used by the bounding box type and not that of the contents. - */ - using Construct_point_d = unspecified_type; - - /// @} - - /// \name Operations - /// @{ - - /*! - * constructs an object of type `Construct_root_node_bbox`. - */ - Construct_root_node_bbox construct_root_node_bbox_object() const; - - /*! - * constructs an object of type `Construct_point_d`. - */ - Construct_point_d construct_point_d_object() const; - - /// @} -}; diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index efb42fd47b54..66cdb6aea705 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -40,8 +40,8 @@ Quadtree, Octree and Orthtree Reference \cgalClassifedRefPages \cgalCRPSection{Concepts} -- `OrthtreeTraitsWithoutData` - `OrthtreeTraits` +- `OrthtreeTraitsWithData` - `OrthtreeTraversal` - `CollectionPartitioningOrthtreeTraits` diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e1cac145205f..2f8ffeb814bc 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -176,7 +175,7 @@ class Orthtree { using Split_predicate = std::function; /*! - * \brief A model of `ConstRange` whose value type is `Node_index` and its iterator type is `ForwardIterator`. + * \brief A model of `ForwardRange` whose value type is `Node_index`. */ #ifdef DOXYGEN_RUNNING using Node_index_range = unspecified_type; @@ -463,7 +462,7 @@ class Orthtree { \param traversal class defining the traversal strategy - \return a forward input iterator over the node indices of the tree + \return a `ForwardRange` over the node indices of the tree */ template Node_index_range traverse(Traversal traversal) const { @@ -486,7 +485,7 @@ class Orthtree { \param args Arguments to to pass to the traversal's constructor, excluding the first (always an orthtree reference) - \return a forward input iterator over the node indices of the tree + \return a `ForwardRange` over the node indices of the tree */ template Node_index_range traverse(Args&& ...args) const { diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index cf3ad5221486..f7bf2bdd6c08 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -30,8 +30,8 @@ namespace Orthtrees { This is a bucket size predicate that considers a node should be split if it contains more than a certain number of items. - \warning This split predicate is only appropriate for trees with traits classes where - `Node_data` is present and a model of `Range`. `RandomAccessRange` is suggested for performance. + \warning This split predicate is only appropriate for trees with traits classes which are models of `OrthtreeTraitsWithData` + and where `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ class Maximum_number_of_inliers { @@ -95,8 +95,8 @@ class Maximum_depth { at a depth smaller than `max_depth` but already has fewer inliers than `bucket_size`, it is not split. - \warning This split predicate is only appropriate for trees with traits classes where - `Node_data` is present and a model of `Range`. `RandomAccessRange` is suggested for performance. + \warning This split predicate is only appropriate for trees with traits classes which are models of `OrthtreeTraitsWithData` + and where `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ class Maximum_depth_and_maximum_number_of_inliers { diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 302368da55d7..3562a97b10b2 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -35,7 +35,7 @@ to which the minimal extend of a node should be provided. \tparam TriangleMesh a model of `FaceListGraph` with all faces being triangles \tparam VertexPointMap a property map associating points to the vertices of `TriangleMesh` -\cgalModels{OrthtreeTraits} +\cgalModels{OrthtreeTraitsWithData} */ template struct Orthtree_traits_face_graph : public Orthtree_traits_base< diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index e992a03960c3..68aa20bf9a1c 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -69,7 +69,7 @@ void reassign_points( and is rearranged by the `Orthtree`. Altering the point range after creating the orthtree will leave it in an invalid state. - \cgalModels{OrthtreeTraits} + \cgalModels{CollectionPartitioningOrthtreeTraits} \sa `CGAL::Octree` \sa `CGAL::Quadtree` \sa `CGAL::Orthtree_traits_base` From 20e3cb84624b53608a796a5da7da54aaf30d52db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 15 Feb 2024 14:29:45 +0100 Subject: [PATCH 418/520] remove no longer needed boost headers --- .../CGAL/STL_Extension/internal/boost/data.h | 51 --- STL_Extension/include/CGAL/span.h | 405 ------------------ 2 files changed, 456 deletions(-) delete mode 100644 STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h delete mode 100644 STL_Extension/include/CGAL/span.h diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h b/STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h deleted file mode 100644 index 5dfabf05fe2e..000000000000 --- a/STL_Extension/include/CGAL/STL_Extension/internal/boost/data.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2023 Glen Joseph Fernandes -// (glenjofe@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. -// (http://www.boost.org/LICENSE_1_0.txt) -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: BSL-1.0 -// -// NOTE: This file was taken from boost 1.83 for use by span.h. - -#ifndef BOOST_CORE_DATA_HPP -#define BOOST_CORE_DATA_HPP - -#include -#include - -namespace boost { - -template -inline constexpr auto -data(C& c) noexcept(noexcept(c.data())) -> decltype(c.data()) -{ - return c.data(); -} - -template -inline constexpr auto -data(const C& c) noexcept(noexcept(c.data())) -> decltype(c.data()) -{ - return c.data(); -} - -template -inline constexpr T* -data(T(&a)[N]) noexcept -{ - return a; -} - -template -inline constexpr const T* -data(std::initializer_list l) noexcept -{ - return l.begin(); -} - -} /* boost */ - -#endif \ No newline at end of file diff --git a/STL_Extension/include/CGAL/span.h b/STL_Extension/include/CGAL/span.h deleted file mode 100644 index 5f4544667bfa..000000000000 --- a/STL_Extension/include/CGAL/span.h +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright 2019-2023 Glen Joseph Fernandes -// (glenjofe@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. -// (http://www.boost.org/LICENSE_1_0.txt) -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: BSL-1.0 -// -// NOTE: This file was taken from boost 1.83 for use in the Orthtree package. - -#ifndef BOOST_CORE_SPAN_HPP -#define BOOST_CORE_SPAN_HPP - -#include - -#include -#include -#include - -namespace boost { - -constexpr std::size_t dynamic_extent = static_cast(-1); - -template -class span; - -namespace detail { - -template -struct span_convertible { - static constexpr bool value = std::is_convertible::value; -}; - -template -struct span_capacity { - static constexpr bool value = E == boost::dynamic_extent || E == N; -}; - -template -struct span_compatible { - static constexpr bool value = span_capacity::value && - span_convertible::value; -}; - -template -using span_uncvref = typename std::remove_cv::type>::type; - -template -struct span_is_span { - static constexpr bool value = false; -}; - -template -struct span_is_span > { - static constexpr bool value = true; -}; - -template -struct span_is_array { - static constexpr bool value = false; -}; - -template -struct span_is_array > { - static constexpr bool value = true; -}; - -template -using span_ptr = decltype(boost::data(std::declval())); - -template -struct span_data { }; - -template -struct span_data >::value>::type> { - typedef typename std::remove_pointer >::type type; -}; - -template -struct span_has_data { - static constexpr bool value = false; -}; - -template -struct span_has_data::type, T>::value>::type> { - static constexpr bool value = true; -}; - -template -struct span_has_size { - static constexpr bool value = false; -}; - -template -struct span_has_size().size()), - std::size_t>::value>::type> { - static constexpr bool value = true; -}; - -template -struct span_is_range { - static constexpr bool value = (std::is_const::value || - std::is_lvalue_reference::value) && - !span_is_span >::value && - !span_is_array >::value && - !std::is_array >::value && - span_has_data::value && - span_has_size::value; -}; - -template -struct span_implicit { - static constexpr bool value = E == boost::dynamic_extent || - N != boost::dynamic_extent; -}; - -template -struct span_copyable { - static constexpr bool value = (N == boost::dynamic_extent || - span_capacity::value) && span_convertible::value; -}; - -template -struct span_sub { - static constexpr std::size_t value = E == boost::dynamic_extent ? - boost::dynamic_extent : E - O; -}; - -template -struct span_store { - constexpr span_store(T* p_, std::size_t) noexcept - : p(p_) { } - static constexpr std::size_t n = E; - T* p; -}; - -template -struct span_store { - constexpr span_store(T* p_, std::size_t n_) noexcept - : p(p_) - , n(n_) { } - T* p; - std::size_t n; -}; - -template -struct span_bytes { - static constexpr std::size_t value = sizeof(T) * E; -}; - -template -struct span_bytes { - static constexpr std::size_t value = boost::dynamic_extent; -}; - -} /* detail */ - -template -class span { -public: - typedef T element_type; - typedef typename std::remove_cv::type value_type; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - typedef T* pointer; - typedef const T* const_pointer; - typedef T& reference; - typedef const T& const_reference; - typedef T* iterator; - typedef const T* const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - static constexpr std::size_t extent = E; - - template::type = 0> - constexpr span() noexcept - : s_(0, 0) { } - - template::value, int>::type = 0> - constexpr span(I* f, size_type c) - : s_(f, c) { } - - template::value, int>::type = 0> - explicit constexpr span(I* f, size_type c) - : s_(f, c) { } - - template::value, int>::type = 0> - constexpr span(I* f, L* l) - : s_(f, l - f) { } - - template::value, int>::type = 0> - explicit constexpr span(I* f, L* l) - : s_(f, l - f) { } - - template::value, - int>::type = 0> - constexpr span(typename std::enable_if::type (&a)[N]) noexcept - : s_(a, N) { } - - template::value, - int>::type = 0> - constexpr span(std::array& a) noexcept - : s_(a.data(), N) { } - - template::value, int>::type = 0> - constexpr span(const std::array& a) noexcept - : s_(a.data(), N) { } - - template::value, int>::type = 0> - constexpr span(R&& r) noexcept(noexcept(boost::data(r)) && - noexcept(r.size())) - : s_(boost::data(r), r.size()) { } - - template::value, int>::type = 0> - explicit constexpr span(R&& r) noexcept(noexcept(boost::data(r)) && - noexcept(r.size())) - : s_(boost::data(r), r.size()) { } - - template::value && - detail::span_copyable::value, int>::type = 0> - constexpr span(const span& s) noexcept - : s_(s.data(), s.size()) { } - - template::value && - detail::span_copyable::value, int>::type = 0> - explicit constexpr span(const span& s) noexcept - : s_(s.data(), s.size()) { } - - template - constexpr span first() const { - static_assert(C <= E, "Count <= Extent"); - return span(s_.p, C); - } - - template - constexpr span last() const { - static_assert(C <= E, "Count <= Extent"); - return span(s_.p + (s_.n - C), C); - } - - template - constexpr typename std::enable_if::value> >::type subspan() const { - static_assert(O <= E, "Offset <= Extent"); - return span::value>(s_.p + O, s_.n - O); - } - - template - constexpr typename std::enable_if >::type subspan() const { - static_assert(O <= E && C <= E - O, - "Offset <= Extent && Count <= Extent - Offset"); - return span(s_.p + O, C); - } - - constexpr span first(size_type c) const { - return span(s_.p, c); - } - - constexpr span last(size_type c) const { - return span(s_.p + (s_.n - c), c); - } - - constexpr span subspan(size_type o, - size_type c = dynamic_extent) const { - return span(s_.p + o, - c == dynamic_extent ? s_.n - o : c); - } - - constexpr size_type size() const noexcept { - return s_.n; - } - - constexpr size_type size_bytes() const noexcept { - return s_.n * sizeof(T); - } - - constexpr bool empty() const noexcept { - return s_.n == 0; - } - - constexpr reference operator[](size_type i) const { - return s_.p[i]; - } - - constexpr reference front() const { - return *s_.p; - } - - constexpr reference back() const { - return s_.p[s_.n - 1]; - } - - constexpr pointer data() const noexcept { - return s_.p; - } - - constexpr iterator begin() const noexcept { - return s_.p; - } - - constexpr iterator end() const noexcept { - return s_.p + s_.n; - } - - constexpr reverse_iterator rbegin() const noexcept { - return reverse_iterator(s_.p + s_.n); - } - - constexpr reverse_iterator rend() const noexcept { - return reverse_iterator(s_.p); - } - - constexpr const_iterator cbegin() const noexcept { - return s_.p; - } - - constexpr const_iterator cend() const noexcept { - return s_.p + s_.n; - } - - constexpr const_reverse_iterator crbegin() const noexcept { - return const_reverse_iterator(s_.p + s_.n); - } - - constexpr const_reverse_iterator crend() const noexcept { - return const_reverse_iterator(s_.p); - } - -private: - detail::span_store s_; -}; - -template -constexpr std::size_t span::extent; - -#ifdef __cpp_deduction_guides -template -span(I*, L) -> span; - -template -span(T(&)[N]) -> span; - -template -span(std::array&) -> span; - -template -span(const std::array&) -> span; - -template -span(R&&) -> span::type>; - -template -span(span) -> span; -#endif - -#ifdef __cpp_lib_byte -template -inline span::value> -as_bytes(span s) noexcept -{ - return span::value>(reinterpret_cast(s.data()), - s.size_bytes()); -} - -template -inline typename std::enable_if::value, - span::value> >::type -as_writable_bytes(span s) noexcept -{ - return span::value>(reinterpret_cast(s.data()), s.size_bytes()); -} -#endif - -} /* boost */ - -#endif \ No newline at end of file From 7aa4f37b6ba35f99cf9594744718e6fb529f1a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 16 Feb 2024 11:39:09 +0100 Subject: [PATCH 419/520] rename macro --- .../CGAL/Polygon_mesh_processing/autorefinement.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index 94b6a9c96677..e7108e6d521f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -55,7 +55,7 @@ #endif #endif -#ifdef USE_FIXED_PROJECTION_TRAITS +#ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS #include #endif @@ -544,7 +544,7 @@ struct Triangle_data }; template @@ -820,7 +820,7 @@ void generate_subtriangles(std::size_t ti, // init CDT + insert points and constraints CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer3.start();) -#ifdef USE_FIXED_PROJECTION_TRAITS +#ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS typedef ::CGAL::internal::Projection_traits_3 P_traits; #else typedef CGAL::Projection_traits_3 P_traits; @@ -835,7 +835,7 @@ void generate_subtriangles(std::size_t ti, const std::array& t = triangles[ti]; std::vector vhandles(triangle_data.points.size()); -#ifdef USE_FIXED_PROJECTION_TRAITS +#ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS P_traits cdt_traits; bool orientation_flipped = false; CDT cdt(cdt_traits); @@ -1354,7 +1354,7 @@ void autorefine_triangle_soup(PointRange& soup_points, new_triangles.push_back({triangles[ti], ti}); else { -#ifdef USE_FIXED_PROJECTION_TRAITS +#ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS const std::array& t = triangles[ti]; typename EK::Vector_3 orth = CGAL::normal(t[0], t[1], t[2]); // TODO::avoid construction? int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1; From 5cae5340e99570b8eb16ac2e6a5cc0b942e59365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 16 Feb 2024 11:48:15 +0100 Subject: [PATCH 420/520] remove TODO that are not --- .../include/CGAL/Polygon_mesh_processing/autorefinement.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index e7108e6d521f..37d9a31f4f13 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -486,7 +486,6 @@ bool collect_intersections(const std::array& t1, return false; } -//TODO: rename struct struct Triangle_data { using Point_3 = Exact_predicates_exact_constructions_kernel::Point_3; @@ -950,7 +949,6 @@ void generate_subtriangles(std::size_t ti, for (const std::pair& ids : triangle_data.segments) { - //TODO remove me CGAL_assertion(ids.first < vhandles.size()); CGAL_assertion(ids.second < vhandles.size()); CGAL_assertion( vhandles[ids.first]!= typename CDT::Vertex_handle() ); From a57800ce07afdfe18429c4922460812b628f38eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 16 Feb 2024 12:12:19 +0100 Subject: [PATCH 421/520] get rid of extra container --- .../Polygon_mesh_processing/autorefinement.h | 90 +++++++++---------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index 37d9a31f4f13..ff9a8ee4d8a3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -255,12 +255,12 @@ do_coplanar_segments_intersect(std::size_t pi, std::size_t qi, // imported from Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h template -void coplanar_intersections(const std::array& t1, - const std::array& t2, +void coplanar_intersections(const std::vector& t1, + const std::vector& t2, std::vector>& inter_pts) { - const typename K::Point_3& p1 = t1[0], q1 = t1[1], r1 = t1[2]; - const typename K::Point_3& p2 = t2[0], q2 = t2[1], r2 = t2[2]; + const typename K::Point_3& p1 = t1[0], & q1 = t1[1], & r1 = t1[2]; + const typename K::Point_3& p2 = t2[0], & q2 = t2[1], & r2 = t2[2]; std::list> l_inter_pts; l_inter_pts.push_back(Intersections::internal::Point_on_triangle(0,-1)); @@ -416,8 +416,8 @@ test_edge(const typename K::Point_3& p, const typename K::Point_3& q, } template -bool collect_intersections(const std::array& t1, - const std::array& t2, +bool collect_intersections(const std::vector& t1, + const std::vector& t2, std::vector>& inter_pts) { // test edges of t1 vs t2 @@ -548,10 +548,9 @@ template void generate_subtriangles(std::size_t ti, - Triangle_data& triangle_data, + std::vector& all_triangle_data, const std::set >& intersecting_triangles, const std::set >& coplanar_triangles, - const std::vector>& triangles, PointVector& new_triangles ) { @@ -588,6 +587,7 @@ void generate_subtriangles(std::size_t ti, #define CGAL_AUTOREF_COUNTER_INSTRUCTION(X) #endif + Triangle_data& triangle_data=all_triangle_data[ti]; std::vector& points=triangle_data.points; std::vector& point_locations=triangle_data.point_locations; std::vector>& segments=triangle_data.segments; @@ -680,9 +680,9 @@ void generate_subtriangles(std::size_t ti, { CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer6.start();) typename EK::Point_3 pt = typename EK::Construct_planes_intersection_point_3()( - triangles[in_triangle_ids[i]][0], triangles[in_triangle_ids[i]][1],triangles[in_triangle_ids[i]][2], - triangles[in_triangle_ids[j]][0], triangles[in_triangle_ids[j]][1],triangles[in_triangle_ids[j]][2], - triangles[ti][0], triangles[ti][1],triangles[ti][2]); + all_triangle_data[in_triangle_ids[i]].points[0], all_triangle_data[in_triangle_ids[i]].points[1],all_triangle_data[in_triangle_ids[i]].points[2], + all_triangle_data[in_triangle_ids[j]].points[0], all_triangle_data[in_triangle_ids[j]].points[1],all_triangle_data[in_triangle_ids[j]].points[2], + points[0], points[1],points[2]); CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer6.stop();) CGAL_AUTOREF_COUNTER_INSTRUCTION(++counter.c1;) @@ -831,7 +831,6 @@ void generate_subtriangles(std::size_t ti, //typedef CGAL::Constrained_triangulation_plus_2 CDT; typedef CDT_2 CDT; - const std::array& t = triangles[ti]; std::vector vhandles(triangle_data.points.size()); #ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS @@ -839,12 +838,12 @@ void generate_subtriangles(std::size_t ti, bool orientation_flipped = false; CDT cdt(cdt_traits); // TODO: still need to figure out why I can't make the orientation_flipped correctly - vhandles[0]=cdt.insert(t[0]); - vhandles[1]=cdt.insert(t[1]); - vhandles[2]=cdt.insert(t[2]); + vhandles[0]=cdt.insert(points[0]); + vhandles[1]=cdt.insert(points[1]); + vhandles[2]=cdt.insert(points[2]); #else // positive triangle normal - typename EK::Vector_3 n = normal(t[0], t[1], t[2]); + typename EK::Vector_3 n = normal(points[0], points[1], points[2]); typename EK::Point_3 o(CGAL::ORIGIN); bool orientation_flipped = false; @@ -856,18 +855,14 @@ void generate_subtriangles(std::size_t ti, P_traits cdt_traits(n); CDT cdt(cdt_traits); - vhandles[0]=cdt.insert_outside_affine_hull(t[0]); - vhandles[1]=cdt.insert_outside_affine_hull(t[1]); + vhandles[0]=cdt.insert_outside_affine_hull(points[0]); + vhandles[1]=cdt.insert_outside_affine_hull(points[1]); vhandles[2] = cdt.tds().insert_dim_up(cdt.infinite_vertex(), orientation_flipped); - vhandles[2]->set_point(t[2]); + vhandles[2]->set_point(points[2]); #endif // insert points and fill vhandles -#if 0 - std::vector indices(triangle_data.points.size()-3); - std::iota(indices.begin(), indices.end(), 3); -#else - //start by points on edges + // start by points on edges std::array, 3> indices_on_edges; std::vector indices; for (std::size_t i=3; i::type Pmap; typedef Spatial_sort_traits_adapter_2 Search_traits; @@ -1107,22 +1102,17 @@ void autorefine_triangle_soup(PointRange& soup_points, // init the vector of triangles used for the autorefinement of triangles typedef CGAL::Exact_predicates_exact_constructions_kernel EK; - std::vector< std::array > triangles(tiid+1); // TODO get rid of triangles and use all_triangle_data // vector of data for refining triangles - std::vector all_triangle_data(triangles.size()); + std::vector all_triangle_data(tiid+1); Cartesian_converter to_exact; for(Input_TID f : intersected_faces) { std::size_t tid=tri_inter_ids[f]; - triangles[tid]= CGAL::make_array( - to_exact( get(pm, soup_points[soup_triangles[f][0]]) ), - to_exact( get(pm, soup_points[soup_triangles[f][1]]) ), - to_exact( get(pm, soup_points[soup_triangles[f][2]]) ) ); all_triangle_data[tid].points.resize(3); - all_triangle_data[tid].points[0]=triangles[tri_inter_ids[f]][0]; - all_triangle_data[tid].points[1]=triangles[tri_inter_ids[f]][1]; - all_triangle_data[tid].points[2]=triangles[tri_inter_ids[f]][2]; + all_triangle_data[tid].points[0]=to_exact( get(pm, soup_points[soup_triangles[f][0]]) ); + all_triangle_data[tid].points[1]=to_exact( get(pm, soup_points[soup_triangles[f][1]]) ); + all_triangle_data[tid].points[2]=to_exact( get(pm, soup_points[soup_triangles[f][2]]) ); all_triangle_data[tid].point_locations.resize(3); all_triangle_data[tid].point_locations[0]=-1; all_triangle_data[tid].point_locations[1]=-2; @@ -1144,8 +1134,8 @@ void autorefine_triangle_soup(PointRange& soup_points, if (i1==-1 || i2==-1) continue; //skip degenerate faces - const std::array& t1 = triangles[i1]; - const std::array& t2 = triangles[i2]; + const std::vector& t1 = all_triangle_data[i1].points; + const std::vector& t2 = all_triangle_data[i2].points; std::vector> inter_pts; bool triangles_are_coplanar = autorefine_impl::collect_intersections(t1, t2, inter_pts); @@ -1313,7 +1303,7 @@ void autorefine_triangle_soup(PointRange& soup_points, #ifdef CGAL_LINKED_WITH_TBB if (parallel_execution) { - tbb::parallel_for(tbb::blocked_range(0, triangles.size()), + tbb::parallel_for(tbb::blocked_range(0, all_triangle_data.size()), [&](const tbb::blocked_range& r) { for (size_t ti = r.begin(); ti != r.end(); ++ti) deduplicate_inserted_segments(ti); @@ -1322,7 +1312,7 @@ void autorefine_triangle_soup(PointRange& soup_points, } else #endif - for (std::size_t ti = 0; ti < triangles.size(); ++ti) { + for (std::size_t ti = 0; ti < all_triangle_data.size(); ++ti) { deduplicate_inserted_segments(ti); } @@ -1343,32 +1333,34 @@ void autorefine_triangle_soup(PointRange& soup_points, #endif #ifdef CGAL_AUTOREF_USE_PROGRESS_DISPLAY - boost::timer::progress_display pd(triangles.size()); + boost::timer::progress_display pd(all_triangle_data.size()); #endif auto refine_triangles = [&](std::size_t ti) { if (all_triangle_data[ti].points.size()==3) - new_triangles.push_back({triangles[ti], ti}); + new_triangles.push_back({CGAL::make_array(all_triangle_data[ti].points[0], + all_triangle_data[ti].points[1], + all_triangle_data[ti].points[2]), + ti}); else { #ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS - const std::array& t = triangles[ti]; - typename EK::Vector_3 orth = CGAL::normal(t[0], t[1], t[2]); // TODO::avoid construction? + typename EK::Vector_3 orth = CGAL::normal(all_triangle_data[ti].points[0], all_triangle_data[ti].points[1], all_triangle_data[ti].points[2]); // TODO::avoid construction? int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1; c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c; if (c == 0) { - autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); } else if (c == 1) { - autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); } else if (c == 2) { - autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); } #else - autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); #endif } @@ -1383,7 +1375,7 @@ void autorefine_triangle_soup(PointRange& soup_points, #ifdef CGAL_LINKED_WITH_TBB if (parallel_execution) { - tbb::parallel_for(tbb::blocked_range(0, triangles.size()), + tbb::parallel_for(tbb::blocked_range(0, all_triangle_data.size()), [&](const tbb::blocked_range& r) { for (size_t ti = r.begin(); ti != r.end(); ++ti) refine_triangles(ti); @@ -1392,7 +1384,7 @@ void autorefine_triangle_soup(PointRange& soup_points, } else #endif - for (std::size_t ti = 0; ti < triangles.size(); ++ti) { + for (std::size_t ti = 0; ti < all_triangle_data.size(); ++ti) { refine_triangles(ti); } @@ -1449,7 +1441,7 @@ void autorefine_triangle_soup(PointRange& soup_points, } // raw copy of input triangles with no intersection - std::vector tri_inter_ids_inverse(triangles.size()); + std::vector tri_inter_ids_inverse(all_triangle_data.size()); for (Input_TID f=0; f Date: Mon, 19 Feb 2024 12:10:59 +0100 Subject: [PATCH 422/520] small doc update --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h | 2 +- Orthtree/include/CGAL/Orthtree.h | 2 +- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h index c67a1af8dc89..e3e5f5e6124a 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -42,7 +42,7 @@ class OrthtreeTraitsWithData using Construct_root_node_contents = unspecified_type; /*! - * \brief functor which fills the contents of the nodes children. + * \brief Functor which fills the contents of the nodes children. * * Provides the operator: * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2f8ffeb814bc..af99d730eaf9 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -724,7 +724,7 @@ class Orthtree { } /*! - \brief determines whether the node specified by index `n` is a root node. + \brief determines whether the node specified by index `n` is the root node. */ bool is_root(Node_index n) const { return n == 0; diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 3970a7235e23..fdcbe33eccbf 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -119,7 +119,7 @@ namespace Orthtrees { \ingroup PkgOrthtreeNeighbors \brief finds at most `k` elements within a specific radius that are nearest to the center of the sphere `query`: if `query` does not contain - at least `k` elements, only contained points will be returned. + at least `k` elements, only contained elements will be returned. This function is useful when the user already knows how sparse the elements are, or if they do not care about elements that are too far away. From e190f302cac0594bdc3c2b48d6ba34195a1afa90 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 19 Feb 2024 15:09:11 +0100 Subject: [PATCH 423/520] add vertex_is_constrained_map to tetrahedral_isotropic_remeshing() and to convert_to_triangulation_3(), to collect them --- .../mesh_and_remesh_c3t3.cpp | 8 +- .../tetrahedral_adaptive_remeshing_impl.h | 11 ++- .../include/CGAL/tetrahedral_remeshing.h | 86 +++++++++++++++---- 3 files changed, 82 insertions(+), 23 deletions(-) diff --git a/Tetrahedral_remeshing/examples/Tetrahedral_remeshing/mesh_and_remesh_c3t3.cpp b/Tetrahedral_remeshing/examples/Tetrahedral_remeshing/mesh_and_remesh_c3t3.cpp index b5a10784e929..1187886dd0c2 100644 --- a/Tetrahedral_remeshing/examples/Tetrahedral_remeshing/mesh_and_remesh_c3t3.cpp +++ b/Tetrahedral_remeshing/examples/Tetrahedral_remeshing/mesh_and_remesh_c3t3.cpp @@ -39,6 +39,8 @@ using Vertex_pair = std::pair; using Constraints_set = std::unordered_set>; using Constraints_pmap = CGAL::Boolean_property_map; +using Corners_set = std::unordered_set>; +using Corners_pmap = CGAL::Boolean_property_map; // To avoid verbose function and named parameters call using namespace CGAL::parameters; @@ -73,8 +75,12 @@ int main(int argc, char* argv[]) Constraints_set constraints; Constraints_pmap constraints_pmap(constraints); + Corners_set corners; + Corners_pmap corners_pmap(corners); + Triangulation_3 tr = CGAL::convert_to_triangulation_3(std::move(c3t3), - CGAL::parameters::edge_is_constrained_map(constraints_pmap)); + CGAL::parameters::edge_is_constrained_map(constraints_pmap). + vertex_is_constrained_map(corners_pmap)); //note we use the move semantic, with std::move(c3t3), // to avoid a copy of the triangulation by the function diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h index 7a9050928706..baf6b7673197 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h @@ -76,6 +76,7 @@ struct All_cells_selected templatein_dimension() == 0 + || get(vcmap, vit) || nb_incident_complex_edges(vit, m_c3t3) > 2) { if (!m_c3t3.is_in_complex(vit)) diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index d11d72973b6a..67d133b0e813 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -107,6 +107,15 @@ namespace CGAL * by `Remeshing_edge_is_constrained_map` and `Remeshing_facet_is_constrained_map`.} * \cgalParamNEnd * +* \cgalParamNBegin{facet_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each facet of `tr`.} +* \cgalParamType{a class model of `ReadablePropertyMap` with `Triangulation_3::Facet` +* as key type and `bool` as value type. It must be default constructible.} +* \cgalParamDefault{a default property map where no facet is constrained} +* \cgalParamExtra{A constrained facet can be split or collapsed, but not flipped.} +* \cgalParamExtra{This map, contrary to the others, is not updated throughout the remeshing process.} +* \cgalParamNEnd +* * \cgalParamNBegin{edge_is_constrained_map} * \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tr`.} * \cgalParamType{a class model of `ReadWritePropertyMap` with `std::pair` @@ -118,13 +127,12 @@ namespace CGAL * with edge splits and collapses, so the property map must be writable.} * \cgalParamNEnd * -* \cgalParamNBegin{facet_is_constrained_map} -* \cgalParamDescription{a property map containing the constrained-or-not status of each facet of `tr`.} -* \cgalParamType{a class model of `ReadablePropertyMap` with `Triangulation_3::Facet` +* \cgalParamNBegin{vertex_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tr`.} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `Triangulation_3::Vertex_handle` * as key type and `bool` as value type. It must be default constructible.} -* \cgalParamDefault{a default property map where no facet is constrained} -* \cgalParamExtra{A constrained facet can be split or collapsed, but not flipped.} -* \cgalParamExtra{This map, contrary to the others, is not updated throughout the remeshing process.} +* \cgalParamDefault{a default property map where no vertex is constrained} +* \cgalParamExtra{A constrained vertex cannot be removed by collapse, nor moved by smoothing.} * \cgalParamNEnd * * \cgalParamNBegin{cell_is_selected_map} @@ -147,7 +155,8 @@ namespace CGAL * \cgalParamExtra{The endvertices of constraints listed * by `edge_is_constrained_map`, and edges incident to at least three subdomains * are made eligible to one dimensional smoothing, along the constrained polylines they belong to. -* Corners (i.e. vertices incident to more than 2 constrained edges) are not allowed +* Corners (i.e. vertices listed by `vertex_is_constrained_map` or +* incident to more than 2 constrained edges) are not allowed * to move at all.\n * Note that activating the smoothing step on polyline constraints tends to reduce * the quality of the minimal dihedral angle in the mesh.\n @@ -225,6 +234,15 @@ void tetrahedral_isotropic_remeshing( = choose_parameter(get_parameter(np, internal_np::cell_selector), Tetrahedral_remeshing::internal::All_cells_selected()); + typedef typename Tr::Vertex_handle Vertex_handle; + typedef typename internal_np::Lookup_named_param_def < + internal_np::vertex_is_constrained_t, + NamedParameters, + Constant_property_map//default + > ::type VCMap; + VCMap vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + Constant_property_map(false)); + typedef std::pair Edge_vv; typedef typename internal_np::Lookup_named_param_def < internal_np::edge_is_constrained_t, @@ -263,9 +281,9 @@ void tetrahedral_isotropic_remeshing( #endif typedef Tetrahedral_remeshing::internal::Adaptive_remesher< - Tr, SizingFunction, ECMap, FCMap, SelectionFunctor, Visitor> Remesher; + Tr, SizingFunction, VCMap, ECMap, FCMap, SelectionFunctor, Visitor> Remesher; Remesher remesher(tr, sizing, protect - , ecmap, fcmap + , vcmap, ecmap, fcmap , smooth_constrained_edges , cell_select , visitor); @@ -327,7 +345,16 @@ void tetrahedral_isotropic_remeshing( * as key type and `bool` as value type. It must be default constructible.} * \cgalParamDefault{a default property map where no edge is constrained} * \cgalParamNEnd -* +* \cgalParamNBegin{vertex_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of +* `c3t3.triangulation()`. +* For each vertex `v` for which `c3t3.is_in_complex(v)` returns `true`, +* the constrained status of `v` is set to `true`.} +* \cgalParamType{a class model of `ReadWritePropertyMap` +* with `Triangulation_3::Vertex_handle` +* as key type and `bool` as value type. It must be default constructible.} +* \cgalParamDefault{a default property map where no vertex is constrained} +* \cgalParamNEnd * \cgalNamedParamsEnd */ @@ -349,16 +376,25 @@ convert_to_triangulation_3( using Vertex_handle = typename Tr::Vertex_handle; using Edge_vv = std::pair; - using Default_pmap = Constant_property_map; + using Default_edge_pmap = Constant_property_map; using ECMap = typename internal_np::Lookup_named_param_def < internal_np::edge_is_constrained_t, NamedParameters, - Default_pmap + Default_edge_pmap + >::type; + using Default_vertex_pmap = Constant_property_map; + using VCMap = typename internal_np::Lookup_named_param_def < + internal_np::vertex_is_constrained_t, + NamedParameters, + Default_vertex_pmap >::type; + ECMap ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), - Default_pmap(false)); + Default_edge_pmap(false)); + VCMap vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + Default_vertex_pmap(false)); - if (!std::is_same_v) + if (!std::is_same_v) { for (auto e : c3t3.edges_in_complex()) { @@ -367,6 +403,13 @@ convert_to_triangulation_3( put(ecmap, evv, true); } } + if (!std::is_same_v) + { + for (auto v : c3t3.vertices_in_complex()) + { + put(vcmap, v, true); + } + } CGAL::Triangulation_3 tr; tr.swap(c3t3.triangulation()); @@ -444,6 +487,14 @@ void tetrahedral_isotropic_remeshing( = choose_parameter(get_parameter(np, internal_np::cell_selector), Tetrahedral_remeshing::internal::All_cells_selected()); + typedef typename internal_np::Lookup_named_param_def < + internal_np::vertex_is_constrained_t, + NamedParameters, + Constant_property_map//default + > ::type VCMap; + VCMap vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + Constant_property_map(false)); + typedef std::pair Edge_vv; typedef typename internal_np::Lookup_named_param_def < internal_np::edge_is_constrained_t, @@ -482,12 +533,9 @@ void tetrahedral_isotropic_remeshing( #endif typedef Tetrahedral_remeshing::internal::Adaptive_remesher< - Tr, SizingFunction, ECMap, FCMap, SelectionFunctor, - Visitor, - CornerIndex, CurveIndex - > Remesher; + Tr, SizingFunction, VCMap, ECMap, FCMap, SelectionFunctor, Visitor> Remesher; Remesher remesher(c3t3, sizing, protect - , ecmap, fcmap + , vcmap, ecmap, fcmap , smooth_constrained_edges , cell_select , visitor); From 26639f4c29509db99a3d59574312d906b8c40fab Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 19 Feb 2024 16:57:15 +0100 Subject: [PATCH 424/520] add missing typedef --- Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index 67d133b0e813..55ee573d570e 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -487,6 +487,7 @@ void tetrahedral_isotropic_remeshing( = choose_parameter(get_parameter(np, internal_np::cell_selector), Tetrahedral_remeshing::internal::All_cells_selected()); + typedef typename Tr::Vertex_handle Vertex_handle; typedef typename internal_np::Lookup_named_param_def < internal_np::vertex_is_constrained_t, NamedParameters, From b503b426368cce69cc6358e63df1ffb7d48eed4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 19 Feb 2024 17:36:53 +0100 Subject: [PATCH 425/520] typos + clean up --- Orthtree/doc/Orthtree/Orthtree.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 615042dd51d8..9f474e87f14a 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -12,7 +12,7 @@ namespace CGAL { Quadtrees are tree data structures in which each node encloses a rectangular section of space, and each internal node has exactly 4 children. Octrees are a similar data structure in 3D in which each -node encloses a cuboid section of space, and each internal node has +node encloses a rectangular cuboid section of space, and each internal node has exactly 8 children. We call the generalization of such data structure "orthtrees", as @@ -72,11 +72,11 @@ Nodes are split if their depth is less than 10, and they contain more than 5 inl \subsection Section_Orthtree_Point_Vector Building an Octree `Orthtree_traits_point<>` can also be templated with a 3d dimension tag and thus -behave as an %octree. For convenience, the alias `CGAL::Octree` is provided. +behaves as an %octree. For convenience, the alias `CGAL::Octree` is provided. The following example shows how to create an %octree from a vector of `Point_3` objects. As with the %quadtree example, we use the default split predicate. -In this case the max depth is 10, and the bucket size is 1. +In this case the maximum depth is 10, and the bucket size is 1. \cgalExample{Orthtree/octree_build_from_point_vector.cpp} @@ -91,8 +91,8 @@ An %octree is constructed from the point set and its corresponding map, and then written to the standard output. The split predicate is manually constructed and passed to the refine method. -In this case, we use a maximum number of inliers with no corresponding max depth, -this means that nodes will continue to be split until none contain more than 10 points. +In this case, we use a maximum number of inliers with no corresponding maximum depth. +This means that nodes will continue to be split until none contain more than 10 points. \cgalExample{Orthtree/octree_build_from_point_set.cpp} @@ -288,10 +288,10 @@ Simon Giraudot, supervisor of the GSoC internship, completed and finalized the package for integration in CGAL 5.3. Pierre Alliez provided kind help and advice all the way through. Starting with CGAL 6.0 an API improvement of the Orthtree package was released. -Most notably, the orthtree does not need to store anything. Data to be stored +Most notably, the orthtree nodes do not need to store anything. Data to be stored by the node are managed through a mechanism of dynamic properties. This improvement was done by Jackson Campolattaro thanks to a funding provided by -INRIA, together with help from GeometryFactory. +INRIA, together with GeometryFactory. */ From 14089f54f916b078bcf0054bfb6cbc8b2732b039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 19 Feb 2024 18:42:35 +0100 Subject: [PATCH 426/520] clean up --- .../Concepts/CollectionPartitioningOrthtreeTraits.h | 4 ++-- .../doc/Orthtree/Concepts/OrthtreeTraitsWithData.h | 5 ++--- Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h | 3 +-- Orthtree/include/CGAL/Orthtree.h | 13 ++++++++++--- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 6 +++--- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 523408e63e8b..6c8df3b9347e 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -2,7 +2,7 @@ \ingroup PkgOrthtreeConcepts \cgalConcept - Refinement of the OrthtreeTraitsWithData concept, adding requirements for the + Refinement of the `OrthtreeTraitsWithData` concept, adding requirements for the traits class of a `CGAL::Orthtree` in order to supports nearest-neighbor searching. Nearest neighbor searches expect a tree where `Node_data` is a model of `ForwardRange`. @@ -36,7 +36,7 @@ class CollectionPartitioningOrthtreeTraits { using Node_data_element = unspecified_type; /*! - * \brief Functor with an operator calculate the squared distance of `Node_data_element` from a point. + * \brief Functor with an operator that calculates the squared distance of a `Node_data_element` from a point. * * Provides the operator: * `FT operator()(const Node_data_element&, const Point_d&)` diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h index e3e5f5e6124a..508a639361df 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -25,11 +25,10 @@ class OrthtreeTraitsWithData using Node_data = unspecified_type; /*! - * \brief Functor which initializes the contained elements for the root node. + * \brief Functor which initializes elements contained by the root node. * * Each node of a tree has an associated `Node_data` value. - * For most nodes, this is set by `Distribute_node_contents`, but that is not possible for the root node. - * Instead, this functor initializes the `Node_data` of the root node. + * This functor initializes the `Node_data` of the root node. * It takes no arguments, and returns an instance of `Node_data`. * * Provides the operator: diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h index b95cb0908f63..64e84572cea5 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraversal.h @@ -1,4 +1,3 @@ - /*! \ingroup PkgOrthtreeConcepts \cgalConcept @@ -24,7 +23,7 @@ class OrthtreeTraversal { Node_index first_index() const; /*! - \brief returns the next node to iterate to, given the previous node. + \brief returns the next node to be traversed given the previous node `n`. */ Node_index next(Node_index n) const; }; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index af99d730eaf9..aa866510a83d 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -110,12 +110,10 @@ struct Node_data_wrapper \sa `CGAL::Quadtree` \sa `CGAL::Octree` - \tparam GeomTraits must be a model of `OrthtreeTraits` + \tparam GeomTraits must be a model of `OrthtreeTraits` or `OrthtreeTraitswithData`. */ template class Orthtree { - static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; - public: /// \name Template Types /// @{ @@ -124,6 +122,11 @@ class Orthtree { /// \name Traits Types /// @{ +#ifndef DOXYGEN_RUNNING + static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; +#else + static inline constexpr bool has_data = bool_value; ///< `true` if `GeomTraits` is a model of `OrthtreeTraitswithData` and `false` otherwise. +#endif static constexpr int dimension = Traits::dimension; ///< Dimension of the tree using Kernel = typename Traits::Kernel; ///< Kernel type. using FT = typename Traits::FT; ///< Number type. @@ -133,7 +136,11 @@ class Orthtree { using Adjacency = typename Traits::Adjacency; ///< Adjacency type. using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. +#ifndef DOXYGEN_RUNNING using Node_data = typename Orthtree_impl::Node_data_wrapper::Node_data; +#else + using Node_data = std::conditional_t; +#endif /// @} diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 3562a97b10b2..4f06dff45daa 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -26,7 +26,7 @@ namespace CGAL { /*! \ingroup PkgOrthtreeTraits -Traits class for the `Orthtree` class to be used to contruct a 3D octree around +Traits class for the `Orthtree` class to be used to construct a 3D octree around a triangulated surface mesh. Each node of the octree will store all the faces of the mesh intersected by its bounding box. The subdivision of the octree is controlled by the nested class `Orthtree_traits_face_graph::Split_predicate_node_min_extent` @@ -93,13 +93,13 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base< }; } - auto construct_root_node_contents_object() { + auto construct_root_node_contents_object() const { return [&]() -> Node_data { return {faces(m_pm).begin(), faces(m_pm).end()}; }; } - auto distribute_node_contents_object() { + auto distribute_node_contents_object() const { return [&](Node_index n, Tree& tree, const Point_d& /* center */) -> void { Node_data& ndata = tree.data(n); for (int i = 0; i < 8; ++i) { From c954092d46db16a521267543338c4db0ef6425aa Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 20 Feb 2024 13:26:29 +0100 Subject: [PATCH 427/520] renaming Orthtree_traits_without_data --- Orthtree/doc/Orthtree/PackageDescription.txt | 2 +- ...htree_traits_without_data.h => Orthtree_traits.h} | 12 ++++++------ .../Orthtree/test_octree_copy_move_constructors.cpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) rename Orthtree/include/CGAL/{Orthtree_traits_without_data.h => Orthtree_traits.h} (79%) diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 66cdb6aea705..4e8cad96e92c 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -51,7 +51,7 @@ Quadtree, Octree and Orthtree Reference - `CGAL::Orthtree` \cgalCRPSection{Traits} -- `CGAL::Orthtree_traits_without_data` +- `CGAL::Orthtree_traits` - `CGAL::Orthtree_traits_point` - `CGAL::Orthtree_traits_base` - `CGAL::Orthtree_traits_face_graph` diff --git a/Orthtree/include/CGAL/Orthtree_traits_without_data.h b/Orthtree/include/CGAL/Orthtree_traits.h similarity index 79% rename from Orthtree/include/CGAL/Orthtree_traits_without_data.h rename to Orthtree/include/CGAL/Orthtree_traits.h index 608c058407f7..cc1665622e7b 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_without_data.h +++ b/Orthtree/include/CGAL/Orthtree_traits.h @@ -9,8 +9,8 @@ // // Author(s) : Sven Oesau -#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H -#define ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H +#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_H +#define ORTHTREE_TESTS_ORTHTREE_TRAITS_H #include @@ -37,15 +37,15 @@ namespace CGAL { \sa `CGAL::Orthtree_traits_base` */ template -struct Orthtree_traits_without_data: public Orthtree_traits_base { +struct Orthtree_traits : public Orthtree_traits_base { public: using Base = Orthtree_traits_base; - using Self = Orthtree_traits_without_data; + using Self = Orthtree_traits; using Tree = Orthtree; using Node_index = typename Base::Node_index; - Orthtree_traits_without_data() {} + Orthtree_traits() {} auto construct_root_node_bbox_object() const { return [&]() -> typename Self::Bbox_d { @@ -58,4 +58,4 @@ struct Orthtree_traits_without_data: public Orthtree_traits_base #include -#include +#include #include using Kernel = CGAL::Simple_cartesian; @@ -17,7 +17,7 @@ using Point = Kernel::Point_3; using FT = Kernel::FT; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; -using Octree_without_data = CGAL::Orthtree>; +using Octree_without_data = CGAL::Orthtree>; template int test(Tree &tree) From 6d84c076213db195aaed46c8e9d467ab6d336bec Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 20 Feb 2024 13:27:10 +0100 Subject: [PATCH 428/520] pass on doc --- .../CollectionPartitioningOrthtreeTraits.h | 7 ++++- .../Concepts/OrthtreeTraitsWithData.h | 8 +++--- Orthtree/doc/Orthtree/Orthtree.txt | 27 +++++++------------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 6c8df3b9347e..f82736f79cf7 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -27,10 +27,15 @@ class CollectionPartitioningOrthtreeTraits { */ using Sphere_d = unspecified_type; + /*! + * \brief The data type contained by each node; must be a model of `ForwardRange`. + */ + using Node_data = unspecified_type; + /*! * \brief An element of the `Node_data` list-like type. * - * Must be constructible from the type produced by dereferencing a `Node_data` iterator. + * Must be constructible from the value type of a `Node_data` iterator. * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. */ using Node_data_element = unspecified_type; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h index 508a639361df..b87cedd89492 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -46,9 +46,11 @@ class OrthtreeTraitsWithData * Provides the operator: * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` * - * It can use `tree.children(node_index)` to access the children of the node, and `tree.data(node_index)` - * to access its children and the contents of the node. - * It must distribute the contents of the node to each of its children. + * The functor is called during refinement of the `Orthtree` on a node after it has been split. The purpose of the functor is to + * distribute the `Node_data`, accessible via `tree.data()`, to the data of the nodes children, accessible via `tree.children()`. + * The first parameter is the `Node_index` of the node. The second parameter provides the instance of the `Orthtree` + * and the last parameter is the barycenter of the node which delimits the internal boundaries of the children. + * * For a tree in which each node contains a span, this may mean rearranging the contents of the original node * and producing spans containing a subset of its contents for each of its children. * For compatibility with locate, the center of the node is considered to be part of the upper half. diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 9f474e87f14a..726922244a74 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -58,7 +58,7 @@ the existing ones do not match users' needs. \subsection Section_Orthtree_Quadtree Building a Quadtree The `Orthtree` class may be templated with `Orthtree_traits_point<>` -with a 2d dimension tag and thus behave as a %quadtree. +while specifying a 2d ambient space and thus behave as a %quadtree. For convenience, the alias `CGAL::Quadtree` is provided. The following example shows the construction of %quadtree from a vector of `Point_2` objects. @@ -71,8 +71,8 @@ Nodes are split if their depth is less than 10, and they contain more than 5 inl \subsection Section_Orthtree_Point_Vector Building an Octree -`Orthtree_traits_point<>` can also be templated with a 3d dimension tag and thus -behaves as an %octree. For convenience, the alias `CGAL::Octree` is provided. +`Orthtree_traits_point<>` can also be templated with dimension 3 and thus +behave as an %octree. For convenience, the alias `CGAL::Octree` is provided. The following example shows how to create an %octree from a vector of `Point_3` objects. As with the %quadtree example, we use the default split predicate. @@ -119,16 +119,10 @@ set as the orthtree's map type, so a map does not need to be provided. \cgalExample{Orthtree/orthtree_build.cpp} \section Section_Orthtree_Properties Properties -The Orthtree uses a mechanism to attach properties to nodes at run-time which follows \ref sectionSurfaceMesh_properties "Surface mesh properties". Each property is identified by a string and its key type and stored in a consecutive block of memory. Contrary to surface mesh the removal of properties is not supported. - -Several properties are provided by default and used to maintain the data structure. The property \c "contents" keeps the data for each node. \c "parents" and \c "children" maintain the relation between nodes. \c "coordinates" and \c "depth" contain absolute positions of nodes. +The Orthtree uses a mechanism to attach properties to nodes at run-time which follows \ref sectionSurfaceMesh_properties "Surface mesh properties". Each property is identified by a string and its value type and stored in a consecutive block of memory. \section Section_Orthtree_Traversal Traversal -\note For simplicity, the rest of the user manual will only use -octrees, but all the presented features also apply to quadtrees and -higher dimension orthtrees. - %Traversal is the act of navigating among the nodes of the tree. The `Orthtree` class provides a number of different solutions for traversing the tree. @@ -164,8 +158,8 @@ It is often useful to be able to iterate over the nodes of the tree in a particu For example, the stream operator `<<` uses a traversal to print out each node. A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal) and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal). -To traverse a tree in preorder is to visit each parent immediately followed by its children, -whereas in postorder, traversal the children are visited first. +Traversing a tree in preorder means to visit each parent immediately followed by its children, +whereas in postorder traversal the children are visited first. The following example illustrates how to use the provided traversals. @@ -181,7 +175,7 @@ Users can define their own traversal methods by creating models of the `Orthtree The custom traversal must provide a method which returns the starting point of the traversal (`first_index()`) and another method which returns the next node in the sequence (`next_index()`). `next_index()` returns an empty optional when the end of the traversal is reached. -The following example shows how to define a custom traversal that only traverses the first branch of the octree: +The following example shows how to define a custom traversal that only traverses the first branch an octree: \cgalExample{Orthtree/octree_traversal_custom.cpp} @@ -202,12 +196,11 @@ Once an orthtree is built, its structure can be used to accelerate different tas \subsection Section_Orthtree_Nearest_Neighbor Finding the Nearest Neighbor of a Point -The naive way of finding the nearest neighbor of a point requires finding the distance to every other point. +The naive way of finding the nearest neighbor of a point requires finding the distance to all elements. An orthtree can be used to perform the same task in significantly less time. -For large numbers of points, this can be a large enough difference to outweigh the time spent building the tree. +For large numbers of elements, this can be a large enough difference to outweigh the time spent building the tree. -Note that a kd-tree is expected to outperform the orthtree for this task, -it should be preferred unless features specific to the orthtree are needed. +Note that a kd-tree is expected to outperform the orthtree for this task on points, it should be preferred unless features specific to the orthtree are needed. The following example illustrates how to use an octree to accelerate the search for points close to a location. From d95d650dbf1788961a692a53951e20cc2855f7b4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 20 Feb 2024 14:21:27 +0100 Subject: [PATCH 429/520] added parameter to Orthtree_traits_point to use square/cubic space --- Orthtree/include/CGAL/Octree.h | 6 ++++-- Orthtree/include/CGAL/Orthtree_traits_point.h | 18 +++++++++++++++++- Orthtree/include/CGAL/Quadtree.h | 7 +++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 625494bb2be1..a8fc9df47950 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -27,13 +27,15 @@ namespace CGAL { \tparam GeomTraits a model of `Kernel` \tparam PointRange a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` + \tparam cubic boolean to enforce a cubic octree */ template < typename GeomTraits, typename PointRange, - typename PointMap = Identity_property_map::value_type> + typename PointMap = Identity_property_map::value_type>, + bool cubic = false > -using Octree = Orthtree>; +using Octree = Orthtree>; } // namespace CGAL diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 68aa20bf9a1c..ee19d0f35e89 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -78,6 +78,7 @@ template < typename GeomTraits, typename PointRange, typename PointMap = Identity_property_map::value_type>, + bool cubic = false, int dimension = Ambient_dimension< typename std::iterator_traits::value_type, GeomTraits @@ -91,7 +92,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base; - using Self = Orthtree_traits_point; + using Self = Orthtree_traits_point; using Tree = Orthtree; using Node_index = typename Base::Node_index; @@ -129,6 +130,21 @@ struct Orthtree_traits_point : public Orthtree_traits_base center; + FT max_side = 0; + for (int i = 0; i < Self::dimension; i++) { + FT side = bbox_max[i] - bbox_min[i]; + max_side = (std::max)(max_side, side); + center[i] = (bbox_min[i] + bbox_max[i]) * 0.5f; + } + max_side *= 0.5f; + for (int i = 0; i < Self::dimension; i++) { + bbox_min[i] = center[i] - max_side; + bbox_max[i] = center[i] + max_side; + } + } + return {std::apply(Self::construct_point_d_object(), bbox_min), std::apply(Self::construct_point_d_object(), bbox_max)}; }; diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 5bbf2ecea8e1..c4b57054ee1a 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -27,12 +27,15 @@ namespace CGAL { \tparam GeomTraits must be a model of `Kernel` \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_2` + \tparam square boolean to enforce a square quadtree */ template ::value_type> > + ::value_type>, + bool squared = false +> -using Quadtree = Orthtree>; +using Quadtree = Orthtree>; } // namespace CGAL From 041950765b020a737430af218ff2e36fda4edc4a Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 20 Feb 2024 15:39:32 +0100 Subject: [PATCH 430/520] missing template parameter --- .../include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h index ceaa7fe0bda4..acb782b70a63 100644 --- a/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h +++ b/Shape_detection/include/CGAL/Shape_detection/Efficient_RANSAC/Octree.h @@ -56,7 +56,7 @@ class RANSAC_octree { typedef std::vector Input_range; typedef Random_index_access_property_map Indexed_point_map; - typedef Orthtree_traits_point::type, Input_range, Indexed_point_map, 3> OTraits; + typedef Orthtree_traits_point::type, Input_range, Indexed_point_map, false, 3> OTraits; typedef CGAL::Orthtree Octree; From ca4d146ffd3cbd41b6e5d778584479097c0c183a Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 20 Feb 2024 16:21:21 +0100 Subject: [PATCH 431/520] fix for ci --- Orthtree/include/CGAL/Orthtree_traits_point.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index ee19d0f35e89..e2ec90407a30 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -132,10 +132,10 @@ struct Orthtree_traits_point : public Orthtree_traits_base center; - FT max_side = 0; + typename Self::FT max_side = 0; for (int i = 0; i < Self::dimension; i++) { - FT side = bbox_max[i] - bbox_min[i]; - max_side = (std::max)(max_side, side); + typename Self::FT side = bbox_max[i] - bbox_min[i]; + max_side = (std::max)(max_side, side); center[i] = (bbox_min[i] + bbox_max[i]) * 0.5f; } max_side *= 0.5f; From fc9535427fdad9f042679a8776656bf753c7977b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 21 Feb 2024 08:16:29 +0100 Subject: [PATCH 432/520] Update Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h Co-authored-by: Sebastien Loriot --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h index b87cedd89492..6bafe92436fa 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -49,7 +49,7 @@ class OrthtreeTraitsWithData * The functor is called during refinement of the `Orthtree` on a node after it has been split. The purpose of the functor is to * distribute the `Node_data`, accessible via `tree.data()`, to the data of the nodes children, accessible via `tree.children()`. * The first parameter is the `Node_index` of the node. The second parameter provides the instance of the `Orthtree` - * and the last parameter is the barycenter of the node which delimits the internal boundaries of the children. + * and the last parameter is the barycenter of the node which will be used as shared corner amongst the children of the node. * * For a tree in which each node contains a span, this may mean rearranging the contents of the original node * and producing spans containing a subset of its contents for each of its children. From 2fb6c400bc327accbb649166038102315d77bca1 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 21 Feb 2024 09:35:29 +0100 Subject: [PATCH 433/520] bug fix --- Orthtree/examples/Orthtree/orthtree_build.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index f4d6d2300f9d..9f08bfc331dd 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -12,8 +12,7 @@ using Kernel = CGAL::Epick_d >; using Point_d = Kernel::Point_d; using Point_vector = std::vector; using Traits = CGAL::Orthtree_traits_point; -using Traits2 = CGAL::Orthtree_traits_point::value_type>, dimension>; -using Orthtree = CGAL::Orthtree; +using Orthtree = CGAL::Orthtree; int main() { From 7b907d54a54c956bd3548b09e03bb3590b8ae996 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 21 Feb 2024 10:01:14 +0100 Subject: [PATCH 434/520] pass on doc --- .../Concepts/CollectionPartitioningOrthtreeTraits.h | 2 +- Orthtree/include/CGAL/Octree.h | 6 +++--- Orthtree/include/CGAL/Orthtree_traits_point.h | 6 +++--- Orthtree/include/CGAL/Quadtree.h | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index f82736f79cf7..993a91d8c304 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -35,7 +35,7 @@ class CollectionPartitioningOrthtreeTraits { /*! * \brief An element of the `Node_data` list-like type. * - * Must be constructible from the value type of a `Node_data` iterator. + * Must be constructible from the value type of a `Node_data::iterator`. * Typically the same as that type, but can also be an `std::reference_wrapper<>` if the type is not copyable. */ using Node_data_element = unspecified_type; diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index a8fc9df47950..009f62cc5144 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -27,15 +27,15 @@ namespace CGAL { \tparam GeomTraits a model of `Kernel` \tparam PointRange a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` - \tparam cubic boolean to enforce a cubic octree + \tparam cubic_nodes boolean to enforce cubic nodes */ template < typename GeomTraits, typename PointRange, typename PointMap = Identity_property_map::value_type>, - bool cubic = false + bool cubic_nodes = false > -using Octree = Orthtree>; +using Octree = Orthtree>; } // namespace CGAL diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index e2ec90407a30..e3abafbbd8d0 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -78,7 +78,7 @@ template < typename GeomTraits, typename PointRange, typename PointMap = Identity_property_map::value_type>, - bool cubic = false, + bool hypercubic_nodes = false, int dimension = Ambient_dimension< typename std::iterator_traits::value_type, GeomTraits @@ -92,7 +92,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base; - using Self = Orthtree_traits_point; + using Self = Orthtree_traits_point; using Tree = Orthtree; using Node_index = typename Base::Node_index; @@ -130,7 +130,7 @@ struct Orthtree_traits_point : public Orthtree_traits_base center; typename Self::FT max_side = 0; for (int i = 0; i < Self::dimension; i++) { diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index c4b57054ee1a..755dfc8860b6 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -27,15 +27,15 @@ namespace CGAL { \tparam GeomTraits must be a model of `Kernel` \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_2` - \tparam square boolean to enforce a square quadtree + \tparam square_nodes boolean to enforce square nodes */ template ::value_type>, - bool squared = false + bool squared_nodes = false > -using Quadtree = Orthtree>; +using Quadtree = Orthtree>; } // namespace CGAL From 5ad3d9081955f6cc481a4700316fe2f51bb87679 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 21 Feb 2024 10:40:16 +0100 Subject: [PATCH 435/520] added requirements for Node_data --- .../Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h | 2 +- Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 993a91d8c304..2c2e469f7f21 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -28,7 +28,7 @@ class CollectionPartitioningOrthtreeTraits { using Sphere_d = unspecified_type; /*! - * \brief The data type contained by each node; must be a model of `ForwardRange`. + * \brief The data type contained by each node; must be a model of `ForwardRange` in addition to default constructible, copy constructible and copy assignable. */ using Node_data = unspecified_type; diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h index 6bafe92436fa..298691cef839 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -20,7 +20,7 @@ class OrthtreeTraitsWithData /// \name Types /// @{ /*! - * \brief The data type contained by each node. + * \brief The data type contained by each node. Must be default constructible, copy constructible and copy assignable. */ using Node_data = unspecified_type; From 2bbf5b8cbe187048e67259d75f4678633c14d6b3 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 21 Feb 2024 12:32:06 +0100 Subject: [PATCH 436/520] added functors for accessing Sphere_d --- .../CollectionPartitioningOrthtreeTraits.h | 41 ++++++++++++++++++- .../include/CGAL/Orthtree/Nearest_neighbors.h | 10 ++--- Orthtree/include/CGAL/Orthtree_traits_point.h | 18 ++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 2c2e469f7f21..9dcdb855330f 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -23,7 +23,7 @@ class CollectionPartitioningOrthtreeTraits { /// @{ /*! - * Sphere type used for the shrinking-sphere approach for neighbor queries + * Sphere Type used for the shrinking-sphere approach for neighbor queries; needs to be copy assignable. */ using Sphere_d = unspecified_type; @@ -48,11 +48,50 @@ class CollectionPartitioningOrthtreeTraits { */ using Squared_distance_of_element = unspecified_type; + /*! + * \brief Functor with an operator that constructs a `Sphere_d` from a provided center and squared radius. + * + * Provides the operator: + * `Sphere_d operator()(const Point_d&, const FT&)` + */ + using Construct_sphere_3 = unspecified_type; + + /*! + * \brief Functor with an operator that provides the center of a `Sphere_d`. + * + * Provides the operator: + * `Point_d operator()(const Sphere_d&)` + */ + using Construct_center_3 = unspecified_type; + + /*! + * \brief Functor with an operator that provides the squared radius of a `Sphere_d`. + * + * Provides the operator: + * `FT operator()(const Sphere_d&)` + */ + using Compute_squared_radius_3 = unspecified_type; + /// @} /// \name Operations /// @{ + /*! + * constructs an object of type `ConstructSphere_3`. + */ + Construct_sphere_3 get_construct_sphere_3_object() const; + + /*! + * constructs an object of type `ConstructCenter_3`. + */ + Construct_center_3 get_construct_center_3_object() const; + + /*! + * constructs an object of type `ComputeSquaredRadius_3`. + */ + Compute_squared_radius_3 get_compute_squared_radius_3_object() const; + /*! * constructs an object of type `Squared_distance_of_element`. */ diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index fdcbe33eccbf..7a033440fbd9 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -38,10 +38,10 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Pair that element with its distance from the search point Result current_element_with_distance = - {e, orthtree.traits().get_squared_distance_of_element_object()(e, search_bounds.center())}; + {e, orthtree.traits().get_squared_distance_of_element_object()(e, orthtree.traits().get_construct_center_3_object()(search_bounds))}; // Check if the new element is within the bounds - if (current_element_with_distance.distance < search_bounds.squared_radius()) { + if (current_element_with_distance.distance < orthtree.traits().get_compute_squared_radius_3_object()(search_bounds)) { // Check if the results list is full if (results.size() == results.capacity()) { @@ -62,7 +62,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, if (results.size() == results.capacity()) { // Set the search radius - search_bounds = typename Tree::Sphere(search_bounds.center(), results.back().distance + epsilon); + search_bounds = orthtree.traits().get_construct_sphere_3_object()(orthtree.traits().get_construct_center_3_object()(search_bounds), results.back().distance + epsilon); } } } @@ -89,7 +89,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Add a child to the list, with its distance children_with_distances.emplace_back( child_node, - CGAL::squared_distance(search_bounds.center(), orthtree.barycenter(child_node)) + CGAL::squared_distance(orthtree.traits().get_construct_center_3_object()(search_bounds), orthtree.barycenter(child_node)) ); } @@ -182,7 +182,7 @@ template OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Point& query, std::size_t k, OutputIterator output) { - typename Tree::Sphere query_sphere(query, (std::numeric_limits::max)()); + typename Tree::Sphere query_sphere = orthtree.traits().get_construct_sphere_3_object()(query, (std::numeric_limits::max)()); return nearest_k_neighbors_in_radius(orthtree, query_sphere, k, output); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index e3abafbbd8d0..068aa0baeaf6 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -163,6 +163,24 @@ struct Orthtree_traits_point : public Orthtree_traits_base typename Self::Sphere_d { + return typename Self::Sphere_d(center, squared_radius); + }; + } + + auto get_construct_center_3_object() const { + return [](const typename Self::Sphere_d& sphere) -> typename Self::Point_d { + return sphere.center(); + }; + } + + auto get_compute_squared_radius_3_object() const { + return [](const typename Self::Sphere_d& sphere) -> typename Self::FT { + return sphere.squared_radius(); + }; + } + auto get_squared_distance_of_element_object() const { return [&](const Node_data_element& index, const typename Self::Point_d& point) -> typename Self::FT { return CGAL::squared_distance(get(m_point_map, index), point); From 95ba63590a109d9dfd34cfbd89459506413484db Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 21 Feb 2024 13:33:22 +0100 Subject: [PATCH 437/520] renamed getter for functors in CollectionPartitioningOrthtreeTraits --- .../Concepts/CollectionPartitioningOrthtreeTraits.h | 8 ++++---- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 10 +++++----- Orthtree/include/CGAL/Orthtree_traits_point.h | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 9dcdb855330f..1bb763500cc5 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -80,22 +80,22 @@ class CollectionPartitioningOrthtreeTraits { /*! * constructs an object of type `ConstructSphere_3`. */ - Construct_sphere_3 get_construct_sphere_3_object() const; + Construct_sphere_3 construct_sphere_3_object() const; /*! * constructs an object of type `ConstructCenter_3`. */ - Construct_center_3 get_construct_center_3_object() const; + Construct_center_3 construct_center_3_object() const; /*! * constructs an object of type `ComputeSquaredRadius_3`. */ - Compute_squared_radius_3 get_compute_squared_radius_3_object() const; + Compute_squared_radius_3 compute_squared_radius_3_object() const; /*! * constructs an object of type `Squared_distance_of_element`. */ - Squared_distance_of_element get_squared_distance_of_element_object() const; + Squared_distance_of_element squared_distance_of_element_object() const; /// @} }; diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 7a033440fbd9..913ba2350b9d 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -38,10 +38,10 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Pair that element with its distance from the search point Result current_element_with_distance = - {e, orthtree.traits().get_squared_distance_of_element_object()(e, orthtree.traits().get_construct_center_3_object()(search_bounds))}; + {e, orthtree.traits().squared_distance_of_element_object()(e, orthtree.traits().construct_center_3_object()(search_bounds))}; // Check if the new element is within the bounds - if (current_element_with_distance.distance < orthtree.traits().get_compute_squared_radius_3_object()(search_bounds)) { + if (current_element_with_distance.distance < orthtree.traits().compute_squared_radius_3_object()(search_bounds)) { // Check if the results list is full if (results.size() == results.capacity()) { @@ -62,7 +62,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, if (results.size() == results.capacity()) { // Set the search radius - search_bounds = orthtree.traits().get_construct_sphere_3_object()(orthtree.traits().get_construct_center_3_object()(search_bounds), results.back().distance + epsilon); + search_bounds = orthtree.traits().construct_sphere_3_object()(orthtree.traits().construct_center_3_object()(search_bounds), results.back().distance + epsilon); } } } @@ -89,7 +89,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Add a child to the list, with its distance children_with_distances.emplace_back( child_node, - CGAL::squared_distance(orthtree.traits().get_construct_center_3_object()(search_bounds), orthtree.barycenter(child_node)) + CGAL::squared_distance(orthtree.traits().construct_center_3_object()(search_bounds), orthtree.barycenter(child_node)) ); } @@ -182,7 +182,7 @@ template OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Point& query, std::size_t k, OutputIterator output) { - typename Tree::Sphere query_sphere = orthtree.traits().get_construct_sphere_3_object()(query, (std::numeric_limits::max)()); + typename Tree::Sphere query_sphere = orthtree.traits().construct_sphere_3_object()(query, (std::numeric_limits::max)()); return nearest_k_neighbors_in_radius(orthtree, query_sphere, k, output); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 068aa0baeaf6..a1dd641edf58 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -163,25 +163,25 @@ struct Orthtree_traits_point : public Orthtree_traits_base typename Self::Sphere_d { return typename Self::Sphere_d(center, squared_radius); }; } - auto get_construct_center_3_object() const { + auto construct_center_3_object() const { return [](const typename Self::Sphere_d& sphere) -> typename Self::Point_d { return sphere.center(); }; } - auto get_compute_squared_radius_3_object() const { + auto compute_squared_radius_3_object() const { return [](const typename Self::Sphere_d& sphere) -> typename Self::FT { return sphere.squared_radius(); }; } - auto get_squared_distance_of_element_object() const { + auto squared_distance_of_element_object() const { return [&](const Node_data_element& index, const typename Self::Point_d& point) -> typename Self::FT { return CGAL::squared_distance(get(m_point_map, index), point); }; From 290f79777ef676fcc7484dadf3a61cc20ba5f9f1 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 22 Feb 2024 08:59:56 +0000 Subject: [PATCH 438/520] Keep only Is_zero --- Number_types/include/CGAL/CORE_Expr.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Number_types/include/CGAL/CORE_Expr.h b/Number_types/include/CGAL/CORE_Expr.h index e0f72a223266..efeb5ad9363a 100644 --- a/Number_types/include/CGAL/CORE_Expr.h +++ b/Number_types/include/CGAL/CORE_Expr.h @@ -123,22 +123,6 @@ template <> class Algebraic_structure_traits< CORE::Expr > } }; - class Is_one - : public CGAL::cpp98::unary_function< Type, bool > { - public: - bool operator()( const Type& x ) const { - double inf, sup; - x.doubleInterval(inf,sup); - if((inf > 1) || (sup < 1)){ - return false; - } - if(inf == sup){ - return true; - } - return x.cmp(Type::getOne()) == 0; - } - }; - }; template <> class Real_embeddable_traits< CORE::Expr > From 9ee06f2dd0245ff0dd750468f7b78dcab686ec38 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 22 Feb 2024 15:51:46 +0100 Subject: [PATCH 439/520] ci fix for msvc2017 --- Orthtree/include/CGAL/Orthtree_traits_point.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index a1dd641edf58..1e88b36f312e 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -130,7 +130,11 @@ struct Orthtree_traits_point : public Orthtree_traits_base 1920 if constexpr (hypercubic_nodes) { +#else + if (hypercubic_nodes) { +#endif std::array center; typename Self::FT max_side = 0; for (int i = 0; i < Self::dimension; i++) { From 38c43ce2cbfd71e79328f37015ee46568e8dc147 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 23 Feb 2024 07:44:07 +0000 Subject: [PATCH 440/520] Fix multipolygon.cpp --- Polygon/examples/Polygon/multipolygon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon/examples/Polygon/multipolygon.cpp b/Polygon/examples/Polygon/multipolygon.cpp index 9699f916e99f..babf3def44d6 100644 --- a/Polygon/examples/Polygon/multipolygon.cpp +++ b/Polygon/examples/Polygon/multipolygon.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { mp.add_polygon_with_holes(p1); mp.add_polygon_with_holes(p2); - for (auto const& p: mp.polygons()) { + for (auto const& p: mp.polygons_with_holes()) { std::cout << p << std::endl; } From 25b084e146e1d19ecba08544e07ed464fa88ff94 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 23 Feb 2024 07:52:05 +0000 Subject: [PATCH 441/520] Fix operator<< for Multipolygon_with_holes --- Polygon/include/CGAL/Multipolygon_with_holes_2.h | 4 ++-- Stream_support/examples/Stream_support/Polygon_WKT.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index 3fb93f903be7..7cabd2871fe2 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -117,8 +117,8 @@ std::ostream& operator<<(std::ostream& os, switch(IO::get_mode(os)) { case IO::ASCII : - os << mp.number_of_polygons() << ' '; - for (i = mp.polygon_with_holes_begin(); i != mp.polygon_with_holes_end(); ++i) { + os << mp.number_of_polygons_with_holes() << ' '; + for (i = mp.polygons_with_holes_begin(); i != mp.polygons_with_holes_end(); ++i) { os << *i << ' '; } return os; diff --git a/Stream_support/examples/Stream_support/Polygon_WKT.cpp b/Stream_support/examples/Stream_support/Polygon_WKT.cpp index f725dcd37494..9bf7a37cf4bd 100644 --- a/Stream_support/examples/Stream_support/Polygon_WKT.cpp +++ b/Stream_support/examples/Stream_support/Polygon_WKT.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include From fa2a031b66ba35a69489057414b751e7f0e8b511 Mon Sep 17 00:00:00 2001 From: Marc Glisse Date: Sat, 24 Feb 2024 16:10:08 +0100 Subject: [PATCH 442/520] [[no_unique_address]] to compact Triangulation_ds_full_cell --- Triangulation/include/CGAL/Triangulation_ds_full_cell.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Triangulation/include/CGAL/Triangulation_ds_full_cell.h b/Triangulation/include/CGAL/Triangulation_ds_full_cell.h index 3e84630dbd9e..cb361f856e34 100644 --- a/Triangulation/include/CGAL/Triangulation_ds_full_cell.h +++ b/Triangulation/include/CGAL/Triangulation_ds_full_cell.h @@ -249,7 +249,9 @@ class Triangulation_ds_full_cell const Vertex_handle_array & vertices() const {return combinatorics_.vertices_; } // DATA MEMBERS - Combinatorics combinatorics_; + // With the Itanium ABI, [[no_unique_address]] allows tda_data_ to reuse the + // padding bytes at the end of combinatorics_ when using the mirror policy. + CGAL_NO_UNIQUE_ADDRESS Combinatorics combinatorics_; mutable TDS_data tds_data_; }; From ae9efb3c53d3a6fe346c1f2e9dfb6a389b6b1efd Mon Sep 17 00:00:00 2001 From: Nicolas Saillant Date: Mon, 26 Feb 2024 09:26:47 +0100 Subject: [PATCH 443/520] Fix summary page link in create_testresult_page --- Maintenance/test_handling/create_testresult_page | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Maintenance/test_handling/create_testresult_page b/Maintenance/test_handling/create_testresult_page index 323ae475b67b..8d9a867592de 100755 --- a/Maintenance/test_handling/create_testresult_page +++ b/Maintenance/test_handling/create_testresult_page @@ -635,7 +635,7 @@ The doxygen documentation testpage (and the overview page)

  • Diff of testsuites results
  • -
  • +
  • Summary Page
  • EOF From c5147435aadcbc9a95f9767b6be2991c46515526 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 26 Feb 2024 11:49:36 +0100 Subject: [PATCH 444/520] reuse.yml: call apt-get update --- .github/workflows/reuse.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index c55ba6df40d9..1ddd0a776445 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -24,7 +24,7 @@ jobs: with: args: spdx - name: install dependencies - run: sudo apt-get install -y cmake + run: sudo apt-get update && sudo apt-get install -y cmake - name: Create CGAL internal release run: | mkdir -p ./release From c6ae91aca40398e991712d9b256ff56a538fe12f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 26 Feb 2024 11:49:36 +0100 Subject: [PATCH 445/520] reuse.yml: call apt-get update --- .github/workflows/reuse.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index c55ba6df40d9..1ddd0a776445 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -24,7 +24,7 @@ jobs: with: args: spdx - name: install dependencies - run: sudo apt-get install -y cmake + run: sudo apt-get update && sudo apt-get install -y cmake - name: Create CGAL internal release run: | mkdir -p ./release From aec58185bd92e5d087a036b1c6e5dcb47148e7ff Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 26 Feb 2024 14:20:06 +0100 Subject: [PATCH 446/520] re-adding nearest_neighbors as deprecated method in Orthtree --- Orthtree/include/CGAL/Orthtree.h | 30 +++++++++++++++++++ .../include/CGAL/Orthtree/Nearest_neighbors.h | 2 -- Orthtree/include/CGAL/Orthtree_traits_point.h | 2 -- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index aa866510a83d..3cc408ed82ec 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -50,6 +50,7 @@ namespace CGAL { namespace Orthtree_impl { BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) +BOOST_MPL_HAS_XXX_TRAIT_DEF(Squared_distance_of_element) template struct Node_data_wrapper; @@ -124,8 +125,10 @@ class Orthtree { /// @{ #ifndef DOXYGEN_RUNNING static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; + static inline constexpr bool supports_neighbor_search = true;// Orthtree_impl::has_Squared_distance_of_element::value; #else static inline constexpr bool has_data = bool_value; ///< `true` if `GeomTraits` is a model of `OrthtreeTraitswithData` and `false` otherwise. + static inline constexpr bool supports_neighbor_search = bool_value; ///< `true` if `GeomTraits` is a model of `CollectionPartitioningOrthtreeTraits` and `false` otherwise. #endif static constexpr int dimension = Traits::dimension; ///< Dimension of the tree using Kernel = typename Traits::Kernel; ///< Kernel type. @@ -657,6 +660,33 @@ class Orthtree { return node_for_point; } + template + CGAL_DEPRECATED_MSG("you are using the deprecated API, please update your code") + auto nearest_neighbors(const Point& query, + std::size_t k, + OutputIterator output) const -> std::enable_if_t { + Sphere query_sphere(query, (std::numeric_limits::max)()); + + Orthtrees::nearest_k_neighbors_in_radius(*this, query_sphere, k, boost::make_function_output_iterator + ([&](const Traits::Node_data_element index) + {*output++ = get(m_traits.m_point_map, index); })); + + return output; + } + + template + CGAL_DEPRECATED_MSG("you are using the deprecated API, please update your code") + auto nearest_neighbors(const Sphere& query, OutputIterator output) const -> std::enable_if_t { + Sphere query_sphere = query; + + Orthtrees::nearest_k_neighbors_in_radius(*this, query_sphere, + (std::numeric_limits::max)(), boost::make_function_output_iterator + ([&](const Traits::Node_data_element index) + {*output++ = get(m_traits.m_point_map, index); })); + + return output; + } + /*! \brief finds the leaf nodes that intersect with any primitive. diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 913ba2350b9d..1482580357ff 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -14,8 +14,6 @@ #include -#include - namespace CGAL { namespace internal { diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 1e88b36f312e..574a982a1354 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -191,8 +191,6 @@ struct Orthtree_traits_point : public Orthtree_traits_base Date: Mon, 26 Feb 2024 14:18:01 +0100 Subject: [PATCH 447/520] Revert "get rid of extra container" This reverts commit a57800ce07afdfe18429c4922460812b628f38eb. points vector of Triangle_data can be updated when intersecting new intersection points. If the container is used at the same time by another thread calling generate_subtriangles, the container might be in an invalid state while resizing it. --- .../Polygon_mesh_processing/autorefinement.h | 92 ++++++++++--------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index ff9a8ee4d8a3..e9d85736f9d4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -255,12 +255,12 @@ do_coplanar_segments_intersect(std::size_t pi, std::size_t qi, // imported from Intersections_3/include/CGAL/Intersections_3/internal/Triangle_3_Triangle_3_intersection.h template -void coplanar_intersections(const std::vector& t1, - const std::vector& t2, +void coplanar_intersections(const std::array& t1, + const std::array& t2, std::vector>& inter_pts) { - const typename K::Point_3& p1 = t1[0], & q1 = t1[1], & r1 = t1[2]; - const typename K::Point_3& p2 = t2[0], & q2 = t2[1], & r2 = t2[2]; + const typename K::Point_3& p1 = t1[0], q1 = t1[1], r1 = t1[2]; + const typename K::Point_3& p2 = t2[0], q2 = t2[1], r2 = t2[2]; std::list> l_inter_pts; l_inter_pts.push_back(Intersections::internal::Point_on_triangle(0,-1)); @@ -416,8 +416,8 @@ test_edge(const typename K::Point_3& p, const typename K::Point_3& q, } template -bool collect_intersections(const std::vector& t1, - const std::vector& t2, +bool collect_intersections(const std::array& t1, + const std::array& t2, std::vector>& inter_pts) { // test edges of t1 vs t2 @@ -548,9 +548,10 @@ template void generate_subtriangles(std::size_t ti, - std::vector& all_triangle_data, + Triangle_data& triangle_data, const std::set >& intersecting_triangles, const std::set >& coplanar_triangles, + const std::vector>& triangles, PointVector& new_triangles ) { @@ -587,7 +588,6 @@ void generate_subtriangles(std::size_t ti, #define CGAL_AUTOREF_COUNTER_INSTRUCTION(X) #endif - Triangle_data& triangle_data=all_triangle_data[ti]; std::vector& points=triangle_data.points; std::vector& point_locations=triangle_data.point_locations; std::vector>& segments=triangle_data.segments; @@ -680,9 +680,9 @@ void generate_subtriangles(std::size_t ti, { CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer6.start();) typename EK::Point_3 pt = typename EK::Construct_planes_intersection_point_3()( - all_triangle_data[in_triangle_ids[i]].points[0], all_triangle_data[in_triangle_ids[i]].points[1],all_triangle_data[in_triangle_ids[i]].points[2], - all_triangle_data[in_triangle_ids[j]].points[0], all_triangle_data[in_triangle_ids[j]].points[1],all_triangle_data[in_triangle_ids[j]].points[2], - points[0], points[1],points[2]); + triangles[in_triangle_ids[i]][0], triangles[in_triangle_ids[i]][1],triangles[in_triangle_ids[i]][2], + triangles[in_triangle_ids[j]][0], triangles[in_triangle_ids[j]][1],triangles[in_triangle_ids[j]][2], + triangles[ti][0], triangles[ti][1],triangles[ti][2]); CGAL_AUTOREF_COUNTER_INSTRUCTION(counter.timer6.stop();) CGAL_AUTOREF_COUNTER_INSTRUCTION(++counter.c1;) @@ -831,6 +831,7 @@ void generate_subtriangles(std::size_t ti, //typedef CGAL::Constrained_triangulation_plus_2 CDT; typedef CDT_2 CDT; + const std::array& t = triangles[ti]; std::vector vhandles(triangle_data.points.size()); #ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS @@ -838,12 +839,12 @@ void generate_subtriangles(std::size_t ti, bool orientation_flipped = false; CDT cdt(cdt_traits); // TODO: still need to figure out why I can't make the orientation_flipped correctly - vhandles[0]=cdt.insert(points[0]); - vhandles[1]=cdt.insert(points[1]); - vhandles[2]=cdt.insert(points[2]); + vhandles[0]=cdt.insert(t[0]); + vhandles[1]=cdt.insert(t[1]); + vhandles[2]=cdt.insert(t[2]); #else // positive triangle normal - typename EK::Vector_3 n = normal(points[0], points[1], points[2]); + typename EK::Vector_3 n = normal(t[0], t[1], t[2]); typename EK::Point_3 o(CGAL::ORIGIN); bool orientation_flipped = false; @@ -855,14 +856,18 @@ void generate_subtriangles(std::size_t ti, P_traits cdt_traits(n); CDT cdt(cdt_traits); - vhandles[0]=cdt.insert_outside_affine_hull(points[0]); - vhandles[1]=cdt.insert_outside_affine_hull(points[1]); + vhandles[0]=cdt.insert_outside_affine_hull(t[0]); + vhandles[1]=cdt.insert_outside_affine_hull(t[1]); vhandles[2] = cdt.tds().insert_dim_up(cdt.infinite_vertex(), orientation_flipped); - vhandles[2]->set_point(points[2]); + vhandles[2]->set_point(t[2]); #endif // insert points and fill vhandles - // start by points on edges +#if 0 + std::vector indices(triangle_data.points.size()-3); + std::iota(indices.begin(), indices.end(), 3); +#else + //start by points on edges std::array, 3> indices_on_edges; std::vector indices; for (std::size_t i=3; i::type Pmap; typedef Spatial_sort_traits_adapter_2 Search_traits; @@ -1102,17 +1107,24 @@ void autorefine_triangle_soup(PointRange& soup_points, // init the vector of triangles used for the autorefinement of triangles typedef CGAL::Exact_predicates_exact_constructions_kernel EK; + // even if the info is duplicated with Triangle_data::point, we keep this container + // so that we can use it in parallel calls of generate_subtriangles + std::vector< std::array > triangles(tiid+1); // vector of data for refining triangles - std::vector all_triangle_data(tiid+1); + std::vector all_triangle_data(triangles.size()); Cartesian_converter to_exact; for(Input_TID f : intersected_faces) { std::size_t tid=tri_inter_ids[f]; + triangles[tid]= CGAL::make_array( + to_exact( get(pm, soup_points[soup_triangles[f][0]]) ), + to_exact( get(pm, soup_points[soup_triangles[f][1]]) ), + to_exact( get(pm, soup_points[soup_triangles[f][2]]) ) ); all_triangle_data[tid].points.resize(3); - all_triangle_data[tid].points[0]=to_exact( get(pm, soup_points[soup_triangles[f][0]]) ); - all_triangle_data[tid].points[1]=to_exact( get(pm, soup_points[soup_triangles[f][1]]) ); - all_triangle_data[tid].points[2]=to_exact( get(pm, soup_points[soup_triangles[f][2]]) ); + all_triangle_data[tid].points[0]=triangles[tri_inter_ids[f]][0]; + all_triangle_data[tid].points[1]=triangles[tri_inter_ids[f]][1]; + all_triangle_data[tid].points[2]=triangles[tri_inter_ids[f]][2]; all_triangle_data[tid].point_locations.resize(3); all_triangle_data[tid].point_locations[0]=-1; all_triangle_data[tid].point_locations[1]=-2; @@ -1134,8 +1146,8 @@ void autorefine_triangle_soup(PointRange& soup_points, if (i1==-1 || i2==-1) continue; //skip degenerate faces - const std::vector& t1 = all_triangle_data[i1].points; - const std::vector& t2 = all_triangle_data[i2].points; + const std::array& t1 = triangles[i1]; + const std::array& t2 = triangles[i2]; std::vector> inter_pts; bool triangles_are_coplanar = autorefine_impl::collect_intersections(t1, t2, inter_pts); @@ -1303,7 +1315,7 @@ void autorefine_triangle_soup(PointRange& soup_points, #ifdef CGAL_LINKED_WITH_TBB if (parallel_execution) { - tbb::parallel_for(tbb::blocked_range(0, all_triangle_data.size()), + tbb::parallel_for(tbb::blocked_range(0, triangles.size()), [&](const tbb::blocked_range& r) { for (size_t ti = r.begin(); ti != r.end(); ++ti) deduplicate_inserted_segments(ti); @@ -1312,7 +1324,7 @@ void autorefine_triangle_soup(PointRange& soup_points, } else #endif - for (std::size_t ti = 0; ti < all_triangle_data.size(); ++ti) { + for (std::size_t ti = 0; ti < triangles.size(); ++ti) { deduplicate_inserted_segments(ti); } @@ -1333,34 +1345,32 @@ void autorefine_triangle_soup(PointRange& soup_points, #endif #ifdef CGAL_AUTOREF_USE_PROGRESS_DISPLAY - boost::timer::progress_display pd(all_triangle_data.size()); + boost::timer::progress_display pd(triangles.size()); #endif auto refine_triangles = [&](std::size_t ti) { if (all_triangle_data[ti].points.size()==3) - new_triangles.push_back({CGAL::make_array(all_triangle_data[ti].points[0], - all_triangle_data[ti].points[1], - all_triangle_data[ti].points[2]), - ti}); + new_triangles.push_back({triangles[ti], ti}); else { #ifdef CGAL_AUTOREF_USE_FIXED_PROJECTION_TRAITS - typename EK::Vector_3 orth = CGAL::normal(all_triangle_data[ti].points[0], all_triangle_data[ti].points[1], all_triangle_data[ti].points[2]); // TODO::avoid construction? + const std::array& t = triangles[ti]; + typename EK::Vector_3 orth = CGAL::normal(t[0], t[1], t[2]); // TODO::avoid construction? int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1; c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c; if (c == 0) { - autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); } else if (c == 1) { - autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); } else if (c == 2) { - autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); } #else - autorefine_impl::generate_subtriangles(ti, all_triangle_data, intersecting_triangles, coplanar_triangles, new_triangles); + autorefine_impl::generate_subtriangles(ti, all_triangle_data[ti], intersecting_triangles, coplanar_triangles, triangles, new_triangles); #endif } @@ -1375,7 +1385,7 @@ void autorefine_triangle_soup(PointRange& soup_points, #ifdef CGAL_LINKED_WITH_TBB if (parallel_execution) { - tbb::parallel_for(tbb::blocked_range(0, all_triangle_data.size()), + tbb::parallel_for(tbb::blocked_range(0, triangles.size()), [&](const tbb::blocked_range& r) { for (size_t ti = r.begin(); ti != r.end(); ++ti) refine_triangles(ti); @@ -1384,7 +1394,7 @@ void autorefine_triangle_soup(PointRange& soup_points, } else #endif - for (std::size_t ti = 0; ti < all_triangle_data.size(); ++ti) { + for (std::size_t ti = 0; ti < triangles.size(); ++ti) { refine_triangles(ti); } @@ -1441,7 +1451,7 @@ void autorefine_triangle_soup(PointRange& soup_points, } // raw copy of input triangles with no intersection - std::vector tri_inter_ids_inverse(all_triangle_data.size()); + std::vector tri_inter_ids_inverse(triangles.size()); for (Input_TID f=0; f Date: Mon, 26 Feb 2024 14:23:09 +0100 Subject: [PATCH 448/520] missing references --- .../include/CGAL/Polygon_mesh_processing/autorefinement.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h index e9d85736f9d4..8de62f6b8021 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/autorefinement.h @@ -259,8 +259,8 @@ void coplanar_intersections(const std::array& t1, const std::array& t2, std::vector>& inter_pts) { - const typename K::Point_3& p1 = t1[0], q1 = t1[1], r1 = t1[2]; - const typename K::Point_3& p2 = t2[0], q2 = t2[1], r2 = t2[2]; + const typename K::Point_3& p1 = t1[0], &q1 = t1[1], &r1 = t1[2]; + const typename K::Point_3& p2 = t2[0], &q2 = t2[1], &r2 = t2[2]; std::list> l_inter_pts; l_inter_pts.push_back(Intersections::internal::Point_on_triangle(0,-1)); From a628773d0a06f3650e3b251dda5f9753068f5af6 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 26 Feb 2024 15:08:58 +0100 Subject: [PATCH 449/520] ci fix --- Orthtree/include/CGAL/Orthtree.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 3cc408ed82ec..77d95c4c898a 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -668,8 +668,9 @@ class Orthtree { Sphere query_sphere(query, (std::numeric_limits::max)()); Orthtrees::nearest_k_neighbors_in_radius(*this, query_sphere, k, boost::make_function_output_iterator - ([&](const Traits::Node_data_element index) - {*output++ = get(m_traits.m_point_map, index); })); + ([&](const typename Traits::Node_data_element index) { + *output++ = get(m_traits.m_point_map, index); + })); return output; } @@ -681,8 +682,9 @@ class Orthtree { Orthtrees::nearest_k_neighbors_in_radius(*this, query_sphere, (std::numeric_limits::max)(), boost::make_function_output_iterator - ([&](const Traits::Node_data_element index) - {*output++ = get(m_traits.m_point_map, index); })); + ([&](const typename Traits::Node_data_element index) { + *output++ = get(m_traits.m_point_map, index); + })); return output; } From 9e6fefa2737092f61140f94065fab09cf7916996 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 26 Feb 2024 15:44:38 +0100 Subject: [PATCH 450/520] missing header --- Orthtree/include/CGAL/Orthtree.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 77d95c4c898a..9b209bdd74f1 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include From a3a102c10106ed6ddcc5a9fcbd0635de89ec5ffe Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 26 Feb 2024 16:23:19 +0100 Subject: [PATCH 451/520] more missing headers --- Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 1482580357ff..33dec24c9042 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -13,6 +13,10 @@ #define ORTHTREE_NEAREST_NEIGHBORS_H #include +#include +#include +#include +#include namespace CGAL { From 99104e5699bd215155e1cfccb06b518e5ea880b9 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 27 Feb 2024 09:24:36 +0100 Subject: [PATCH 452/520] replaced call to CGAL::squared_distance --- .../include/CGAL/Orthtree/Nearest_neighbors.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index 33dec24c9042..a5b8f20bb97c 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -14,9 +14,8 @@ #include #include -#include -#include -#include +#include +#include namespace CGAL { @@ -88,10 +87,18 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, for (int i = 0; i < Tree::degree; ++i) { auto child_node = orthtree.child(node, i); + Orthtrees::internal::Cartesian_ranges cartesian_range; + + typename Tree::FT squared_distance = 0; + for (const auto& r : cartesian_range(orthtree.traits().construct_center_3_object()(search_bounds), orthtree.barycenter(child_node))) { + typename Tree::FT d = (get<0>(r) - get<1>(r)); + squared_distance += d * d; + } + // Add a child to the list, with its distance children_with_distances.emplace_back( child_node, - CGAL::squared_distance(orthtree.traits().construct_center_3_object()(search_bounds), orthtree.barycenter(child_node)) + squared_distance ); } From 84d17b6099bbd99e28203c0a80dfd1b6b11317d5 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 27 Feb 2024 10:26:50 +0100 Subject: [PATCH 453/520] renaming _3 functors in CollectionPartitioningOrthtreeTraits to _d --- .../CollectionPartitioningOrthtreeTraits.h | 18 +++++++++--------- .../include/CGAL/Orthtree/Nearest_neighbors.h | 10 +++++----- Orthtree/include/CGAL/Orthtree_traits_point.h | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h index 1bb763500cc5..97068f40a978 100644 --- a/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/CollectionPartitioningOrthtreeTraits.h @@ -54,7 +54,7 @@ class CollectionPartitioningOrthtreeTraits { * Provides the operator: * `Sphere_d operator()(const Point_d&, const FT&)` */ - using Construct_sphere_3 = unspecified_type; + using Construct_sphere_d = unspecified_type; /*! * \brief Functor with an operator that provides the center of a `Sphere_d`. @@ -62,7 +62,7 @@ class CollectionPartitioningOrthtreeTraits { * Provides the operator: * `Point_d operator()(const Sphere_d&)` */ - using Construct_center_3 = unspecified_type; + using Construct_center_d = unspecified_type; /*! * \brief Functor with an operator that provides the squared radius of a `Sphere_d`. @@ -70,7 +70,7 @@ class CollectionPartitioningOrthtreeTraits { * Provides the operator: * `FT operator()(const Sphere_d&)` */ - using Compute_squared_radius_3 = unspecified_type; + using Compute_squared_radius_d = unspecified_type; /// @} @@ -78,19 +78,19 @@ class CollectionPartitioningOrthtreeTraits { /// @{ /*! - * constructs an object of type `ConstructSphere_3`. + * constructs an object of type `ConstructSphere_d`. */ - Construct_sphere_3 construct_sphere_3_object() const; + Construct_sphere_d construct_sphere_d_object() const; /*! - * constructs an object of type `ConstructCenter_3`. + * constructs an object of type `ConstructCenter_d`. */ - Construct_center_3 construct_center_3_object() const; + Construct_center_d construct_center_d_object() const; /*! - * constructs an object of type `ComputeSquaredRadius_3`. + * constructs an object of type `ComputeSquaredRadius_d`. */ - Compute_squared_radius_3 compute_squared_radius_3_object() const; + Compute_squared_radius_d compute_squared_radius_d_object() const; /*! * constructs an object of type `Squared_distance_of_element`. diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h index a5b8f20bb97c..7c59f1bf7dd0 100644 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h @@ -39,10 +39,10 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, // Pair that element with its distance from the search point Result current_element_with_distance = - {e, orthtree.traits().squared_distance_of_element_object()(e, orthtree.traits().construct_center_3_object()(search_bounds))}; + {e, orthtree.traits().squared_distance_of_element_object()(e, orthtree.traits().construct_center_d_object()(search_bounds))}; // Check if the new element is within the bounds - if (current_element_with_distance.distance < orthtree.traits().compute_squared_radius_3_object()(search_bounds)) { + if (current_element_with_distance.distance < orthtree.traits().compute_squared_radius_d_object()(search_bounds)) { // Check if the results list is full if (results.size() == results.capacity()) { @@ -63,7 +63,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, if (results.size() == results.capacity()) { // Set the search radius - search_bounds = orthtree.traits().construct_sphere_3_object()(orthtree.traits().construct_center_3_object()(search_bounds), results.back().distance + epsilon); + search_bounds = orthtree.traits().construct_sphere_d_object()(orthtree.traits().construct_center_d_object()(search_bounds), results.back().distance + epsilon); } } } @@ -90,7 +90,7 @@ void nearest_k_neighbors_recursive(const Tree& orthtree, Orthtrees::internal::Cartesian_ranges cartesian_range; typename Tree::FT squared_distance = 0; - for (const auto& r : cartesian_range(orthtree.traits().construct_center_3_object()(search_bounds), orthtree.barycenter(child_node))) { + for (const auto& r : cartesian_range(orthtree.traits().construct_center_d_object()(search_bounds), orthtree.barycenter(child_node))) { typename Tree::FT d = (get<0>(r) - get<1>(r)); squared_distance += d * d; } @@ -191,7 +191,7 @@ template OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Point& query, std::size_t k, OutputIterator output) { - typename Tree::Sphere query_sphere = orthtree.traits().construct_sphere_3_object()(query, (std::numeric_limits::max)()); + typename Tree::Sphere query_sphere = orthtree.traits().construct_sphere_d_object()(query, (std::numeric_limits::max)()); return nearest_k_neighbors_in_radius(orthtree, query_sphere, k, output); } diff --git a/Orthtree/include/CGAL/Orthtree_traits_point.h b/Orthtree/include/CGAL/Orthtree_traits_point.h index 574a982a1354..84e95bde81c0 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_point.h +++ b/Orthtree/include/CGAL/Orthtree_traits_point.h @@ -167,19 +167,19 @@ struct Orthtree_traits_point : public Orthtree_traits_base typename Self::Sphere_d { return typename Self::Sphere_d(center, squared_radius); }; } - auto construct_center_3_object() const { + auto construct_center_d_object() const { return [](const typename Self::Sphere_d& sphere) -> typename Self::Point_d { return sphere.center(); }; } - auto compute_squared_radius_3_object() const { + auto compute_squared_radius_d_object() const { return [](const typename Self::Sphere_d& sphere) -> typename Self::FT { return sphere.squared_radius(); }; From 27cb1144d69d7fc7bcdd788331d0ad11565fba18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 27 Feb 2024 11:53:30 +0100 Subject: [PATCH 454/520] be compatible with non c++17 compilers --- .../test/Segment_Delaunay_graph_2/issue7972.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp b/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp index 6dd4a4e5e2b4..dc320717cf9e 100644 --- a/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp +++ b/Segment_Delaunay_graph_2/test/Segment_Delaunay_graph_2/issue7972.cpp @@ -7,15 +7,13 @@ typedef CGAL::Exact_predicates_exact_constructions_kernel K; typedef CGAL::Segment_Delaunay_graph_traits_2 Gt; typedef CGAL::Segment_Delaunay_graph_2 SDG2; -int main() { - auto segments = std::vector({ - CGAL::Segment_2( - CGAL::Point_2(0.0, 0.0), - CGAL::Point_2(1.0, 0.0)) - }); +int main() +{ + std::vector> segments; + segments.emplace_back(CGAL::Point_2(0.0, 0.0),CGAL::Point_2(1.0, 0.0)); - SDG2 delaunay; - delaunay.insert_segments(segments.begin(), segments.end()); + SDG2 delaunay; + delaunay.insert_segments(segments.begin(), segments.end()); - return 0; + return 0; } From 3251248b68c8b813c8d2a50d88f0b9ba086f4f47 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 27 Feb 2024 18:01:53 +0100 Subject: [PATCH 455/520] reintegrated nearest neighbors into Orthtree --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- Orthtree/doc/Orthtree/PackageDescription.txt | 2 - .../Orthtree/octree_find_nearest_neighbor.cpp | 46 +++- Orthtree/include/CGAL/Orthtree.h | 183 +++++++++++++-- .../include/CGAL/Orthtree/Nearest_neighbors.h | 221 ------------------ .../Orthtree/test_octree_nearest_neighbor.cpp | 5 +- 6 files changed, 209 insertions(+), 250 deletions(-) delete mode 100644 Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 726922244a74..97991439db1f 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -213,7 +213,7 @@ Results are put in a vector, and then printed. Not all octrees are compatible with nearest neighbor functionality, as the idea of a nearest neighbor may not make sense for some tree contents. -For the nearest neighbor functions to work, the traits class must implement the +For the nearest neighbor methods to work, the traits class must implement the `CollectionPartitioningOrthtreeTraits` concept. \subsection Section_Orthtree_Grade Grading diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index 4e8cad96e92c..c1baeeae8494 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -67,6 +67,4 @@ Quadtree, Octree and Orthtree Reference - `CGAL::Orthtrees::Leaves_traversal` - `CGAL::Orthtrees::Level_traversal` -\cgalCRPSection{Neighbor search} -- \link PkgOrthtreeNeighbors `CGAL::Orthtrees::nearest_neighbors` \endlink */ diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index 0d457c5e24d0..9c9986fe2a17 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -45,14 +44,43 @@ int main(int argc, char** argv) { {-0.46026, -0.25353, 0.32051}, {-0.460261, -0.253533, 0.320513} }; - for (const Point& p: points_to_find) - CGAL::Orthtrees::nearest_neighbors( - octree, - p, 1, // k=1 to find the single closest point - boost::make_function_output_iterator([&](const Point_set::Index& nearest) { - std::cout << "the nearest point to (" << p << ") is (" << points.point(nearest) << ")" << std::endl; - }) - ); + typename Octree::Sphere s(Point(0, -0.1, 0), 5.0); + + for (const Point& p : points_to_find) + octree.nearest_neighbors + (p, 1, // k=1 to find the single closest point + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << "the nearest point to (" << p << + ") is (" << points.point(nearest) << ")" << std::endl; + })); + + + std::cout << std::endl << "Closest points within sphere" << std::endl; +/* + for (const Point& p : points_to_find) + octree.nearest_neighbors + (s, + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << points.point(nearest) << std::endl; + }));*/ + + std::cout << std::endl << "Closest points within sphere" << std::endl; + for (const Point& p : points_to_find) { + std::cout << "The up to three closest points to (" << p << + ") within a squared radius of " << s.squared_radius() << " are:" << std::endl; + octree.nearest_k_neighbors_in_radius + (s, 3, + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << "(" << points.point(nearest) << ")" << std::endl; + })); + std::cout << std::endl; + } return EXIT_SUCCESS; } diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9b209bdd74f1..ae662c94c36c 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -15,7 +15,6 @@ #include #include -#include #include #include #include @@ -661,31 +660,89 @@ class Orthtree { return node_for_point; } + /*! + \brief finds the `k` nearest neighbors of the point `query`. + + Nearest neighbors are outputted in order of increasing distance to `query`. + + \tparam OutputIterator a model of `OutputIterator` that accepts `GeomTraits::Node_data_element` objects. + + \param query query point + \param k number of neighbors to find + \param output output iterator + + \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. + */ template - CGAL_DEPRECATED_MSG("you are using the deprecated API, please update your code") auto nearest_neighbors(const Point& query, std::size_t k, OutputIterator output) const -> std::enable_if_t { Sphere query_sphere(query, (std::numeric_limits::max)()); - Orthtrees::nearest_k_neighbors_in_radius(*this, query_sphere, k, boost::make_function_output_iterator - ([&](const typename Traits::Node_data_element index) { - *output++ = get(m_traits.m_point_map, index); - })); - - return output; + return nearest_k_neighbors_in_radius(query_sphere, k, output); } + /*! + \brief finds the elements in the sphere `query`. + + Elements are outputted in order of increasing distance to + the center of the sphere. + + \tparam OutputIterator a model of `OutputIterator` that accepts `GeomTraits::Node_data_element` objects. + + \param query query sphere + \param output output iterator + + \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. + */ template - CGAL_DEPRECATED_MSG("you are using the deprecated API, please update your code") auto nearest_neighbors(const Sphere& query, OutputIterator output) const -> std::enable_if_t { Sphere query_sphere = query; - Orthtrees::nearest_k_neighbors_in_radius(*this, query_sphere, - (std::numeric_limits::max)(), boost::make_function_output_iterator - ([&](const typename Traits::Node_data_element index) { - *output++ = get(m_traits.m_point_map, index); - })); + return nearest_k_neighbors_in_radius(query_sphere, (std::numeric_limits::max)(), output); + } + + /*! + \brief finds at most `k` elements within a specific radius that are + nearest to the center of the sphere `query`: if `query` does not contain + at least `k` elements, only contained elements will be returned. + + This function is useful when the user already knows how sparse the elements are, + or if they do not care about elements that are too far away. + Setting a small radius may have performance benefits. + + \tparam OutputIterator must be a model of `OutputIterator` that accepts `GeomTraits::Node_data_element` + + \param query the region to search within + \param k the number of elements to find + \param output the output iterator to add the found elements to (in order of increasing distance) + + \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. + */ + template + auto nearest_k_neighbors_in_radius( + Sphere& query, + std::size_t k, + OutputIterator output + ) const -> std::enable_if_t { + + // todo: this type is over-constrained, this must be made more generic + struct Node_element_with_distance { + typename Traits::Node_data_element element; + FT distance; + }; + + // Create an empty list of elements + std::vector element_list; + if (k != (std::numeric_limits::max)()) + element_list.reserve(k); + + // Invoking the recursive function adds those elements to the vector (passed by reference) + nearest_k_neighbors_recursive(query, root(), element_list); + + // Add all the points found to the output + for (auto& item : element_list) + *output++ = item.element; return output; } @@ -1232,6 +1289,104 @@ class Orthtree { return output; } + template + auto nearest_k_neighbors_recursive( + Sphere& search_bounds, + Node_index node, + std::vector& results, + FT epsilon = 0) const -> std::enable_if_t { + + // Check whether the node has children + if (is_leaf(node)) { + + // Base case: the node has no children + + // Loop through each of the elements contained by the node + // Note: there might be none, and that should be fine! + for (auto& e : data(node)) { + + // Pair that element with its distance from the search point + Result current_element_with_distance = + { e, m_traits.squared_distance_of_element_object()(e, m_traits.construct_center_d_object()(search_bounds)) }; + + // Check if the new element is within the bounds + if (current_element_with_distance.distance < m_traits.compute_squared_radius_d_object()(search_bounds)) { + + // Check if the results list is full + if (results.size() == results.capacity()) { + + // Delete a element if we need to make room + results.pop_back(); + } + + // Add the new element + results.push_back(current_element_with_distance); + + // Sort the list + std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { + return left.distance < right.distance; + }); + + // Check if the results list is full + if (results.size() == results.capacity()) { + + // Set the search radius + search_bounds = m_traits.construct_sphere_d_object()(m_traits.construct_center_d_object()(search_bounds), results.back().distance + epsilon); + } + } + } + } + else { + + struct Node_index_with_distance { + Node_index index; + FT distance; + + Node_index_with_distance(const Node_index& index, FT distance) : + index(index), distance(distance) {} + }; + + // Recursive case: the node has children + + // Create a list to map children to their distances + std::vector children_with_distances; + children_with_distances.reserve(Self::degree); + + // Fill the list with child nodes + for (int i = 0; i < Self::degree; ++i) { + auto child_node = child(node, i); + + FT squared_distance = 0; + for (const auto& r : cartesian_range(m_traits.construct_center_d_object()(search_bounds), barycenter(child_node))) { + FT d = (get<0>(r) - get<1>(r)); + squared_distance += d * d; + } + + // Add a child to the list, with its distance + children_with_distances.emplace_back( + child_node, + squared_distance + ); + } + + // Sort the children by their distance from the search point + std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto& left, auto& right) { + return left.distance < right.distance; + }); + + // Loop over the children + for (auto child_with_distance : children_with_distances) { + + // Check whether the bounding box of the child intersects with the search bounds + if (CGAL::do_intersect(bbox(child_with_distance.index), search_bounds)) { + + // Recursively invoke this function + nearest_k_neighbors_recursive(search_bounds, child_with_distance.index, results); + } + } + } + } + public: /// \cond SKIP_IN_MANUAL diff --git a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h b/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h deleted file mode 100644 index 7c59f1bf7dd0..000000000000 --- a/Orthtree/include/CGAL/Orthtree/Nearest_neighbors.h +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2023 INRIA -// 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) : Jackson Campolattaro - -#ifndef ORTHTREE_NEAREST_NEIGHBORS_H -#define ORTHTREE_NEAREST_NEIGHBORS_H - -#include -#include -#include -#include - -namespace CGAL { - -namespace internal { - -template -void nearest_k_neighbors_recursive(const Tree& orthtree, - typename Tree::Sphere& search_bounds, - typename Tree::Node_index node, - std::vector& results, - typename Tree::FT epsilon = 0) { - - // Check whether the node has children - if (orthtree.is_leaf(node)) { - - // Base case: the node has no children - - // Loop through each of the elements contained by the node - // Note: there might be none, and that should be fine! - for (auto& e: orthtree.data(node)) { - - // Pair that element with its distance from the search point - Result current_element_with_distance = - {e, orthtree.traits().squared_distance_of_element_object()(e, orthtree.traits().construct_center_d_object()(search_bounds))}; - - // Check if the new element is within the bounds - if (current_element_with_distance.distance < orthtree.traits().compute_squared_radius_d_object()(search_bounds)) { - - // Check if the results list is full - if (results.size() == results.capacity()) { - - // Delete a element if we need to make room - results.pop_back(); - } - - // Add the new element - results.push_back(current_element_with_distance); - - // Sort the list - std::sort(results.begin(), results.end(), [=](auto& left, auto& right) { - return left.distance < right.distance; - }); - - // Check if the results list is full - if (results.size() == results.capacity()) { - - // Set the search radius - search_bounds = orthtree.traits().construct_sphere_d_object()(orthtree.traits().construct_center_d_object()(search_bounds), results.back().distance + epsilon); - } - } - } - } else { - - struct Node_index_with_distance { - typename Tree::Node_index index; - typename Tree::FT distance; - - Node_index_with_distance(const typename Tree::Node_index& index, const typename Tree::FT& distance) : - index(index), distance(distance) {} - }; - - // Recursive case: the node has children - - // Create a list to map children to their distances - std::vector children_with_distances; - children_with_distances.reserve(Tree::degree); - - // Fill the list with child nodes - for (int i = 0; i < Tree::degree; ++i) { - auto child_node = orthtree.child(node, i); - - Orthtrees::internal::Cartesian_ranges cartesian_range; - - typename Tree::FT squared_distance = 0; - for (const auto& r : cartesian_range(orthtree.traits().construct_center_d_object()(search_bounds), orthtree.barycenter(child_node))) { - typename Tree::FT d = (get<0>(r) - get<1>(r)); - squared_distance += d * d; - } - - // Add a child to the list, with its distance - children_with_distances.emplace_back( - child_node, - squared_distance - ); - } - - // Sort the children by their distance from the search point - std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto& left, auto& right) { - return left.distance < right.distance; - }); - - // Loop over the children - for (auto child_with_distance: children_with_distances) { - - // Check whether the bounding box of the child intersects with the search bounds - if (CGAL::do_intersect(orthtree.bbox(child_with_distance.index), search_bounds)) { - - // Recursively invoke this function - CGAL::internal::nearest_k_neighbors_recursive(orthtree, search_bounds, child_with_distance.index, results); - } - } - } -} - -} - -namespace Orthtrees { - -/*! - \ingroup PkgOrthtreeNeighbors - \brief finds at most `k` elements within a specific radius that are - nearest to the center of the sphere `query`: if `query` does not contain - at least `k` elements, only contained elements will be returned. - - This function is useful when the user already knows how sparse the elements are, - or if they do not care about elements that are too far away. - Setting a small radius may have performance benefits. - - \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` - \tparam OutputIterator must be a model of `OutputIterator` that accepts `GT::Node_data_element` - - \param orthtree the tree to search within - \param query the region to search within - \param k the number of elements to find - \param output the output iterator to add the found elements to (in order of increasing distance) - */ -template -OutputIterator nearest_k_neighbors_in_radius( - const Tree& orthtree, - typename Tree::Sphere& query, - std::size_t k, - OutputIterator output -) { - - // todo: this type is over-constrained, this must be made more generic - struct Node_element_with_distance { - typename Tree::Traits::Node_data_element element; - typename Tree::FT distance; - }; - - // Create an empty list of elements - std::vector element_list; - if (k != (std::numeric_limits::max)()) - element_list.reserve(k); - - // Invoking the recursive function adds those elements to the vector (passed by reference) - CGAL::internal::nearest_k_neighbors_recursive(orthtree, query, orthtree.root(), element_list); - - // Add all the points found to the output - for (auto& item: element_list) - *output++ = item.element; - - return output; -} - - -/*! - \ingroup PkgOrthtreeNeighbors - \brief finds the `k` nearest neighbors of the point `query`. - - Nearest neighbors are outputted in order of increasing distance to - `query`. - - \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` - \tparam OutputIterator a model of `OutputIterator` that accepts `GT::Node_data_element` objects. - - \param orthtree the tree to search within - \param query query point - \param k number of neighbors to find - \param output output iterator - */ -template -OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Point& query, - std::size_t k, - OutputIterator output) { - typename Tree::Sphere query_sphere = orthtree.traits().construct_sphere_d_object()(query, (std::numeric_limits::max)()); - return nearest_k_neighbors_in_radius(orthtree, query_sphere, k, output); -} - -/*! - \ingroup PkgOrthtreeNeighbors - \brief finds the elements in the sphere `query`. - - Elements are outputted in order of increasing distance to - the center of the sphere. - - \tparam Tree must be `Orthtree` with `GT` being a model of `CollectionPartitioningOrthtreeTraits` - \tparam OutputIterator a model of `OutputIterator` that accepts `GT::Node_data_element` objects. - - \param orthtree the tree to search within - \param query query sphere - \param output output iterator - */ -template -OutputIterator nearest_neighbors(const Tree& orthtree, const typename Tree::Sphere& query, OutputIterator output) { - typename Tree::Sphere query_sphere = query; - return nearest_k_neighbors_in_radius(orthtree, query_sphere, (std::numeric_limits::max)(), output); -} - -} -} - -#endif //ORTHTREE_NEAREST_NEIGHBORS_H diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index e558ebbfe6d7..72b423199ff4 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -72,7 +71,7 @@ void naive_vs_octree(std::size_t dataset_size) { auto octree_start_time = high_resolution_clock::now(); { std::vector k_neighbors; - CGAL::Orthtrees::nearest_neighbors(octree, random_point, 1, std::back_inserter(k_neighbors)); + octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); octree_nearest = get(points.point_map(), *k_neighbors.begin()); } duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; @@ -122,7 +121,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { Octree octree({points, points.point_map()}); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); - CGAL::Orthtrees::nearest_neighbors(octree, random_point, K, std::back_inserter(octree_nearest_neighbors)); + octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; std::cout << "Octree --> " From 08a7a66991eae35c8a530df640a0d8fde967d7b3 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 28 Feb 2024 10:28:19 +0100 Subject: [PATCH 456/520] bugfix nearest neighbor search --- .../Orthtree/octree_find_nearest_neighbor.cpp | 43 ++++++++----------- Orthtree/include/CGAL/Orthtree.h | 19 ++++---- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index 9c9986fe2a17..3ce7056424da 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -44,7 +44,6 @@ int main(int argc, char** argv) { {-0.46026, -0.25353, 0.32051}, {-0.460261, -0.253533, 0.320513} }; - typename Octree::Sphere s(Point(0, -0.1, 0), 5.0); for (const Point& p : points_to_find) octree.nearest_neighbors @@ -56,31 +55,25 @@ int main(int argc, char** argv) { ") is (" << points.point(nearest) << ")" << std::endl; })); + typename Octree::Sphere s(points_to_find[0], 0.0375); + std::cout << std::endl << "Closest points within the sphere around " << s.center() << " with squared radius of " << s.squared_radius() << ":" << std::endl; + octree.nearest_neighbors + (s, + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << points.point(nearest) << " dist: " << (Point(0, 0, 0) - points.point(nearest)).squared_length() << std::endl; + })); - std::cout << std::endl << "Closest points within sphere" << std::endl; -/* - for (const Point& p : points_to_find) - octree.nearest_neighbors - (s, - boost::make_function_output_iterator - ([&](const Point_set::Index& nearest) - { - std::cout << points.point(nearest) << std::endl; - }));*/ - - std::cout << std::endl << "Closest points within sphere" << std::endl; - for (const Point& p : points_to_find) { - std::cout << "The up to three closest points to (" << p << - ") within a squared radius of " << s.squared_radius() << " are:" << std::endl; - octree.nearest_k_neighbors_in_radius - (s, 3, - boost::make_function_output_iterator - ([&](const Point_set::Index& nearest) - { - std::cout << "(" << points.point(nearest) << ")" << std::endl; - })); - std::cout << std::endl; - } + std::size_t k = 3; + std::cout << std::endl << "The up to " << k << " closest points to(" << s.center() << ") within a squared radius of " << s.squared_radius() << " are:" << std::endl; + octree.nearest_k_neighbors_in_radius + (s, k, + boost::make_function_output_iterator + ([&](const Point_set::Index& nearest) + { + std::cout << "(" << points.point(nearest) << " dist: " << (Point(0, 0, 0) - points.point(nearest)).squared_length() << std::endl; + })); return EXIT_SUCCESS; } diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ae662c94c36c..552026d0b3fb 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -678,6 +678,7 @@ class Orthtree { std::size_t k, OutputIterator output) const -> std::enable_if_t { Sphere query_sphere(query, (std::numeric_limits::max)()); + CGAL_precondition(k > 0); return nearest_k_neighbors_in_radius(query_sphere, k, output); } @@ -697,9 +698,7 @@ class Orthtree { */ template auto nearest_neighbors(const Sphere& query, OutputIterator output) const -> std::enable_if_t { - Sphere query_sphere = query; - - return nearest_k_neighbors_in_radius(query_sphere, (std::numeric_limits::max)(), output); + return nearest_k_neighbors_in_radius(query, (std::numeric_limits::max)(), output); } /*! @@ -721,10 +720,12 @@ class Orthtree { */ template auto nearest_k_neighbors_in_radius( - Sphere& query, + const Sphere& query, std::size_t k, OutputIterator output ) const -> std::enable_if_t { + CGAL_precondition(k > 0); + Sphere query_sphere = query; // todo: this type is over-constrained, this must be made more generic struct Node_element_with_distance { @@ -738,7 +739,7 @@ class Orthtree { element_list.reserve(k); // Invoking the recursive function adds those elements to the vector (passed by reference) - nearest_k_neighbors_recursive(query, root(), element_list); + nearest_k_neighbors_recursive(query_sphere, root(), element_list, k); // Add all the points found to the output for (auto& item : element_list) @@ -1294,6 +1295,7 @@ class Orthtree { Sphere& search_bounds, Node_index node, std::vector& results, + std::size_t k, FT epsilon = 0) const -> std::enable_if_t { // Check whether the node has children @@ -1313,8 +1315,7 @@ class Orthtree { if (current_element_with_distance.distance < m_traits.compute_squared_radius_d_object()(search_bounds)) { // Check if the results list is full - if (results.size() == results.capacity()) { - + if (results.size() == k) { // Delete a element if we need to make room results.pop_back(); } @@ -1328,7 +1329,7 @@ class Orthtree { }); // Check if the results list is full - if (results.size() == results.capacity()) { + if (results.size() == k) { // Set the search radius search_bounds = m_traits.construct_sphere_d_object()(m_traits.construct_center_d_object()(search_bounds), results.back().distance + epsilon); @@ -1381,7 +1382,7 @@ class Orthtree { if (CGAL::do_intersect(bbox(child_with_distance.index), search_bounds)) { // Recursively invoke this function - nearest_k_neighbors_recursive(search_bounds, child_with_distance.index, results); + nearest_k_neighbors_recursive(search_bounds, child_with_distance.index, results, k); } } } From b4e378e72141672b958290a7642893c71c1be202 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 28 Feb 2024 11:53:03 +0100 Subject: [PATCH 457/520] next release from this branch will be 5.5.5 --- Installation/include/CGAL/version.h | 4 ++-- Installation/lib/cmake/CGAL/CGALConfigVersion.cmake | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Installation/include/CGAL/version.h b/Installation/include/CGAL/version.h index 68a5584b6e4d..d59219aa9a1d 100644 --- a/Installation/include/CGAL/version.h +++ b/Installation/include/CGAL/version.h @@ -17,10 +17,10 @@ #define CGAL_VERSION_H #ifndef SWIG -#define CGAL_VERSION 5.5.4 +#define CGAL_VERSION 5.5.5 #define CGAL_GIT_HASH abcdef #endif -#define CGAL_VERSION_NR 1050541000 +#define CGAL_VERSION_NR 1050551000 #define CGAL_SVN_REVISION 99999 #define CGAL_RELEASE_DATE 20220531 diff --git a/Installation/lib/cmake/CGAL/CGALConfigVersion.cmake b/Installation/lib/cmake/CGAL/CGALConfigVersion.cmake index 5aa354fe65f3..56d9c6768313 100644 --- a/Installation/lib/cmake/CGAL/CGALConfigVersion.cmake +++ b/Installation/lib/cmake/CGAL/CGALConfigVersion.cmake @@ -1,8 +1,8 @@ set(CGAL_MAJOR_VERSION 5) set(CGAL_MINOR_VERSION 5) -set(CGAL_BUGFIX_VERSION 4) +set(CGAL_BUGFIX_VERSION 5) include(${CMAKE_CURRENT_LIST_DIR}/CGALConfigBuildVersion.cmake) -set(CGAL_VERSION_PUBLIC_RELEASE_VERSION "5.5.4") +set(CGAL_VERSION_PUBLIC_RELEASE_VERSION "5.5.5") set(CGAL_VERSION_PUBLIC_RELEASE_NAME "CGAL-${CGAL_VERSION_PUBLIC_RELEASE_VERSION}") if (CGAL_BUGFIX_VERSION AND CGAL_BUGFIX_VERSION GREATER 0) From d68ef56d8d830f19a4429d5781891c1e229b1999 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 28 Feb 2024 15:19:40 +0100 Subject: [PATCH 458/520] update the documentation menu --- Documentation/doc/resources/1.10.0/menu_version.js | 4 ++-- Documentation/doc/resources/1.8.13/menu_version.js | 4 ++-- Documentation/doc/resources/1.9.6/menu_version.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/doc/resources/1.10.0/menu_version.js b/Documentation/doc/resources/1.10.0/menu_version.js index f3b20c2fe004..5b8d97efba0b 100644 --- a/Documentation/doc/resources/1.10.0/menu_version.js +++ b/Documentation/doc/resources/1.10.0/menu_version.js @@ -7,9 +7,9 @@ var all_versions = [ 'master', '6.0-beta1', - '5.6', + '5.6.1', 'latest', - '5.5.2', + '5.5.4', '5.4.4', '5.3.2', '5.2.4', diff --git a/Documentation/doc/resources/1.8.13/menu_version.js b/Documentation/doc/resources/1.8.13/menu_version.js index f3b20c2fe004..5b8d97efba0b 100644 --- a/Documentation/doc/resources/1.8.13/menu_version.js +++ b/Documentation/doc/resources/1.8.13/menu_version.js @@ -7,9 +7,9 @@ var all_versions = [ 'master', '6.0-beta1', - '5.6', + '5.6.1', 'latest', - '5.5.2', + '5.5.4', '5.4.4', '5.3.2', '5.2.4', diff --git a/Documentation/doc/resources/1.9.6/menu_version.js b/Documentation/doc/resources/1.9.6/menu_version.js index f3b20c2fe004..5b8d97efba0b 100644 --- a/Documentation/doc/resources/1.9.6/menu_version.js +++ b/Documentation/doc/resources/1.9.6/menu_version.js @@ -7,9 +7,9 @@ var all_versions = [ 'master', '6.0-beta1', - '5.6', + '5.6.1', 'latest', - '5.5.2', + '5.5.4', '5.4.4', '5.3.2', '5.2.4', From 3bd448d1a88a21ccbcc99ef5a9499338a712bad6 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 28 Feb 2024 14:28:00 +0000 Subject: [PATCH 459/520] Polygon: Avoid stackoverflow when summing exact numbers --- Polygon/include/CGAL/Polygon_2_algorithms.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Polygon/include/CGAL/Polygon_2_algorithms.h b/Polygon/include/CGAL/Polygon_2_algorithms.h index 5c836466c115..dc8f9c8708f5 100644 --- a/Polygon/include/CGAL/Polygon_2_algorithms.h +++ b/Polygon/include/CGAL/Polygon_2_algorithms.h @@ -24,6 +24,7 @@ #include #include #include +#include #include /// @@ -153,6 +154,7 @@ area_2( ForwardIterator first, ForwardIterator last, ForwardIterator third = second; while (++third != last) { result = result + compute_area_2(*first, *second, *third); + exact(result); second = third; } } @@ -190,6 +192,7 @@ polygon_area_2( ForwardIterator first, ForwardIterator last, ForwardIterator third = second; while (++third != last) { result = result + compute_area_2(*first, *second, *third); + exact(result); second = third; } return result; From b3b9bf0d98c06f37fc781c07c10879d97499794b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 28 Feb 2024 15:49:30 +0100 Subject: [PATCH 460/520] doc section on migration --- Orthtree/doc/Orthtree/Orthtree.txt | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 97991439db1f..4e72839501d0 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -272,6 +272,64 @@ purposes. For nontrivial point counts, the naive approach's calculation time dwarfs that of either the %orthtree or kd-tree. +\section Section_Orthtree_Migration Migration from CGAL 5.6 + +The orthtree traits changed to allow for custom data stored per node in the orthtree. To migrate existing code from CGAL 5.6 Orthtree_traits_point can be used for a point-based orthtrees. The aliases `CGAL::Quadtree` and `CGAL::Octree` have been extended by a boolean template parameter to allow for non-cubic cells, which is the default. The data is passed via the traits class and no longer directly into the orthtree. + +CGAL 5.6 code to declare and define an Octree with cubic cells: +\code{.cpp} +typedef CGAL::Point_set_3 Point_set; +typedef CGAL::Octree Octree; +... +Octree octree(points, points.point_map()); +\endcode + +CGAL 6.0 code with identical behavior: +\code{.cpp} +typedef CGAL::Point_set_3 Point_set; +typedef CGAL::Octree Octree; +... +Octree octree({points, points.point_map()}); +\endcode + +The node class does not exist anymore and has been replaced by the lightweight type Node_index. All information formerly contained in the node class is now accessible via the Orthtree interface and stored using the new property maps. + +CGAL 5.6 exemplary node access: +\code{.cpp} +Orthtree::Node root = orthtree.root(); +Orthtree::Node child = root[0]; +bool is_leaf = child.is_leaf(); + +for (Orthtree::Node::const_iterator it = child.begin(); it != child.end(); it++) + ... +\endcode + +CGAL 6.0 exemplary node access: +\code{.cpp} +Orthtree::Node_index root = orthtree.root(); +Orthtree::Node_index child = orthtree.child(root, 0); +bool is_leaf = orthtree.is_leaf(child); + +for (const Orthtree::Traits::Node_data_element &idx : orthtree.data(child)) + ... // may require the use of a point map to retrieve the point from idx +\endcode + +The nearest neighbor search behaves as before, however, the output iterator will be required to store `CollectionPartitioningOrthtreeTraits::Node_data_element`. This may be the actual point or, e.g., in case a `Point_set_3` is used, an index which requires the use of a point map to retrieve the point. + +The provided traversals, i.e., CGAL::Orthtrees::Leaves_traversal, CGAL::Orthtrees::Preorder_traversal, CGAL::Orthtrees::Postorder_traversal, require the orthtree as template parameter now. + +CGAL 5.6 traversal use: +\code{.cpp} +for (Orthtree::Node node : orthtree.traverse()) + ... +\endcode + +CGAL 6.0 traversal use: +\code{.cpp} +for (Orthtree::Node_index node : orthtree.traverse>()) + ... +\endcode + \section Section_Orthtree_History History A prototype code was implemented by Pierre Alliez and improved by Tong From c622af14306a6237cf89d1fc2c1dbaddbf6cf34c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 28 Feb 2024 18:05:05 +0100 Subject: [PATCH 461/520] add mesh orthtree picture --- Orthtree/doc/Orthtree/Doxyfile.in | 5 ++++- Orthtree/doc/Orthtree/Orthtree.txt | 18 +++++++++++++++--- Orthtree/doc/Orthtree/fig/orthtree_mesh.png | Bin 0 -> 151100 bytes 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 Orthtree/doc/Orthtree/fig/orthtree_mesh.png diff --git a/Orthtree/doc/Orthtree/Doxyfile.in b/Orthtree/doc/Orthtree/Doxyfile.in index 69f43f5fda86..2adfd640f365 100644 --- a/Orthtree/doc/Orthtree/Doxyfile.in +++ b/Orthtree/doc/Orthtree/Doxyfile.in @@ -5,4 +5,7 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Quadtrees, Octrees, and Orthtrees" EXTRACT_ALL = false HIDE_UNDOC_MEMBERS = true HIDE_UNDOC_CLASSES = true -WARN_IF_UNDOCUMENTED = false \ No newline at end of file +WARN_IF_UNDOCUMENTED = false + +HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/orthtree.png \ + ${CGAL_PACKAGE_DOC_DIR}/fig/orthtree_mesh.png diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 4e72839501d0..5a607422a556 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -25,9 +25,21 @@ aliases for `Quadtree` and `Octree`. These trees can be constructed with custom contents and split predicates, and iterated on with various traversal methods. Orthants can be orthotopes and not only hypercubes. -\cgalFigureBegin{Orthtree_fig, orthtree.png} -Building an %orthtree in 3D (%octree) from a point cloud (top) and a mesh (bottom). -\cgalFigureEnd +\cgalFigureAnchor{Orthtree_fig } +
    + + + + + + + +
    +
    +\cgalFigureCaptionBegin{Orthtree_fig} +Top: an %orthtree in 3D (%octree) from a point cloud (top); +Bottom: an %orthtree in 3D (%octree) from the triangle faces of a mesh. +\cgalFigureCaptionEnd \section Section_Orthtree_Building Building diff --git a/Orthtree/doc/Orthtree/fig/orthtree_mesh.png b/Orthtree/doc/Orthtree/fig/orthtree_mesh.png new file mode 100644 index 0000000000000000000000000000000000000000..12864286cd0a5916d1c2ebdf7f3d5f20734cf444 GIT binary patch literal 151100 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 literal 0 HcmV?d00001 From 3da1087bfe3b077afeca37238774f0464e6c22da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 29 Feb 2024 19:12:29 +0100 Subject: [PATCH 462/520] try to improve phrasing --- Orthtree/doc/Orthtree/Orthtree.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 5a607422a556..e5ec559ebcbc 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -284,11 +284,11 @@ purposes. For nontrivial point counts, the naive approach's calculation time dwarfs that of either the %orthtree or kd-tree. -\section Section_Orthtree_Migration Migration from CGAL 5.6 +\section Section_Orthtree_Migration Migrating Code Written Before Release 6.0 -The orthtree traits changed to allow for custom data stored per node in the orthtree. To migrate existing code from CGAL 5.6 Orthtree_traits_point can be used for a point-based orthtrees. The aliases `CGAL::Quadtree` and `CGAL::Octree` have been extended by a boolean template parameter to allow for non-cubic cells, which is the default. The data is passed via the traits class and no longer directly into the orthtree. +The orthtree package changed to allow for custom data stored per node in the orthtree. To migrate existing code written before \cgal 6.0 `Orthtree_traits_point` can be used for a point-based orthtrees. The aliases `CGAL::Quadtree` and `CGAL::Octree` have been extended by a boolean template parameter to allow for non-cubic cells, which is the default. The data is passed via the traits class and no longer directly to the orthtree. -CGAL 5.6 code to declare and define an Octree with cubic cells: +Former code to declare and define an Octree with cubic cells was as follows: \code{.cpp} typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; @@ -296,7 +296,7 @@ typedef CGAL::Octree Octree; Octree octree(points, points.point_map()); \endcode -CGAL 6.0 code with identical behavior: +\cgal 6.0 code with identical behavior is now: \code{.cpp} typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; @@ -304,9 +304,9 @@ typedef CGAL::Octree Octree; Octree octree({points, points.point_map()}); \endcode -The node class does not exist anymore and has been replaced by the lightweight type Node_index. All information formerly contained in the node class is now accessible via the Orthtree interface and stored using the new property maps. +The node class does not exist anymore and has been replaced by the lightweight type `Node_index`. All information formerly contained in the node class is now accessible via the `Orthtree` interface. -CGAL 5.6 exemplary node access: +Former node access was as follows: \code{.cpp} Orthtree::Node root = orthtree.root(); Orthtree::Node child = root[0]; @@ -316,7 +316,7 @@ for (Orthtree::Node::const_iterator it = child.begin(); it != child.end(); it++) ... \endcode -CGAL 6.0 exemplary node access: +\cgal 6.0 node access is now: \code{.cpp} Orthtree::Node_index root = orthtree.root(); Orthtree::Node_index child = orthtree.child(root, 0); @@ -328,15 +328,15 @@ for (const Orthtree::Traits::Node_data_element &idx : orthtree.data(child)) The nearest neighbor search behaves as before, however, the output iterator will be required to store `CollectionPartitioningOrthtreeTraits::Node_data_element`. This may be the actual point or, e.g., in case a `Point_set_3` is used, an index which requires the use of a point map to retrieve the point. -The provided traversals, i.e., CGAL::Orthtrees::Leaves_traversal, CGAL::Orthtrees::Preorder_traversal, CGAL::Orthtrees::Postorder_traversal, require the orthtree as template parameter now. +The provided traversals, i.e., `CGAL::Orthtrees::Leaves_traversal`, `CGAL::Orthtrees::Preorder_traversal`, `CGAL::Orthtrees::Postorder_traversal`, require the orthtree as template parameter now. -CGAL 5.6 traversal use: +Former traversal use was as follows: \code{.cpp} for (Orthtree::Node node : orthtree.traverse()) ... \endcode -CGAL 6.0 traversal use: +\cgal 6.0 traversal use is now: \code{.cpp} for (Orthtree::Node_index node : orthtree.traverse>()) ... From dffac51a18f904b9258d8450e21b06953b1515fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 29 Feb 2024 19:14:04 +0100 Subject: [PATCH 463/520] restore old behavior with a forward constructor drawback: construction with initilization list become ambiguous --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- .../Orthtree/octree_build_from_point_set.cpp | 2 +- .../Orthtree/octree_build_with_custom_split.cpp | 2 +- .../Orthtree/octree_find_nearest_neighbor.cpp | 2 +- Orthtree/examples/Orthtree/octree_surface_mesh.cpp | 2 +- .../examples/Orthtree/octree_traversal_custom.cpp | 2 +- .../examples/Orthtree/octree_traversal_manual.cpp | 2 +- .../Orthtree/octree_traversal_preorder.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 14 +++++++++++--- Orthtree/test/Orthtree/test_node_adjacent.cpp | 2 +- Orthtree/test/Orthtree/test_node_index.cpp | 2 +- Orthtree/test/Orthtree/test_octree_bbox.cpp | 6 +++--- .../test_octree_copy_move_constructors.cpp | 2 +- .../Orthtree/test_octree_custom_properties.cpp | 2 +- Orthtree/test/Orthtree/test_octree_equality.cpp | 12 ++++++------ Orthtree/test/Orthtree/test_octree_grade.cpp | 2 +- Orthtree/test/Orthtree/test_octree_kernels.cpp | 2 +- Orthtree/test/Orthtree/test_octree_locate.cpp | 6 +++--- .../test/Orthtree/test_octree_nearest_neighbor.cpp | 4 ++-- Orthtree/test/Orthtree/test_octree_refine.cpp | 10 +++++----- Orthtree/test/Orthtree/test_octree_traverse.cpp | 8 ++++---- 21 files changed, 48 insertions(+), 40 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index e5ec559ebcbc..29253a80257f 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -301,7 +301,7 @@ Octree octree(points, points.point_map()); typedef CGAL::Point_set_3 Point_set; typedef CGAL::Octree Octree; ... -Octree octree({points, points.point_map()}); +Octree octree(points, points.point_map()); \endcode The node class does not exist anymore and has been replaced by the lightweight type `Node_index`. All information formerly contained in the node class is now accessible via the `Orthtree` interface. diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index fd6c0bd97ab1..19b5c404e36d 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -29,7 +29,7 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Build the octree with a small bucket size, using a more verbose method octree.refine(CGAL::Orthtrees::Maximum_number_of_inliers(10)); diff --git a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp index b3cbac974f54..92c7c777a1d0 100644 --- a/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp +++ b/Orthtree/examples/Orthtree/octree_build_with_custom_split.cpp @@ -46,7 +46,7 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points" << std::endl; // Create an octree from the points - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Build the octree using our custom split predicate octree.refine(Split_by_ratio(2)); diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index 3ce7056424da..a6d53bd2f0dd 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -31,7 +31,7 @@ int main(int argc, char** argv) { std::cout << "loaded " << points.number_of_points() << " points" << std::endl; // Create an octree from the points - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Build the octree octree.refine(10, 20); diff --git a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp index cda6ad2b0240..676eacae2739 100644 --- a/Orthtree/examples/Orthtree/octree_surface_mesh.cpp +++ b/Orthtree/examples/Orthtree/octree_surface_mesh.cpp @@ -60,7 +60,7 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - Octree tree({mesh, mesh.points()}); + Octree tree(mesh, mesh.points()); OTraits::Split_predicate_node_min_extent sp(0.01); tree.refine(sp); diff --git a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp index 573219433113..a9a3ccba0728 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_custom.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_custom.cpp @@ -54,7 +54,7 @@ int main(int argc, char** argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Build the octree octree.refine(); diff --git a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp index 974d52f6077d..45c51fa3797e 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_manual.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_manual.cpp @@ -29,7 +29,7 @@ int main(int argc, char** argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Build the octree using the default arguments octree.refine(); diff --git a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp index 5ae7cc0a657e..a359f6217eea 100644 --- a/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp +++ b/Orthtree/examples/Orthtree/octree_traversal_preorder.cpp @@ -30,7 +30,7 @@ int main(int argc, char **argv) { std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl; // Create an octree from the points - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Build the octree octree.refine(); diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 552026d0b3fb..882d664431ba 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -235,7 +235,7 @@ class Orthtree { /// @{ /*! - \brief creates an orthtree for a traits instance. + \brief constructs an orthtree for a traits instance. The constructed orthtree has a root node with no children, containing the contents determined by `Construct_root_node_contents` from the traits class. @@ -275,10 +275,18 @@ class Orthtree { data(root()) = m_traits.construct_root_node_contents_object()(); } + /*! + constructs an orthtree from a set of arguments provided to the traits constructor + */ + template + explicit Orthtree(Arg1&& arg1, Arg2&& arg2, Args&& ... args) + : Orthtree(Traits(std::forward(arg1), std::forward(arg2), std::forward(args)...)) + {} + /// @} // copy constructor - Orthtree(const Orthtree& other) : + explicit Orthtree(const Orthtree& other) : m_traits(other.m_traits), m_node_properties(other.m_node_properties), m_node_contents(m_node_properties), @@ -289,7 +297,7 @@ class Orthtree { m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) {} // move constructor - Orthtree(Orthtree&& other) : + explicit Orthtree(Orthtree&& other) : m_traits(other.m_traits), m_node_properties(std::move(other.m_node_properties)), m_node_contents(m_node_properties), diff --git a/Orthtree/test/Orthtree/test_node_adjacent.cpp b/Orthtree/test/Orthtree/test_node_adjacent.cpp index 3655fb2f2c31..db9581b7225b 100644 --- a/Orthtree/test/Orthtree/test_node_adjacent.cpp +++ b/Orthtree/test/Orthtree/test_node_adjacent.cpp @@ -34,7 +34,7 @@ int main(void) { points.insert({-1, -1, -1.8}); points.insert({-1, -1, -1.9}); - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); std::cout << octree << std::endl; diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index f2270251e340..472f34455d69 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -33,7 +33,7 @@ int main(void) { points.insert({-1, -1, -1.8}); points.insert({-1, -1, -1.9}); - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); std::cout << "root: " << octree.local_coordinates(octree.root()) << std::endl; diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index c6be3ecfacfd..6ec98b0e023a 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -19,7 +19,7 @@ void test_1_node() { points.insert({-1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); Octree::Bbox expected_bbox{-1, -1, -1, -1, -1, -1}; @@ -36,7 +36,7 @@ void test_9_nodes() { points.insert({1, 1, 1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Compare the top node @@ -63,7 +63,7 @@ void test_25_nodes() { points.insert({1, 0.5, 1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Compare the top node diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index fcd7caaa5a93..7da85b51a8eb 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -57,7 +57,7 @@ int main() for (std::size_t i = 0; i < nb_pts; ++i) points.insert(*(generator++)); - Octree base({ points, points.point_map() }); + Octree base(points, points.point_map()); test(base); Octree_without_data base2({}); diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index 3c69724bd8a5..c3f4a7706a28 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -23,7 +23,7 @@ int main(void) { for (std::size_t i = 0; i < nb_pts; ++i) points.insert(*(generator++)); - Octree tree({points, points.point_map()}); + Octree tree(points, points.point_map()); // Testing built in node properties typename Octree::Property_map data_prop = *tree.property("contents"); diff --git a/Orthtree/test/Orthtree/test_octree_equality.cpp b/Orthtree/test/Orthtree/test_octree_equality.cpp index db1c18a9a124..bbdc0cbb7d10 100644 --- a/Orthtree/test/Orthtree/test_octree_equality.cpp +++ b/Orthtree/test/Orthtree/test_octree_equality.cpp @@ -25,8 +25,8 @@ void test_identical_trees() { points.insert({1, 1, 1}); // Create a pair of trees from the same point set - Octree a({points, points.point_map()}); - Octree b({points, points.point_map()}); + Octree a(points, points.point_map()); + Octree b(points, points.point_map()); // Refine both trees using the same criteria a.refine(10, 1); @@ -51,8 +51,8 @@ void test_identical_contents_different_criteria() { points.insert({1, 1, 1}); // Create a pair of trees from the same point set - Octree a({points, points.point_map()}); - Octree b({points, points.point_map()}); + Octree a(points, points.point_map()); + Octree b(points, points.point_map()); // Refine both trees using different criteria a.refine(10, 1); @@ -86,8 +86,8 @@ void test_different_contents_identical_criteria() { points_b.insert({1, 1, 2}); // Create a pair of trees from the different point sets - Octree a({points_a, points_a.point_map()}); - Octree b({points_b, points_b.point_map()}); + Octree a(points_a, points_a.point_map()); + Octree b(points_b, points_b.point_map()); // Refine both trees using the same criteria a.refine(10, 1); diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 226aa29b10f8..5b9eb411c2f3 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -48,7 +48,7 @@ void test(std::size_t dataset_size) { points.insert(*(generator++)); // Build an octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); // Refine the octree octree.refine(); diff --git a/Orthtree/test/Orthtree/test_octree_kernels.cpp b/Orthtree/test/Orthtree/test_octree_kernels.cpp index 7cd6fb0de8bb..5e477d4a9ebd 100644 --- a/Orthtree/test/Orthtree/test_octree_kernels.cpp +++ b/Orthtree/test/Orthtree/test_octree_kernels.cpp @@ -18,7 +18,7 @@ void test() for (std::size_t i = 0; i < 100; ++i) points.insert(*(generator++)); - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(); octree.grade(); } diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index 77a243d7bc66..c53c4ce3f635 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -21,7 +21,7 @@ void test_1_point() { points.insert({-1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Because there's only the root node, any point should be placed in it @@ -47,7 +47,7 @@ void test_8_points() { points.insert({1, 1, 1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Existing points should end up in the same place @@ -88,7 +88,7 @@ void test_10_points() { points.insert({-1, -0.75, 1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Existing points should end up in the same place diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index 72b423199ff4..5697affb4910 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -66,7 +66,7 @@ void naive_vs_octree(std::size_t dataset_size) { // Do the same using the octree Point octree_nearest = *generator; - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); { @@ -118,7 +118,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { // Do the same using the octree std::vector octree_nearest_neighbors; - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index f61b6141e53e..3aa2ef3dfc25 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -37,7 +37,7 @@ void test_1_point() { points.insert({-1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Check that the root node was never split @@ -53,11 +53,11 @@ void test_2_points() { points.insert({1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // The octree should have been split once - Octree other({points, points.point_map()}); + Octree other(points, points.point_map()); other.split(other.root()); assert(Octree::is_topology_equal(other, octree)); assert(1 == octree.depth()); @@ -72,10 +72,10 @@ void test_4_points() { points.insert({1, 1, 4}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); - Octree other({points, points.point_map()}); + Octree other(points, points.point_map()); other.split(other.root()); other.split(other.node(3)); other.split(other.node(7)); diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index 4d96f88442cf..f61e9919eafb 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -22,7 +22,7 @@ bool test_preorder_1_node() { points.insert({-1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Create the range @@ -43,7 +43,7 @@ bool test_preorder_9_nodes() { points.insert({1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Create the range @@ -68,7 +68,7 @@ bool test_level_9_nodes() { points.insert({1, -1, -1}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); // Create the range @@ -94,7 +94,7 @@ bool test_preorder_25_nodes() { points.insert({1, 1, 4}); // Create the octree - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(10, 1); std::cout << octree << std::endl; From 3714189a14bbf9e917d2bb53ef1888b6d6fa8b26 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:30:53 +0100 Subject: [PATCH 464/520] Update Orthtree/include/CGAL/Orthtree_traits_base.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree_traits_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base.h b/Orthtree/include/CGAL/Orthtree_traits_base.h index 6a55b4f6193d..73f6bee405c4 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -51,7 +51,7 @@ struct Orthtree_traits_base { * * \note This type is used to identify adjacency directions with * easily understandable keywords (left, right, up, down, ...) and is thus - * mainly useful in 2d and 3d. In + * mainly useful in 2D and 3D. In * higher dimensions, such keywords do not exist and this type is * simply an integer. Conversions from this integer to bitsets still * work but do not provide any user-friendly API for adjacency selection. From ff5f855f2744d86336701045f9393c625296afc7 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:31:07 +0100 Subject: [PATCH 465/520] Update Orthtree/include/CGAL/Orthtree_traits_face_graph.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index 4f06dff45daa..c0b98ff40e7a 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -30,7 +30,7 @@ Traits class for the `Orthtree` class to be used to construct a 3D octree around a triangulated surface mesh. Each node of the octree will store all the faces of the mesh intersected by its bounding box. The subdivision of the octree is controlled by the nested class `Orthtree_traits_face_graph::Split_predicate_node_min_extent` -to which the minimal extend of a node should be provided. +to which the minimal extent of a node should be provided. \tparam TriangleMesh a model of `FaceListGraph` with all faces being triangles \tparam VertexPointMap a property map associating points to the vertices of `TriangleMesh` From d85edb3e8e858e4ee1bb39115841a1ea0d0ba379 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:32:00 +0100 Subject: [PATCH 466/520] Update Orthtree/include/CGAL/Orthtree_traits_base.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree_traits_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base.h b/Orthtree/include/CGAL/Orthtree_traits_base.h index 73f6bee405c4..4a748c97ffb9 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -58,7 +58,7 @@ struct Orthtree_traits_base { * * Two directions along each axis in %Cartesian space, relative to a node. * - * Directions are mapped to numbers as 3-bit integers in the 3d case or as 2-bit integers in the 2d case. + * Directions are mapped to numbers as 3-bit integers in the 3D case or as 2-bit integers in the 2D case. * In the 3d case the numbers 6 and 7 are not used because there are only 6 different directions. * * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), From d49529d5d08cec4456e665422b0c8bd0fc1346b4 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:32:14 +0100 Subject: [PATCH 467/520] Update Orthtree/include/CGAL/Orthtree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 882d664431ba..3bcd50f8cc93 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -952,7 +952,7 @@ class Orthtree { \param n the index of the node to find the sibling of \return the index of the next sibling of n - if n is not the last node in its parent, otherwise nothing. + if n is not the last node in its parent, otherwise `std::nullopt`. */ const std::optional next_sibling(Node_index n) const { From b5741aa4b1dffaea631e449747e70dd82275338d Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:32:30 +0100 Subject: [PATCH 468/520] Update Orthtree/include/CGAL/Orthtree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 3bcd50f8cc93..e37187048243 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -324,7 +324,7 @@ class Orthtree { /*! \brief recursively subdivides the orthtree until it meets the given criteria. - The split predicate should return `true` if a leaf node should be split and false` otherwise. + The split predicate should return `true` if a leaf node should be split and `false` otherwise. This function may be called several times with different predicates: in that case, nodes already split are left unaltered, From 8574b1ce4f10205844bca721128ae86586827928 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:32:43 +0100 Subject: [PATCH 469/520] Update Orthtree/include/CGAL/Orthtree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index e37187048243..94aca9043f78 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -127,7 +127,7 @@ class Orthtree { static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; static inline constexpr bool supports_neighbor_search = true;// Orthtree_impl::has_Squared_distance_of_element::value; #else - static inline constexpr bool has_data = bool_value; ///< `true` if `GeomTraits` is a model of `OrthtreeTraitswithData` and `false` otherwise. + static inline constexpr bool has_data = bool_value; ///< `true` if `GeomTraits` is a model of `OrthtreeTraitsWithData` and `false` otherwise. static inline constexpr bool supports_neighbor_search = bool_value; ///< `true` if `GeomTraits` is a model of `CollectionPartitioningOrthtreeTraits` and `false` otherwise. #endif static constexpr int dimension = Traits::dimension; ///< Dimension of the tree From 87d895254dabb266101acc8d7df3f44d56307b82 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:32:53 +0100 Subject: [PATCH 470/520] Update Orthtree/include/CGAL/Orthtree_traits_base.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree_traits_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_base.h b/Orthtree/include/CGAL/Orthtree_traits_base.h index 4a748c97ffb9..9750b2684ac6 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -64,7 +64,7 @@ struct Orthtree_traits_base { * The first two bits indicate the axis (00 = x, 01 = y, 10 = z), * the third bit indicates the direction along that axis (0 = -, 1 = +). * - * The following diagram and table showing the 3d case may be a useful reference (2d case is identical with one dimension less): + * The following diagram and table showing the 3D case may be a useful reference (2D case is identical with one dimension less): * * 3 * * | * 4 From 020f3231723d063e50251d882b07288aec22e21f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:33:17 +0100 Subject: [PATCH 471/520] Update Orthtree/include/CGAL/Orthtree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 94aca9043f78..2a79b541b318 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -111,7 +111,7 @@ struct Node_data_wrapper \sa `CGAL::Quadtree` \sa `CGAL::Octree` - \tparam GeomTraits must be a model of `OrthtreeTraits` or `OrthtreeTraitswithData`. + \tparam GeomTraits must be a model of `OrthtreeTraits` or `OrthtreeTraitsWithData`. */ template class Orthtree { From 683be7aa790c7c57890b97d53cd1455feff3d8c9 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:33:32 +0100 Subject: [PATCH 472/520] Update Orthtree/include/CGAL/Orthtree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2a79b541b318..9ca83def3338 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -935,7 +935,7 @@ class Orthtree { Equivalent to `tree.descendant(tree.root(), indices...)`. - \param indices the integer indices specifying the descent to perform, starting from root + \param indices the integer indices specifying the descent to perform, starting from the root \return the index of the specified node */ From 5bf13e85e10d17a3f65a5420a942354aefca92bc Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:33:39 +0100 Subject: [PATCH 473/520] Update Orthtree/include/CGAL/Octree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Octree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Octree.h b/Orthtree/include/CGAL/Octree.h index 009f62cc5144..e59a19be46d5 100644 --- a/Orthtree/include/CGAL/Octree.h +++ b/Orthtree/include/CGAL/Octree.h @@ -27,7 +27,7 @@ namespace CGAL { \tparam GeomTraits a model of `Kernel` \tparam PointRange a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3` - \tparam cubic_nodes boolean to enforce cubic nodes + \tparam cubic_nodes Boolean to enforce cubic nodes */ template < typename GeomTraits, From f90dfbaa2c453b84bba97cef80a9ebd103871eda Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:33:49 +0100 Subject: [PATCH 474/520] Update Orthtree/include/CGAL/Quadtree.h Co-authored-by: Andreas Fabri --- Orthtree/include/CGAL/Quadtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Quadtree.h b/Orthtree/include/CGAL/Quadtree.h index 755dfc8860b6..5b6086bcf73e 100644 --- a/Orthtree/include/CGAL/Quadtree.h +++ b/Orthtree/include/CGAL/Quadtree.h @@ -27,7 +27,7 @@ namespace CGAL { \tparam GeomTraits must be a model of `Kernel` \tparam PointRange must be a model of `Range` whose value type is the key type of `PointMap` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_2` - \tparam square_nodes boolean to enforce square nodes + \tparam square_nodes Boolean to enforce square nodes */ template Date: Fri, 1 Mar 2024 08:37:11 +0100 Subject: [PATCH 475/520] Update Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h Co-authored-by: Andreas Fabri --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index ee889f0776f7..88d4727a83b6 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -22,7 +22,7 @@ class OrthtreeTraits constexpr int dimension; ///< Dimension. using FT = unspecified_type; ///< The number type of the %Cartesian coordinates of types `Point_d` using Point_d = unspecified_type; ///< Point type. - using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` types. + using Bbox_d = unspecified_type; ///< Bounding box type. Must be constructible from a pair of `Point_d` objects. /*! A random access iterator type to enumerate the From 159bd6de58a517baca9232e88cea5cb6b9d7f8d9 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 08:37:32 +0100 Subject: [PATCH 476/520] Update Orthtree/doc/Orthtree/PackageDescription.txt Co-authored-by: Andreas Fabri --- Orthtree/doc/Orthtree/PackageDescription.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index c1baeeae8494..d9e578e2709d 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -13,8 +13,6 @@ Quadtree, Octree and Orthtree Reference /// \defgroup PkgOrthtreeTraversal Traversal /// \ingroup PkgOrthtreeRef -/// \defgroup PkgOrthtreeNeighbors Neighbor Search Functions -/// \ingroup PkgOrthtreeRef /*! From 48c1fef4d542a56b7a46f7a292e7dc7129b5ffb7 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 09:09:11 +0100 Subject: [PATCH 477/520] moving package headers to front --- Orthtree/test/Orthtree/test_node_index.cpp | 2 -- Orthtree/test/Orthtree/test_octree_bbox.cpp | 6 ++---- .../Orthtree/test_octree_copy_move_constructors.cpp | 10 +++++----- .../test/Orthtree/test_octree_custom_properties.cpp | 7 ++++--- Orthtree/test/Orthtree/test_octree_equality.cpp | 2 +- Orthtree/test/Orthtree/test_octree_grade.cpp | 6 +++--- Orthtree/test/Orthtree/test_octree_intersecting.cpp | 4 ++-- Orthtree/test/Orthtree/test_octree_locate.cpp | 4 ++-- .../test/Orthtree/test_octree_nearest_neighbor.cpp | 6 +++--- Orthtree/test/Orthtree/test_octree_refine.cpp | 4 ++-- Orthtree/test/Orthtree/test_octree_traverse.cpp | 2 +- 11 files changed, 25 insertions(+), 28 deletions(-) diff --git a/Orthtree/test/Orthtree/test_node_index.cpp b/Orthtree/test/Orthtree/test_node_index.cpp index 472f34455d69..d8216f6e996a 100644 --- a/Orthtree/test/Orthtree/test_node_index.cpp +++ b/Orthtree/test/Orthtree/test_node_index.cpp @@ -42,7 +42,5 @@ int main(void) { std::cout << "fifth child of first child: " << octree.local_coordinates(octree.child(octree.child(octree.root(), 0), 4)) << std::endl; - // TODO - return 0; } diff --git a/Orthtree/test/Orthtree/test_octree_bbox.cpp b/Orthtree/test/Orthtree/test_octree_bbox.cpp index 6ec98b0e023a..f36ba6aeb942 100644 --- a/Orthtree/test/Orthtree/test_octree_bbox.cpp +++ b/Orthtree/test/Orthtree/test_octree_bbox.cpp @@ -1,10 +1,8 @@ - -#include -#include #include #include #include - +#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index 7da85b51a8eb..e802572ccff4 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -1,16 +1,16 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include #include -#include +#include +#include +#include #include #include -#include -#include +#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp index c3f4a7706a28..2c4ab94918b1 100644 --- a/Orthtree/test/Orthtree/test_octree_custom_properties.cpp +++ b/Orthtree/test/Orthtree/test_octree_custom_properties.cpp @@ -1,13 +1,14 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include #include -#include +#include #include #include +#include +#include + using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; using FT = Kernel::FT; diff --git a/Orthtree/test/Orthtree/test_octree_equality.cpp b/Orthtree/test/Orthtree/test_octree_equality.cpp index bbdc0cbb7d10..ce6d3c95dcb6 100644 --- a/Orthtree/test/Orthtree/test_octree_equality.cpp +++ b/Orthtree/test/Orthtree/test_octree_equality.cpp @@ -1,10 +1,10 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include #include #include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_grade.cpp b/Orthtree/test/Orthtree/test_octree_grade.cpp index 5b9eb411c2f3..19acd787d9a0 100644 --- a/Orthtree/test/Orthtree/test_octree_grade.cpp +++ b/Orthtree/test/Orthtree/test_octree_grade.cpp @@ -1,14 +1,14 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include #include #include -#include +#include #include #include +#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index ae40a16037e2..7576ad168ec4 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -1,12 +1,12 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include #include #include #include #include +#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_locate.cpp b/Orthtree/test/Orthtree/test_octree_locate.cpp index c53c4ce3f635..8bbc8932d9ea 100644 --- a/Orthtree/test/Orthtree/test_octree_locate.cpp +++ b/Orthtree/test/Orthtree/test_octree_locate.cpp @@ -1,12 +1,12 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include #include #include #include +#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index 5697affb4910..b0383ee2d26a 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -1,9 +1,6 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include -#include #include #include #include @@ -12,6 +9,9 @@ #include #include +#include +#include +#include using namespace std::chrono; diff --git a/Orthtree/test/Orthtree/test_octree_refine.cpp b/Orthtree/test/Orthtree/test_octree_refine.cpp index 3aa2ef3dfc25..ff75dd18edc7 100644 --- a/Orthtree/test/Orthtree/test_octree_refine.cpp +++ b/Orthtree/test/Orthtree/test_octree_refine.cpp @@ -1,12 +1,12 @@ #define CGAL_TRACE_STREAM std::cerr -#include -#include #include #include #include +#include +#include using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; diff --git a/Orthtree/test/Orthtree/test_octree_traverse.cpp b/Orthtree/test/Orthtree/test_octree_traverse.cpp index f61e9919eafb..e7ec64fd3577 100644 --- a/Orthtree/test/Orthtree/test_octree_traverse.cpp +++ b/Orthtree/test/Orthtree/test_octree_traverse.cpp @@ -1,11 +1,11 @@ #define CGAL_TRACE_STREAM std::cerr -#include #include #include #include #include +#include using Kernel = CGAL::Simple_cartesian; From 77622a691bcb0a3e99be799e1c4c427cb6c5a5e6 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 10:08:46 +0100 Subject: [PATCH 478/520] renamed nearest_neighbors to neighbors_in_radius --- Orthtree/include/CGAL/Orthtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 9ca83def3338..fdcb5e647c41 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -705,7 +705,7 @@ class Orthtree { \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. */ template - auto nearest_neighbors(const Sphere& query, OutputIterator output) const -> std::enable_if_t { + auto neighbors_in_radius(const Sphere& query, OutputIterator output) const -> std::enable_if_t { return nearest_k_neighbors_in_radius(query, (std::numeric_limits::max)(), output); } From fd20d9800284af1b4efd0c552301d192ca24c898 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 10:09:54 +0100 Subject: [PATCH 479/520] functor parameter fix --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h index 298691cef839..f6533340079a 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraitsWithData.h @@ -44,7 +44,7 @@ class OrthtreeTraitsWithData * \brief Functor which fills the contents of the nodes children. * * Provides the operator: - * `void operator()(typename Tree::Node_index, Tree&, const Point_d&)` + * `void operator()(Node_index, Orthtree&, const Point_d&)` * * The functor is called during refinement of the `Orthtree` on a node after it has been split. The purpose of the functor is to * distribute the `Node_data`, accessible via `tree.data()`, to the data of the nodes children, accessible via `tree.children()`. From 546888c6d19cf1ecacb68a389c8fcfe0c40df1a6 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 10:19:31 +0100 Subject: [PATCH 480/520] lower number of sample points to 20 --- Orthtree/examples/Orthtree/orthtree_build.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/examples/Orthtree/orthtree_build.cpp b/Orthtree/examples/Orthtree/orthtree_build.cpp index 9f08bfc331dd..56419faf0271 100644 --- a/Orthtree/examples/Orthtree/orthtree_build.cpp +++ b/Orthtree/examples/Orthtree/orthtree_build.cpp @@ -19,7 +19,7 @@ int main() CGAL::Random r; Point_vector points_dd; - for (std::size_t i = 0; i < 500; ++ i) + for (std::size_t i = 0; i < 20; ++ i) { std::array init{}; for (double& v : init) From a8fd56c7240759e82343bd293fe27d5df440ff1d Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 15:43:01 +0100 Subject: [PATCH 481/520] using 'contained elements' instead of elements to descripe split predicate --- Orthtree/doc/Orthtree/Orthtree.txt | 8 ++++---- Orthtree/doc/Orthtree/PackageDescription.txt | 4 ++-- .../Orthtree/octree_build_from_point_set.cpp | 2 +- Orthtree/include/CGAL/Orthtree.h | 10 +++++----- .../include/CGAL/Orthtree/Split_predicates.h | 18 +++++++++--------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 29253a80257f..e82ea0d25bcd 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -57,7 +57,7 @@ split, `false` otherwise: this enables users to choose on what criterion should the orthtree be refined. Predefined predicates are provided for the point-set orthtree, including [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth) -and [Maximum_number_of_inliers](@ref CGAL::Orthtrees::Maximum_number_of_inliers). +and [Maximum_contained_elements](@ref CGAL::Orthtrees::Maximum_contained_elements). The simplest way to create a point-set orthtree is from a vector of points. The constructor generally expects a separate point range and map, @@ -75,8 +75,8 @@ For convenience, the alias `CGAL::Quadtree` is provided. The following example shows the construction of %quadtree from a vector of `Point_2` objects. `quadtree.refine(10, 5)` uses the default split predicate, which -enforces a max-depth and a maximum number of inliers per node ("bucket size"). -Nodes are split if their depth is less than 10, and they contain more than 5 inliers. +enforces a max-depth and a maximum of elements contained per node ("bucket size"). +Nodes are split if their depth is less than 10, and they contain more than 5 points. \cgalExample{Orthtree/quadtree_build_from_point_vector.cpp} @@ -103,7 +103,7 @@ An %octree is constructed from the point set and its corresponding map, and then written to the standard output. The split predicate is manually constructed and passed to the refine method. -In this case, we use a maximum number of inliers with no corresponding maximum depth. +In this case, we use a maximum number of contained elements with no corresponding maximum depth. This means that nodes will continue to be split until none contain more than 10 points. \cgalExample{Orthtree/octree_build_from_point_set.cpp} diff --git a/Orthtree/doc/Orthtree/PackageDescription.txt b/Orthtree/doc/Orthtree/PackageDescription.txt index d9e578e2709d..98825d8e237d 100644 --- a/Orthtree/doc/Orthtree/PackageDescription.txt +++ b/Orthtree/doc/Orthtree/PackageDescription.txt @@ -55,9 +55,9 @@ Quadtree, Octree and Orthtree Reference - `CGAL::Orthtree_traits_face_graph` \cgalCRPSection{Split Predicates} -- `CGAL::Orthtrees::Maximum_number_of_inliers` +- `CGAL::Orthtrees::Maximum_contained_elements` - `CGAL::Orthtrees::Maximum_depth` -- `CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers` +- `CGAL::Orthtrees::Maximum_depth_and_maximum_contained_elements` \cgalCRPSection{%Traversal} - `CGAL::Orthtrees::Preorder_traversal` diff --git a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp index 19b5c404e36d..c4226b0dbe8a 100644 --- a/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp +++ b/Orthtree/examples/Orthtree/octree_build_from_point_set.cpp @@ -32,7 +32,7 @@ int main(int argc, char **argv) { Octree octree(points, points.point_map()); // Build the octree with a small bucket size, using a more verbose method - octree.refine(CGAL::Orthtrees::Maximum_number_of_inliers(10)); + octree.refine(CGAL::Orthtrees::Maximum_contained_elements(10)); // Print out the tree std::cout << octree << std::endl; diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index fdcb5e647c41..ff0350c52061 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -366,17 +366,17 @@ class Orthtree { /*! \brief convenience overload that refines an orthtree using a - maximum depth and maximum number of inliers in a node as split + maximum depth and maximum number of contained elements in a node as split predicate. This is equivalent to calling - `refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth, + `refine(Orthtrees::Maximum_depth_and_maximum_contained_elements(max_depth, bucket_size))`. The refinement is stopped as soon as one of the conditions is - violated: if a node has more inliers than `bucket_size` but is + violated: if a node contains more elements than `bucket_size` but is already at `max_depth`, it is not split. Similarly, a node that is - at a depth smaller than `max_depth` but already has fewer inliers + at a depth smaller than `max_depth` but already contains fewer elements than `bucket_size`, it is not split. \warning This convenience method is only appropriate for trees with traits classes where @@ -386,7 +386,7 @@ class Orthtree { \param bucket_size maximum number of items a node is allowed to contain. */ void refine(size_t max_depth = 10, size_t bucket_size = 20) { - refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth, bucket_size)); + refine(Orthtrees::Maximum_depth_and_maximum_contained_elements(max_depth, bucket_size)); } /*! diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index f7bf2bdd6c08..c3b0d929b62e 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -25,7 +25,7 @@ namespace Orthtrees { /*! \ingroup PkgOrthtreeSplitPredicates - \brief A class used to choose when a node should be split depending on the number of inliers. + \brief A class used to choose when a node should be split depending on the number of contained elements. This is a bucket size predicate that considers a node should be split if it contains more than a certain number of items. @@ -33,15 +33,15 @@ namespace Orthtrees { \warning This split predicate is only appropriate for trees with traits classes which are models of `OrthtreeTraitsWithData` and where `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ -class Maximum_number_of_inliers { +class Maximum_contained_elements { std::size_t m_bucket_size; public: /*! - \brief creates a predicate based on the number of inliers (bucket size). + \brief creates a predicate based on the number of contained elements. */ - Maximum_number_of_inliers(std::size_t bucket_size) : + Maximum_contained_elements(std::size_t bucket_size) : m_bucket_size(bucket_size) {} /*! @@ -83,22 +83,22 @@ class Maximum_depth { /*! \ingroup PkgOrthtreeSplitPredicates - \brief A class used to choose when a node should be split depending on the depth and the number of inliers. + \brief A class used to choose when a node should be split depending on the depth and the number of contained elements. This predicate makes a note split if it contains more than a certain number of items and if its depth is lower than a certain limit. The refinement is stopped as soon as one of the conditions is - violated: if a node has more inliers than `bucket_size` but is + violated: if a node has more elements than `bucket_size` but is already at `max_depth`, it is not split. Similarly, a node that is - at a depth smaller than `max_depth` but already has fewer inliers + at a depth smaller than `max_depth` but already has fewer elements than `bucket_size`, it is not split. \warning This split predicate is only appropriate for trees with traits classes which are models of `OrthtreeTraitsWithData` and where `Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. */ -class Maximum_depth_and_maximum_number_of_inliers { +class Maximum_depth_and_maximum_contained_elements { std::size_t m_max_depth, m_bucket_size; @@ -106,7 +106,7 @@ class Maximum_depth_and_maximum_number_of_inliers { /*! \brief creates a predicate using maximum depth or bucket size. */ - Maximum_depth_and_maximum_number_of_inliers(std::size_t max_depth, std::size_t bucket_size) : + Maximum_depth_and_maximum_contained_elements(std::size_t max_depth, std::size_t bucket_size) : m_max_depth(max_depth), m_bucket_size(bucket_size) {} /*! From caa833f439351ead9f9819956ad7aa4bed5614fc Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 16:32:00 +0100 Subject: [PATCH 482/520] renaming nearest_neighbors to nearest_k_neighbors --- Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp | 4 ++-- Orthtree/include/CGAL/Orthtree.h | 2 +- Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index a6d53bd2f0dd..f5c151749eb1 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -46,7 +46,7 @@ int main(int argc, char** argv) { }; for (const Point& p : points_to_find) - octree.nearest_neighbors + octree.nearest_k_neighbors (p, 1, // k=1 to find the single closest point boost::make_function_output_iterator ([&](const Point_set::Index& nearest) @@ -57,7 +57,7 @@ int main(int argc, char** argv) { typename Octree::Sphere s(points_to_find[0], 0.0375); std::cout << std::endl << "Closest points within the sphere around " << s.center() << " with squared radius of " << s.squared_radius() << ":" << std::endl; - octree.nearest_neighbors + octree.neighbors_in_radius (s, boost::make_function_output_iterator ([&](const Point_set::Index& nearest) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index ff0350c52061..5bb336a22c40 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -682,7 +682,7 @@ class Orthtree { \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. */ template - auto nearest_neighbors(const Point& query, + auto nearest_k_neighbors(const Point& query, std::size_t k, OutputIterator output) const -> std::enable_if_t { Sphere query_sphere(query, (std::numeric_limits::max)()); diff --git a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp index b0383ee2d26a..2f37a75236cb 100644 --- a/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp +++ b/Orthtree/test/Orthtree/test_octree_nearest_neighbor.cpp @@ -71,7 +71,7 @@ void naive_vs_octree(std::size_t dataset_size) { auto octree_start_time = high_resolution_clock::now(); { std::vector k_neighbors; - octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); + octree.nearest_k_neighbors(random_point, 1, std::back_inserter(k_neighbors)); octree_nearest = get(points.point_map(), *k_neighbors.begin()); } duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; @@ -121,7 +121,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) { Octree octree(points, points.point_map()); octree.refine(10, 20); auto octree_start_time = high_resolution_clock::now(); - octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); + octree.nearest_k_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors)); duration octree_elapsed_time = high_resolution_clock::now() - octree_start_time; std::cout << "Octree --> " From fcbb9221f808597352bcb5dd938fccedbd2b4a04 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 16:34:21 +0100 Subject: [PATCH 483/520] making benchmark compilable --- Orthtree/benchmark/Orthtree/construction.cpp | 2 +- Orthtree/benchmark/Orthtree/nearest_neighbor.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Orthtree/benchmark/Orthtree/construction.cpp b/Orthtree/benchmark/Orthtree/construction.cpp index b1f2a132aa5b..5d55f39c1ee8 100644 --- a/Orthtree/benchmark/Orthtree/construction.cpp +++ b/Orthtree/benchmark/Orthtree/construction.cpp @@ -45,7 +45,7 @@ int main(int argc, char **argv) { auto octreeTime = bench( [&] { // Build the tree - Octree octree({octreePoints, octreePoints.point_map()}); + Octree octree(octreePoints, octreePoints.point_map()); octree.refine(); } ); diff --git a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp index ddba4b8ffd5c..219d9f1b0f7a 100644 --- a/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp +++ b/Orthtree/benchmark/Orthtree/nearest_neighbor.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -96,14 +95,14 @@ int main(int argc, char **argv) { // ); // Build the octree from points (this had to be done second because it rearranges the point set) - Octree octree({points, points.point_map()}); + Octree octree(points, points.point_map()); octree.refine(); // Time how long it takes to find neighbors using the octree auto octreeTime = bench( [&] { std::vector nearest_neighbors; - CGAL::Orthtrees::nearest_neighbors(octree, search_point, k, std::back_inserter(nearest_neighbors)); + octree.nearest_k_neighbors(search_point, k, std::back_inserter(nearest_neighbors)); } ); From 130cf57758238ab710301e381d094b5988d0ee11 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Fri, 1 Mar 2024 17:29:17 +0100 Subject: [PATCH 484/520] created local copy of point before using Cartesian_ranges --- Orthtree/include/CGAL/Orthtree.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 5bb336a22c40..2028c9f72a8e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -1366,7 +1366,9 @@ class Orthtree { auto child_node = child(node, i); FT squared_distance = 0; - for (const auto& r : cartesian_range(m_traits.construct_center_d_object()(search_bounds), barycenter(child_node))) { + Point c = m_traits.construct_center_d_object()(search_bounds); + Point b = barycenter(child_node); + for (const auto r : cartesian_range(c, b)) { FT d = (get<0>(r) - get<1>(r)); squared_distance += d * d; } From 6461334f797201ca0172463ffb100ec5d0c5153e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 05:02:49 +0000 Subject: [PATCH 485/520] Bump fsfe/reuse-action from 2 to 3 Bumps [fsfe/reuse-action](https://github.com/fsfe/reuse-action) from 2 to 3. - [Release notes](https://github.com/fsfe/reuse-action/releases) - [Commits](https://github.com/fsfe/reuse-action/compare/v2...v3) --- updated-dependencies: - dependency-name: fsfe/reuse-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/reuse.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index 1ddd0a776445..f76a99b18dd1 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -12,15 +12,15 @@ jobs: steps: - uses: actions/checkout@v4 - name: REUSE version - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --version - name: REUSE lint - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --include-submodules lint - name: REUSE SPDX SBOM - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: spdx - name: install dependencies @@ -30,6 +30,6 @@ jobs: mkdir -p ./release cmake -DDESTINATION=./release -DCGAL_VERSION=9.9 -P ./Scripts/developer_scripts/cgal_create_release_with_cmake.cmake - name: REUSE lint release tarball - uses: fsfe/reuse-action@v2 + uses: fsfe/reuse-action@v3 with: args: --root ./release/CGAL-9.9 --include-submodules lint From 2324d61af6a25abdcd53497ff9680a916310f889 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 4 Mar 2024 16:50:46 +0000 Subject: [PATCH 486/520] Rename header file --- Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp | 2 +- .../include/CGAL/Polygon_repair/{Polygon_repair.h => repair.h} | 0 Polygon_repair/test/Polygon_repair/clipart.cpp | 2 +- Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp | 2 +- Polygon_repair/test/Polygon_repair/exact_test.cpp | 2 +- Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp | 2 +- Polygon_repair/test/Polygon_repair/validate_wkt.cpp | 2 +- .../test/Polygon_repair/write_labeled_triangulation.cpp | 2 +- Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename Polygon_repair/include/CGAL/Polygon_repair/{Polygon_repair.h => repair.h} (100%) diff --git a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp index f2f9d3302357..36e3ba74a764 100644 --- a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h similarity index 100% rename from Polygon_repair/include/CGAL/Polygon_repair/Polygon_repair.h rename to Polygon_repair/include/CGAL/Polygon_repair/repair.h diff --git a/Polygon_repair/test/Polygon_repair/clipart.cpp b/Polygon_repair/test/Polygon_repair/clipart.cpp index 94516d6e7649..013265e835b8 100644 --- a/Polygon_repair/test/Polygon_repair/clipart.cpp +++ b/Polygon_repair/test/Polygon_repair/clipart.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp index ed312122c767..7161506f952a 100644 --- a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp index 265c4b6fd751..4ed815ff9ad2 100644 --- a/Polygon_repair/test/Polygon_repair/exact_test.cpp +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 7150090da045..2758dd4aebdf 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp index a5584e6745cc..281da7542f27 100644 --- a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp +++ b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp index 769b56420a19..24d542c7ce1f 100644 --- a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp +++ b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; diff --git a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp index 183c64bbd384..604e04a0ec8d 100644 --- a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; From b0f4e7335842534ce05906786933a59518faa2ce Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 4 Mar 2024 19:48:25 +0000 Subject: [PATCH 487/520] Make the Even_odd_rule a default --- .../doc/Polygon_repair/Polygon_repair.txt | 1 - .../Polygon_repair/repair_polygon_2.cpp | 2 +- .../include/CGAL/Polygon_repair/repair.h | 33 +++++-------------- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt index 746f5874c75e..26b6f6d62c70 100644 --- a/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt +++ b/Polygon_repair/doc/Polygon_repair/Polygon_repair.txt @@ -152,7 +152,6 @@ with holes has zero holes and extract these if needed. It is possible to repair a polygon, polygon with holes or multipolygon with holes using the even-odd rule by calling the `Polygon_repair::repair()` function -giving as second argument the `Polygon_repair::Even_odd_rule` as shown in the following example. This function returns a repaired multipolygon with holes. \cgalExample{Polygon_repair/repair_polygon_2.cpp} diff --git a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp index 36e3ba74a764..9004d092abd2 100644 --- a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) { Polygon_with_holes_2 pin; CGAL::IO::read_polygon_WKT(in, pin); - Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair(pin, CGAL::Polygon_repair::Even_odd_rule()); + Multipolygon_with_holes_2 mp = CGAL::Polygon_repair::repair(pin); if (mp.number_of_polygons_with_holes() > 1) { CGAL::IO::write_multi_polygon_WKT(std::cout, mp); } else { diff --git a/Polygon_repair/include/CGAL/Polygon_repair/repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h index cdeda55d4878..29ea87a6272a 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/repair.h @@ -37,14 +37,9 @@ class Polygon_repair; /// \tparam Kernel parameter of the input and output polygons /// \tparam Container parameter of the input and output polygons /// \tparam Rule must be `Even_odd_rule` -template -Multipolygon_with_holes_2 repair(const Polygon_2& p , Rule rule) { - CGAL_assertion(false); // rule not implemented - return Multipolygon_with_holes_2(); - } - -template -Multipolygon_with_holes_2 repair(const Polygon_2& p, Even_odd_rule) { +template +Multipolygon_with_holes_2 repair(const Polygon_2& p , Rule rule = Rule()) +{ CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { @@ -58,14 +53,9 @@ Multipolygon_with_holes_2 repair(const Polygon_2 -Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule rule) { - CGAL_assertion(false); // rule not implemented - return Multipolygon_with_holes_2(); -} - -template -Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Even_odd_rule) { +template +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule rule = Rule()) +{ CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { @@ -79,14 +69,9 @@ Multipolygon_with_holes_2 repair(const Polygon_with_holes_2 -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule rule) { - CGAL_assertion(false); // rule not implemented - return Multipolygon_with_holes_2(); -} - -template -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& mp, Even_odd_rule) { +template +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule rule = Rule()) +{ CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_even_odd(mp); if (pr.triangulation().number_of_faces() > 0) { From 7edadee3c2a42d1b87c263c849a2b4f22484ea43 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 4 Mar 2024 20:16:35 +0000 Subject: [PATCH 488/520] fix code --- Polygon_repair/include/CGAL/Polygon_repair/repair.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h index 29ea87a6272a..c2c57426467b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/repair.h @@ -73,7 +73,7 @@ template Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule rule = Rule()) { CGAL::Polygon_repair::Polygon_repair pr; - pr.add_to_triangulation_even_odd(mp); + pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { pr.label_triangulation_even_odd(); pr.reconstruct_multipolygon(); From 181cec68f4be0799c81aa3987ce8ea827ecac9cd Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 5 Mar 2024 07:16:44 +0000 Subject: [PATCH 489/520] conversion warning --- Polygon_repair/include/CGAL/Polygon_repair/repair.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h index c2c57426467b..d40fad299f45 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/repair.h @@ -649,7 +649,7 @@ class Polygon_repair { // Create polygons with holes and put in multipolygon std::set, Polygon_with_holes_less> ordered_polygons; - for (int i = 0; i < polygons.size(); ++i) { + for (std::size_t i = 0; i < polygons.size(); ++i) { ordered_polygons.insert(Polygon_with_holes_2(polygons[i], holes[i].begin(), holes[i].end())); } for (auto const& polygon: ordered_polygons) { From 7bf227d5e69e16fd487296eee1bda88fd937b37b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 5 Mar 2024 10:51:48 +0100 Subject: [PATCH 490/520] removed wrong model which is actually a concept --- Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h index 88d4727a83b6..9f2e62d5db4a 100644 --- a/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h +++ b/Orthtree/doc/Orthtree/Concepts/OrthtreeTraits.h @@ -6,7 +6,6 @@ template parameter of the `CGAL::Orthtree` class. \cgalHasModelsBegin - \cgalHasModels{CGAL::Orthtree_traits_with_data} \cgalHasModels{CGAL::Orthtree_traits_point} \cgalHasModels{CGAL::Orthtree_traits_face_graph} \cgalHasModels{CGAL::Orthtree_traits_base} From 22e5a3f65134e1ab41c1eed94aebca4844b6eaef Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 5 Mar 2024 10:52:34 +0100 Subject: [PATCH 491/520] renaming nearest_k_neighbors_in_radius and neighbors_in_radius --- .../examples/Orthtree/octree_find_nearest_neighbor.cpp | 4 ++-- Orthtree/include/CGAL/Orthtree.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp index f5c151749eb1..2d687aa42159 100644 --- a/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp +++ b/Orthtree/examples/Orthtree/octree_find_nearest_neighbor.cpp @@ -57,7 +57,7 @@ int main(int argc, char** argv) { typename Octree::Sphere s(points_to_find[0], 0.0375); std::cout << std::endl << "Closest points within the sphere around " << s.center() << " with squared radius of " << s.squared_radius() << ":" << std::endl; - octree.neighbors_in_radius + octree.neighbors_within_radius (s, boost::make_function_output_iterator ([&](const Point_set::Index& nearest) @@ -67,7 +67,7 @@ int main(int argc, char** argv) { std::size_t k = 3; std::cout << std::endl << "The up to " << k << " closest points to(" << s.center() << ") within a squared radius of " << s.squared_radius() << " are:" << std::endl; - octree.nearest_k_neighbors_in_radius + octree.nearest_k_neighbors_within_radius (s, k, boost::make_function_output_iterator ([&](const Point_set::Index& nearest) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2028c9f72a8e..2ea9f85f115e 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -688,7 +688,7 @@ class Orthtree { Sphere query_sphere(query, (std::numeric_limits::max)()); CGAL_precondition(k > 0); - return nearest_k_neighbors_in_radius(query_sphere, k, output); + return nearest_k_neighbors_within_radius(query_sphere, k, output); } /*! @@ -705,8 +705,8 @@ class Orthtree { \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. */ template - auto neighbors_in_radius(const Sphere& query, OutputIterator output) const -> std::enable_if_t { - return nearest_k_neighbors_in_radius(query, (std::numeric_limits::max)(), output); + auto neighbors_within_radius(const Sphere& query, OutputIterator output) const -> std::enable_if_t { + return nearest_k_neighbors_within_radius(query, (std::numeric_limits::max)(), output); } /*! @@ -727,7 +727,7 @@ class Orthtree { \warning Nearest neighbor searches requires `GeomTraits` to be a model of `CollectionPartitioningOrthtreeTraits`. */ template - auto nearest_k_neighbors_in_radius( + auto nearest_k_neighbors_within_radius( const Sphere& query, std::size_t k, OutputIterator output From 0abf0319d9596746888dfa3e7500b9719accecdb Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 5 Mar 2024 12:31:59 +0100 Subject: [PATCH 492/520] doc update on migration --- Orthtree/doc/Orthtree/Orthtree.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index e82ea0d25bcd..8ec0b1bdba62 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -312,18 +312,30 @@ Orthtree::Node root = orthtree.root(); Orthtree::Node child = root[0]; bool is_leaf = child.is_leaf(); -for (Orthtree::Node::const_iterator it = child.begin(); it != child.end(); it++) +for (Orthtree::Node::const_iterator it = child.begin(); it != child.end(); it++) { + const Orthtree::Point &p = *it; ... +} \endcode \cgal 6.0 node access is now: \code{.cpp} +using Point = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; +Point_set points; +... Orthtree::Node_index root = orthtree.root(); Orthtree::Node_index child = orthtree.child(root, 0); bool is_leaf = orthtree.is_leaf(child); -for (const Orthtree::Traits::Node_data_element &idx : orthtree.data(child)) - ... // may require the use of a point map to retrieve the point from idx +for (Octree::Traits::Node_data_element& e : octree.data(child)) { + // Using a pointmap is necessary when using a Point_set_3. + Point& p = points.point(e); + + // If the orthtree is used with a std::vector, Node_data_element is identical to Point. + // Point& p = e; + ... +} \endcode The nearest neighbor search behaves as before, however, the output iterator will be required to store `CollectionPartitioningOrthtreeTraits::Node_data_element`. This may be the actual point or, e.g., in case a `Point_set_3` is used, an index which requires the use of a point map to retrieve the point. From 0f532d78e411d57db478afed6da56ab4ca2e1070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 5 Mar 2024 13:04:14 +0100 Subject: [PATCH 493/520] ws --- Orthtree/doc/Orthtree/Orthtree.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orthtree/doc/Orthtree/Orthtree.txt b/Orthtree/doc/Orthtree/Orthtree.txt index 8ec0b1bdba62..eb7b2559b2ac 100644 --- a/Orthtree/doc/Orthtree/Orthtree.txt +++ b/Orthtree/doc/Orthtree/Orthtree.txt @@ -331,7 +331,7 @@ bool is_leaf = orthtree.is_leaf(child); for (Octree::Traits::Node_data_element& e : octree.data(child)) { // Using a pointmap is necessary when using a Point_set_3. Point& p = points.point(e); - + // If the orthtree is used with a std::vector, Node_data_element is identical to Point. // Point& p = e; ... From b34d9d6808e661adc3db62e02dcc1f30f32b8486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 5 Mar 2024 13:13:25 +0100 Subject: [PATCH 494/520] use raw string --- Documentation/doc/scripts/html_output_post_processing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/doc/scripts/html_output_post_processing.py b/Documentation/doc/scripts/html_output_post_processing.py index 90e8547eb587..a253f3147150 100755 --- a/Documentation/doc/scripts/html_output_post_processing.py +++ b/Documentation/doc/scripts/html_output_post_processing.py @@ -184,7 +184,7 @@ def automagically_number_figures(): for el in d('a.elRef'): text = pq(el).attr('href') if text.find("index.html")!=-1: - re_pkg_index=re.compile("\.\./([A-Z_a-z0-9]+)/index\.html") + re_pkg_index=re.compile(r'\.\./([A-Z_a-z0-9]+)/index\.html') res=re_pkg_index.match(text) if res: all_packages.append(res.group(1)) @@ -248,7 +248,7 @@ def main(): #workaround issue with operator<< in pyquery all_pages=glob.glob('*/*.html') for f in all_pages: - re_replace_in_file("operator<<\(\)", "operator<<()", f) + re_replace_in_file(r'operator<<\(\)', "operator<<()", f) # number figure automagically_number_figures() @@ -319,7 +319,7 @@ def main(): filesjs_files=package_glob('./*/files.js') for fn in filesjs_files: - re_replace_in_file('^.*\[ "Concepts",.*$', '', fn) + re_replace_in_file(r'^.*\[ "Concepts",.*$', '', fn) #Rewrite the path of some images re_replace_in_file("'src','ftv2", @@ -329,7 +329,7 @@ def main(): # external is placed by doxygen to mark a class from a tagfile, this # is more confusing then helpful in our case if path.isfile(os.path.join('Manual','annotated.html')): - re_replace_in_file('\[external\]', '', os.path.join('Manual','annotated.html')) + re_replace_in_file(r'\[external\]', '', os.path.join('Manual','annotated.html')) else: stderr.write("Error: ./Manual/annotated.html does not exists\n") # fix class/concept mismatch in generated pages From b13b01af997d914c5073590652f64e220257988f Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 6 Mar 2024 08:13:12 +0000 Subject: [PATCH 495/520] Remove argc, argv --- Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp | 2 +- Polygon/examples/Polygon/multipolygon.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp index db7927c8ba5d..bda77eb63236 100644 --- a/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp +++ b/Polygon/examples/Polygon/draw_multipolygon_with_holes.cpp @@ -9,7 +9,7 @@ using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -int main(int argc, char* argv[]) { +int main() { std::string wkt = "MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),((0.2 0.2,0.8 0.2,0.8 0.8,0.2 0.8,0.2 0.2),(0.3 0.3,0.3 0.7,0.7 0.7,0.7 0.3,0.3 0.3)))"; std::istringstream iss(wkt); Multipolygon_with_holes_2 mp; diff --git a/Polygon/examples/Polygon/multipolygon.cpp b/Polygon/examples/Polygon/multipolygon.cpp index babf3def44d6..31ce7d37401c 100644 --- a/Polygon/examples/Polygon/multipolygon.cpp +++ b/Polygon/examples/Polygon/multipolygon.cpp @@ -7,7 +7,7 @@ using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -int main(int argc, char* argv[]) { +int main() { Point_2 p1_outer[] = {Point_2(0,0), Point_2(1,0), Point_2(1,1), Point_2(0,1)}; Point_2 p1_inner[] = {Point_2(0.2,0.2), Point_2(0.8,0.2), Point_2(0.8,0.8), Point_2(0.2,0.8)}; From b9f3e351921db084cefb5846cb5658a43bbfefee Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 6 Mar 2024 08:36:36 +0000 Subject: [PATCH 496/520] Address warning and assert the Rule type --- .../examples/Polygon_repair/repair_polygon_2.cpp | 2 +- Polygon_repair/include/CGAL/Polygon_repair/repair.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp index 9004d092abd2..be6a011b4a0c 100644 --- a/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp +++ b/Polygon_repair/examples/Polygon_repair/repair_polygon_2.cpp @@ -11,7 +11,7 @@ using Polygon_2 = CGAL::Polygon_2; using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; -int main(int argc, char* argv[]) { +int main() { std::ifstream in("data/bridge-edge.wkt"); Polygon_with_holes_2 pin; CGAL::IO::read_polygon_WKT(in, pin); diff --git a/Polygon_repair/include/CGAL/Polygon_repair/repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h index d40fad299f45..523764ce508b 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/repair.h @@ -38,8 +38,9 @@ class Polygon_repair; /// \tparam Container parameter of the input and output polygons /// \tparam Rule must be `Even_odd_rule` template -Multipolygon_with_holes_2 repair(const Polygon_2& p , Rule rule = Rule()) +Multipolygon_with_holes_2 repair(const Polygon_2& p , Rule = Rule()) { + static_assert(std::is_same_v); CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { @@ -54,8 +55,9 @@ Multipolygon_with_holes_2 repair(const Polygon_2 -Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule rule = Rule()) +Multipolygon_with_holes_2 repair(const Polygon_with_holes_2& p, Rule = Rule()) { + static_assert(std::is_same_v); CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { @@ -70,8 +72,9 @@ Multipolygon_with_holes_2 repair(const Polygon_with_holes_2 -Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule rule = Rule()) +Multipolygon_with_holes_2 repair(const Multipolygon_with_holes_2& p, Rule = Rule()) { + static_assert(std::is_same_v); CGAL::Polygon_repair::Polygon_repair pr; pr.add_to_triangulation_even_odd(p); if (pr.triangulation().number_of_faces() > 0) { From f716cc02605da50af6e2fcfec0162deef954d9a4 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 Mar 2024 10:25:30 +0100 Subject: [PATCH 497/520] fix a bug with devicePixelRatio and picking --- GraphicsView/include/CGAL/Qt/qglviewer_impl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h index 3cca531c5fb2..7387b1f48745 100644 --- a/GraphicsView/include/CGAL/Qt/qglviewer_impl.h +++ b/GraphicsView/include/CGAL/Qt/qglviewer_impl.h @@ -1047,6 +1047,9 @@ static QString mouseButtonsString(::Qt::MouseButtons b) { CGAL_INLINE_FUNCTION void CGAL::QGLViewer::performClickAction(qglviewer::ClickAction ca, const QMouseEvent *const e) { + // the following call is needed to update the pixel ratio + camera()->setScreenWidthAndHeight(this->width(), this->height(), this->devicePixelRatio()); + // Note: action that need it should call update(). switch (ca) { // # CONNECTION setMouseBinding prevents adding NO_CLICK_ACTION in From 2c8567ed16d16650e517cbba7688965f964ee313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 6 Mar 2024 10:48:52 +0100 Subject: [PATCH 498/520] add overload for Quotent as specialization for std::arithmetic result in undefined behavior --- Filtered_kernel/include/CGAL/Lazy.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Filtered_kernel/include/CGAL/Lazy.h b/Filtered_kernel/include/CGAL/Lazy.h index 463ef501b343..dcfb877ee8e5 100644 --- a/Filtered_kernel/include/CGAL/Lazy.h +++ b/Filtered_kernel/include/CGAL/Lazy.h @@ -108,6 +108,10 @@ templateinline std::enable_if_t::value||std::is_e templateinline std::enable_if_t::value||std::is_enum::value, T> exact (T d){return d;} templateinline std::enable_if_t::value||std::is_enum::value, int> depth(T){return -1;} +templateinline std::enable_if_t::value, Quotient> approx(Quotient d){return d;} +templateinline std::enable_if_t::value, Quotient> exact (Quotient d){return d;} +templateinline std::enable_if_t::value, int> depth(Quotient){return -1;} + // For tag classes: Return_base_tag, Homogeneous_tag, Null_vector, Origin templateinline std::enable_if_t::value, T> exact(T){return {};} templateinline std::enable_if_t::value, T> approx(T){return {};} From 9b4f35dfa1ef528794360f501ead82594fff68d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 6 Mar 2024 11:32:30 +0100 Subject: [PATCH 499/520] fix dependencies --- Bounding_volumes/package_info/Bounding_volumes/dependencies | 1 + Generator/package_info/Generator/dependencies | 1 + Inscribed_areas/package_info/Inscribed_areas/dependencies | 1 + Nef_2/package_info/Nef_2/dependencies | 1 + Polygon/package_info/Polygon/dependencies | 3 +++ .../package_info/Set_movable_separability_2/dependencies | 3 +++ Stream_support/package_info/Stream_support/dependencies | 1 + 7 files changed, 11 insertions(+) diff --git a/Bounding_volumes/package_info/Bounding_volumes/dependencies b/Bounding_volumes/package_info/Bounding_volumes/dependencies index 99a12a27c822..6a7b86ebd72c 100644 --- a/Bounding_volumes/package_info/Bounding_volumes/dependencies +++ b/Bounding_volumes/package_info/Bounding_volumes/dependencies @@ -4,6 +4,7 @@ Cartesian_kernel Circulator Distance_2 Distance_3 +Filtered_kernel Installation Intersections_2 Intersections_3 diff --git a/Generator/package_info/Generator/dependencies b/Generator/package_info/Generator/dependencies index 04faaf3547dd..925a743b87c0 100644 --- a/Generator/package_info/Generator/dependencies +++ b/Generator/package_info/Generator/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations BGL Circulator +Filtered_kernel Generator Installation Interval_support diff --git a/Inscribed_areas/package_info/Inscribed_areas/dependencies b/Inscribed_areas/package_info/Inscribed_areas/dependencies index da58f1b4f087..fc0704df4432 100644 --- a/Inscribed_areas/package_info/Inscribed_areas/dependencies +++ b/Inscribed_areas/package_info/Inscribed_areas/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations Circulator Distance_2 +Filtered_kernel Inscribed_areas Installation Interval_support diff --git a/Nef_2/package_info/Nef_2/dependencies b/Nef_2/package_info/Nef_2/dependencies index cca2b94b65fb..1abad5d8686e 100644 --- a/Nef_2/package_info/Nef_2/dependencies +++ b/Nef_2/package_info/Nef_2/dependencies @@ -5,6 +5,7 @@ Cartesian_kernel Circulator Distance_2 Distance_3 +Filtered_kernel HalfedgeDS Hash_map Homogeneous_kernel diff --git a/Polygon/package_info/Polygon/dependencies b/Polygon/package_info/Polygon/dependencies index 3312eb37e462..7ed360974c5f 100644 --- a/Polygon/package_info/Polygon/dependencies +++ b/Polygon/package_info/Polygon/dependencies @@ -1,8 +1,11 @@ Algebraic_foundations Circulator +Filtered_kernel GraphicsView Installation +Interval_support Kernel_23 +Modular_arithmetic Number_types Polygon Profiling_tools diff --git a/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies b/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies index e88f195eeab2..c84b76944600 100644 --- a/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies +++ b/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies @@ -1,7 +1,10 @@ Algebraic_foundations Circulator +Filtered_kernel Installation +Interval_support Kernel_23 +Modular_arithmetic Number_types Polygon Profiling_tools diff --git a/Stream_support/package_info/Stream_support/dependencies b/Stream_support/package_info/Stream_support/dependencies index 86fd53b13ace..f45a1a985bf5 100644 --- a/Stream_support/package_info/Stream_support/dependencies +++ b/Stream_support/package_info/Stream_support/dependencies @@ -1,6 +1,7 @@ Algebraic_foundations BGL Circulator +Filtered_kernel Installation Interval_support Kernel_23 From 786b01574616896d277f02f697188a8f59dbe65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 6 Mar 2024 17:53:07 +0100 Subject: [PATCH 500/520] doc copy and move constructors --- Orthtree/include/CGAL/Orthtree.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 2ea9f85f115e..8521e37cfce0 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -283,9 +283,7 @@ class Orthtree { : Orthtree(Traits(std::forward(arg1), std::forward(arg2), std::forward(args)...)) {} - /// @} - - // copy constructor + /// copy constructor explicit Orthtree(const Orthtree& other) : m_traits(other.m_traits), m_node_properties(other.m_node_properties), @@ -296,7 +294,7 @@ class Orthtree { m_node_children(m_node_properties.template get_property>("children")), m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) {} - // move constructor + /// move constructor explicit Orthtree(Orthtree&& other) : m_traits(other.m_traits), m_node_properties(std::move(other.m_node_properties)), @@ -307,10 +305,12 @@ class Orthtree { m_node_children(m_node_properties.template get_property>("children")), m_bbox(other.m_bbox), m_side_per_depth(other.m_side_per_depth) { - // todo: makes sure moved-from is still valid. Maybe this shouldn't be necessary. other.m_node_properties.emplace(); } + /// @} + + // Non-necessary but just to be clear on the rule of 5: // assignment operators deleted From 1e71bbe0a0de66a3254c42d7a3206f185a48c4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 6 Mar 2024 18:00:21 +0100 Subject: [PATCH 501/520] update changes --- Installation/CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 09bde261d24b..8ec9ae609a36 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -38,6 +38,12 @@ Release date: October 2023 - Removed the class templates `Gray_image_mesh_domain_3`, `Implicit_mesh_domain_3`, and `Labeled_image_mesh_domain_3` which are deprecated since CGAL-4.13. +### [Quadtrees, Octrees, and Orthtrees](https://doc.cgal.org/6.0/Manual/packages.html#PkgOrthtree) +- **Breaking change**: + - Node splitting behavior and per-node data are now customizable via the Traits class. + - Nodes are now stored as a property map, with properties of each node accessed by index. + - Nearest neighbors functions only work for Orthtrees which provide the necessary functionality. + ### [Polygon Mesh Processing](https://doc.cgal.org/6.0/Manual/packages.html#PkgPolygonMeshProcessing) - Added the function `CGAL::Polygon_mesh_processing::interpolated_corrected_curvatures()` which can be used to compute From 9e19fd1d7911e8e55445870701b873d4a4af046f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 7 Mar 2024 16:00:43 +0100 Subject: [PATCH 502/520] C3t3 item: fix a segfault --- Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp b/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp index 507122e3de6d..58c66145fcea 100644 --- a/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_triangulation_3_item.cpp @@ -2198,7 +2198,7 @@ void Scene_triangulation_3_item::computeIntersection() void Scene_triangulation_3_item::set_cut_edge(bool b) { d->cut_edges = b; - d->intersection->setCutEdges(b); + if(d->intersection) d->intersection->setCutEdges(b); Q_EMIT redraw(); } From 5e3cf4376ecf51a2e1682405431c8092ae818b68 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 8 Mar 2024 10:55:42 +0000 Subject: [PATCH 503/520] flush --- Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 2758dd4aebdf..4456cb88f1e5 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -59,8 +59,8 @@ int main(int argc, char* argv[]) { } else { std::cout << "fail" << std::endl; std::cout << "\tin: " << in << std::endl; - std::cout << "\tout: " << out; - std::cout << "\tref: " << ref; + std::cout << "\tout: " << out << std::flush; + std::cout << "\tref: " << ref << std::flush; } CGAL_assertion(ref == out); // Test orientations From 6744cc1dd66573041e3920bf7ec3e9adfe920da8 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 11 Mar 2024 16:40:30 +0100 Subject: [PATCH 504/520] Add operator==() for Multipolygon_wih_holes --- .../include/CGAL/Multipolygon_with_holes_2.h | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Polygon/include/CGAL/Multipolygon_with_holes_2.h b/Polygon/include/CGAL/Multipolygon_with_holes_2.h index 7cabd2871fe2..50edf6d592d2 100644 --- a/Polygon/include/CGAL/Multipolygon_with_holes_2.h +++ b/Polygon/include/CGAL/Multipolygon_with_holes_2.h @@ -92,6 +92,45 @@ class Multipolygon_with_holes_2 { Polygon_with_holes_container m_polygons; }; + +template +bool operator==(const Multipolygon_with_holes_2& p1, + const Multipolygon_with_holes_2& p2) +{ + typedef typename + Multipolygon_with_holes_2::Polygon_with_holes_const_iterator HCI; + typedef CGAL::Polygon_with_holes_2 Polygon_2; + if(&p1 == &p2) + return (true); + + if(p1.number_of_polygons_with_holes() != p2.number_of_polygons_with_holes()) + return (false); + + std::list tmp_list(p2.polygons_with_holes_begin(), p2.polygons_with_holes_end()); + + HCI i = p1.polygons_with_holes_begin(); + for(; i!= p1.polygons_with_holes_end(); ++i) + { + typename std::list::iterator j = + (std::find(tmp_list.begin(), tmp_list.end(), *i)); + + if(j == tmp_list.end()) + return (false); + + tmp_list.erase(j); + } + + + CGAL_assertion(tmp_list.empty()); + return (true); +} + +template +inline bool operator!=(const Multipolygon_with_holes_2& p1, + const Multipolygon_with_holes_2& p2) +{ + return (!(p1==p2)); +} /*! inserts a multipolygon with holes to the output stream `os`. From cf0c917b0234b6186631ef9b3b7b6f155ecb6ed2 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 12 Mar 2024 09:22:33 +0100 Subject: [PATCH 505/520] fix unused variables --- .../test/Polygon_repair/draw_test_polygons.cpp | 2 +- Polygon_repair/test/Polygon_repair/exact_test.cpp | 2 +- .../test/Polygon_repair/repair_polygon_2_test.cpp | 10 ++++++---- Polygon_repair/test/Polygon_repair/validate_wkt.cpp | 2 +- .../Polygon_repair/write_labeled_triangulation.cpp | 2 +- .../test/Polygon_repair/write_repaired_polygons.cpp | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp index 7161506f952a..a61e8f80531b 100644 --- a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -17,7 +17,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(int argc, char* argv[]) { +int main() { for (const auto& file: std::filesystem::directory_iterator("data/in")) { if (file.path().filename().extension() != ".wkt") continue; diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp index 4ed815ff9ad2..4182f8faa061 100644 --- a/Polygon_repair/test/Polygon_repair/exact_test.cpp +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -19,7 +19,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(int argc, char* argv[]) { +int main() { std::string in = "POLYGON((0.03 0.02,0.97 0.01,0.99 0.96,0.04 0.98,0.03 0.02),(0.5 0.5,1.5 0.5,0.5 0.5,1.5 0.7,0.5 0.5,1.5 0.9,0.5 0.5,1.5 1.1,0.5 0.5,1.5 1.3,0.5 0.5,1.5 1.5,0.5 0.5,1.3 1.5,0.5 0.5,1.1 1.5,0.5 0.5,0.9 1.5,0.5 0.5,0.7 1.5,0.5 0.5,0.5 1.5,0.5 0.5,0.3 1.5,0.5 0.5,0.1 1.5,0.5 0.5,-0.1 1.5,0.5 0.5,-0.3 1.5,0.5 0.5,-0.5 1.5,0.5 0.5,-0.5 1.3,0.5 0.5,-0.5 1.1,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.9,0.5 0.5,-0.5 0.7,0.5 0.5,-0.5 0.5,0.5 0.5,-0.5 0.3,0.5 0.5,-0.5 0.1,0.5 0.5,-0.5 -0.1,0.5 0.5,-0.5 -0.3,0.5 0.5,-0.5 -0.5,0.5 0.5,-0.3 -0.5,0.5 0.5,-0.1 -0.5,0.5 0.5,0.1 -0.5,0.5 0.5,0.3 -0.5,0.5 0.5,0.5 -0.5,0.5 0.5,0.7 -0.5,0.5 0.5,0.9 -0.5,0.5 0.5,1.1 -0.5,0.5 0.5,1.3 -0.5,0.5 0.5,1.5 -0.5,0.5 0.5,1.5 -0.3,0.5 0.5,1.5 -0.1,0.5 0.5,1.5 0.1,0.5 0.5,1.5 0.3,0.5 0.5))"; std::istringstream iss(in); diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 4456cb88f1e5..72399b5c72e4 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Point_2 = Kernel::Point_2; @@ -14,7 +15,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(int argc, char* argv[]) { +int main(]) { for (const auto& file: std::filesystem::directory_iterator("data/in")) { if (file.path().filename().extension() != ".wkt") continue; @@ -61,13 +62,14 @@ int main(int argc, char* argv[]) { std::cout << "\tin: " << in << std::endl; std::cout << "\tout: " << out << std::flush; std::cout << "\tref: " << ref << std::flush; - } CGAL_assertion(ref == out); + } + assert(ref == out); // Test orientations for (auto const& polygon: rmp.polygons_with_holes()) { - CGAL_assertion(polygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE); + assertpolygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE); for (auto const &hole: polygon.holes()) { - CGAL_assertion(hole.orientation() == CGAL::CLOCKWISE); + assert(hole.orientation() == CGAL::CLOCKWISE); } } } diff --git a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp index 281da7542f27..af560d376291 100644 --- a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp +++ b/Polygon_repair/test/Polygon_repair/validate_wkt.cpp @@ -15,7 +15,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(int argc, char* argv[]) { +int main() { // std::string folder = "/Users/ken/Downloads/big polygons/"; std::string folder = "data/in"; diff --git a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp index 24d542c7ce1f..5b21e141505a 100644 --- a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp +++ b/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp @@ -12,7 +12,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(int argc, char* argv[]) { +int main() { std::ifstream ifs("data/in/nesting-spike.wkt"); diff --git a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp index 604e04a0ec8d..e31996ae33eb 100644 --- a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp @@ -14,7 +14,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(int argc, char* argv[]) { +int main() { // std::ifstream ifs("/Users/ken/Downloads/180927.wkt"); // std::ofstream ofs("/Users/ken/Downloads/1.geojson"); From a3fab59c1764847566911537c39e563cb87b2570 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 12 Mar 2024 09:25:56 +0100 Subject: [PATCH 506/520] fix unused variables --- Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 72399b5c72e4..de36b0f08b7f 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -15,7 +15,7 @@ using Polygon_with_holes_2 = CGAL::Polygon_with_holes_2; using Multipolygon_with_holes_2 = CGAL::Multipolygon_with_holes_2; using Polygon_repair = CGAL::Polygon_repair::Polygon_repair; -int main(]) { +int main() { for (const auto& file: std::filesystem::directory_iterator("data/in")) { if (file.path().filename().extension() != ".wkt") continue; @@ -67,7 +67,7 @@ int main(]) { // Test orientations for (auto const& polygon: rmp.polygons_with_holes()) { - assertpolygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE); + assert(polygon.outer_boundary().orientation() == CGAL::COUNTERCLOCKWISE); for (auto const &hole: polygon.holes()) { assert(hole.orientation() == CGAL::CLOCKWISE); } From 3bfc61af588db468472ef30281e8565a61e5c9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 12 Mar 2024 10:39:28 +0100 Subject: [PATCH 507/520] pass the VPM to polyline graph --- .../include/CGAL/Polygon_mesh_processing/region_growing.h | 5 +++-- 1 file changed, 3 insertions(+), 2 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 fc594b9122f1..d6f78085c448 100644 --- a/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h +++ b/Shape_detection/include/CGAL/Polygon_mesh_processing/region_growing.h @@ -354,6 +354,7 @@ detect_corners_of_regions( using parameters::get_parameter; using parameters::is_default_parameter; + using VPM = typename GetVertexPointMap < PolygonMesh, NamedParameters>::const_type; using Traits = typename GetGeomTraits::type; using Graph_traits = boost::graph_traits; using halfedge_descriptor = typename Graph_traits::halfedge_descriptor; @@ -377,7 +378,7 @@ detect_corners_of_regions( } Ecm ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), dynamic_ecm); - using Polyline_graph = CGAL::Shape_detection::Polygon_mesh::Polyline_graph; + using Polyline_graph = CGAL::Shape_detection::Polygon_mesh::Polyline_graph; using Segment_map = typename Polyline_graph::Segment_map; using Item = typename Polyline_graph::Item; @@ -424,7 +425,7 @@ detect_corners_of_regions( filtered_edges.push_back(e); } - Polyline_graph pgraph(mesh, filtered_edges, region_map); + Polyline_graph pgraph(mesh, filtered_edges, region_map, np); const auto& segment_range = pgraph.segment_range(); Line_region line_region(np.segment_map(pgraph.segment_map())); From 511cc507aeec12ed7ad8e8b72286cebaecc098be Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 12 Mar 2024 15:26:10 +0100 Subject: [PATCH 508/520] Do not compare strings, and do a write/read back in order to round --- .../test/Polygon_repair/repair_polygon_2_test.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index de36b0f08b7f..6b673faf6396 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -27,7 +27,7 @@ int main() { // Load test file and repair to create output std::istringstream iss(in); - Multipolygon_with_holes_2 rmp; + Multipolygon_with_holes_2 rmp, refmp; if (in.find("POLYGON") == 0) { Polygon_with_holes_2 p; if (in != "POLYGON()") { // maybe should be checked in WKT reader @@ -37,9 +37,11 @@ int main() { Multipolygon_with_holes_2 mp; CGAL::IO::read_multi_polygon_WKT(iss, mp); rmp = CGAL::Polygon_repair::repair(mp, CGAL::Polygon_repair::Even_odd_rule()); - } std::ostringstream oss; + } std::stringstream oss; CGAL::IO::write_multi_polygon_WKT(oss, rmp); std::string out = oss.str(); + rmp.clear(); + CGAL::IO::read_multi_polygon_WKT(oss, rmp); // Read reference file std::string ref_path = "data/ref/"; @@ -53,9 +55,11 @@ int main() { } std::string ref; std::getline(ref_ifs, ref); ref += "\n"; + std::stringstream refss(ref); + CGAL::IO::read_multi_polygon_WKT(refss, refmp); // Compare output with reference file - if (ref == out) { + if (rmp == refmp) { std::cout << "ok" << std::endl; } else { std::cout << "fail" << std::endl; @@ -63,7 +67,7 @@ int main() { std::cout << "\tout: " << out << std::flush; std::cout << "\tref: " << ref << std::flush; } - assert(ref == out); + assert(rmp == refmp); // Test orientations for (auto const& polygon: rmp.polygons_with_holes()) { From 79ec44651df6e81d6e178ba949c636a7fdfcc216 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 13 Mar 2024 16:02:57 +0100 Subject: [PATCH 509/520] Add #includes --- Polygon_repair/include/CGAL/Polygon_repair/repair.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Polygon_repair/include/CGAL/Polygon_repair/repair.h b/Polygon_repair/include/CGAL/Polygon_repair/repair.h index 523764ce508b..0188f1630fb1 100644 --- a/Polygon_repair/include/CGAL/Polygon_repair/repair.h +++ b/Polygon_repair/include/CGAL/Polygon_repair/repair.h @@ -14,6 +14,11 @@ #include +#include +#include +#include +#include + #include #include #include From b0ac1d1c19ef1f2e4b0a54d45db1b97b323e27e1 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 13 Mar 2024 16:33:38 +0100 Subject: [PATCH 510/520] Move files from test to benchmark --- Polygon_repair/{test => benchmark}/Polygon_repair/clipart.cpp | 0 .../{test => benchmark}/Polygon_repair/validate_wkt.cpp | 0 .../Polygon_repair/write_labeled_triangulation.cpp | 0 .../Polygon_repair/write_repaired_polygons.cpp | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename Polygon_repair/{test => benchmark}/Polygon_repair/clipart.cpp (100%) rename Polygon_repair/{test => benchmark}/Polygon_repair/validate_wkt.cpp (100%) rename Polygon_repair/{test => benchmark}/Polygon_repair/write_labeled_triangulation.cpp (100%) rename Polygon_repair/{test => benchmark}/Polygon_repair/write_repaired_polygons.cpp (100%) diff --git a/Polygon_repair/test/Polygon_repair/clipart.cpp b/Polygon_repair/benchmark/Polygon_repair/clipart.cpp similarity index 100% rename from Polygon_repair/test/Polygon_repair/clipart.cpp rename to Polygon_repair/benchmark/Polygon_repair/clipart.cpp diff --git a/Polygon_repair/test/Polygon_repair/validate_wkt.cpp b/Polygon_repair/benchmark/Polygon_repair/validate_wkt.cpp similarity index 100% rename from Polygon_repair/test/Polygon_repair/validate_wkt.cpp rename to Polygon_repair/benchmark/Polygon_repair/validate_wkt.cpp diff --git a/Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp b/Polygon_repair/benchmark/Polygon_repair/write_labeled_triangulation.cpp similarity index 100% rename from Polygon_repair/test/Polygon_repair/write_labeled_triangulation.cpp rename to Polygon_repair/benchmark/Polygon_repair/write_labeled_triangulation.cpp diff --git a/Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp b/Polygon_repair/benchmark/Polygon_repair/write_repaired_polygons.cpp similarity index 100% rename from Polygon_repair/test/Polygon_repair/write_repaired_polygons.cpp rename to Polygon_repair/benchmark/Polygon_repair/write_repaired_polygons.cpp From fcbf327b16a459735bfab7811c6b4a79c1ad207d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 13 Mar 2024 17:12:24 +0100 Subject: [PATCH 511/520] workaround absence of --- .../Polygon_repair/repair_polygon_2_test.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 6b673faf6396..3ca4f4225df7 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -3,6 +3,19 @@ #include #include + +// work around for old compilers (Apple clang < 11 for example) +#define HAS_FILESYSTEM 1 +#if (__has_include) +#if !__has_include() +#undef HAS_FILESYSTEM +#define HAS_FILESYSTEM 0 +#endif +#endif + + +#if HAS_FILESYSTEM + #include #include #include @@ -80,3 +93,13 @@ int main() { return 0; } + +#else + +int main() +{ + std::cout << "Warning: filesystem feature is not present on the system, nothing will be tested\n"; + return 0; +} + +#endif From 426f5067c073467f8b45e3bdead59973f74d58ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 13 Mar 2024 17:29:30 +0100 Subject: [PATCH 512/520] workaround warning --- Orthtree/include/CGAL/Orthtree_traits_face_graph.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h index c0b98ff40e7a..ffa353cd6133 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_face_graph.h +++ b/Orthtree/include/CGAL/Orthtree_traits_face_graph.h @@ -76,15 +76,14 @@ struct Orthtree_traits_face_graph : public Orthtree_traits_base< std::array min = {0.0, 0}, max = {0.0, 0}; if (faces(m_pm).begin() != faces(m_pm).end()) { - const Point_d& p = get(m_vpm, *vertices(m_pm).begin()); - min = {p.x(), p.y(), p.z()}; - max = min; + bool first = true; for (auto v: vertices(m_pm)) { const Point_d& p_v = get(m_vpm, v); for (int i = 0; i < 3; ++i) { - if (p_v[i] < min[i]) min[i] = p_v[i]; - if (p_v[i] > max[i]) max[i] = p_v[i]; + if (first || p_v[i] < min[i]) min[i] = p_v[i]; + if (first || p_v[i] > max[i]) max[i] = p_v[i]; } + first=false; } } From 5331c7e7443e071d9cd0e3d00d84741d0b990c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 13 Mar 2024 17:59:08 +0100 Subject: [PATCH 513/520] move after definition of function used --- Stream_support/include/CGAL/IO/WKT.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index f558c90aea28..3a3eea129d64 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -355,13 +355,6 @@ bool read_multi_polygon_WKT(std::istream& in, } -template -std::ostream& write_multi_polygon_WKT(std::ostream& out, - Multipolygon_with_holes_2& mp) -{ - return write_multi_polygon_WKT(out, mp.polygons_with_holes()); -} - //! \ingroup PkgStreamSupportIoFuncsWKT //! //! \brief writes `point` into a WKT stream. @@ -465,6 +458,13 @@ std::ostream& write_multi_polygon_WKT(std::ostream& out, return out; } +template +std::ostream& write_multi_polygon_WKT(std::ostream& out, + Multipolygon_with_holes_2& mp) +{ + return write_multi_polygon_WKT(out, mp.polygons_with_holes()); +} + //! \ingroup PkgStreamSupportIoFuncsWKT //! //! \brief writes the content of `mls` into a WKT stream. From f34745c4d87c94f954fe1e6b7a3a1b3dbdf85e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 14 Mar 2024 09:14:47 +0100 Subject: [PATCH 514/520] use a functor for collapsing the DAG to not depend on Filtered_kernel nor inclusion order --- .../Bounding_volumes/dependencies | 1 - Filtered_kernel/include/CGAL/Lazy.h | 11 +++++++++ Generator/package_info/Generator/dependencies | 1 - .../Surface_mesh_geodesic_distances_3.h | 2 ++ .../package_info/Heat_method_3/dependencies | 1 - .../package_info/Inscribed_areas/dependencies | 1 - Nef_2/package_info/Nef_2/dependencies | 1 - Number_types/include/CGAL/utils_classes.h | 11 +++++++++ Polygon/include/CGAL/Polygon_2_algorithms.h | 8 ++++--- Polygon/package_info/Polygon/dependencies | 3 --- .../CGAL/Polygon_mesh_processing/measure.h | 24 ++++++++++++------- .../Set_movable_separability_2/dependencies | 3 --- .../package_info/Stream_support/dependencies | 1 - 13 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Bounding_volumes/package_info/Bounding_volumes/dependencies b/Bounding_volumes/package_info/Bounding_volumes/dependencies index 6a7b86ebd72c..99a12a27c822 100644 --- a/Bounding_volumes/package_info/Bounding_volumes/dependencies +++ b/Bounding_volumes/package_info/Bounding_volumes/dependencies @@ -4,7 +4,6 @@ Cartesian_kernel Circulator Distance_2 Distance_3 -Filtered_kernel Installation Intersections_2 Intersections_3 diff --git a/Filtered_kernel/include/CGAL/Lazy.h b/Filtered_kernel/include/CGAL/Lazy.h index dcfb877ee8e5..95d790654518 100644 --- a/Filtered_kernel/include/CGAL/Lazy.h +++ b/Filtered_kernel/include/CGAL/Lazy.h @@ -117,6 +117,17 @@ templateinline std::enable_if_t::value, T> exact(T){re templateinline std::enable_if_t::value, T> approx(T){return {};} templateinline std::enable_if_t::value, int> depth(T){return -1;} +namespace internal{ +template +struct Evaluate> +{ + void operator()(const Lazy& l) + { + exact(l); + } +}; +} // internal namespace + // For an iterator, exact/approx applies to the objects it points to template ::value>> auto exact(T const& t) {return make_transforming_iterator(t,[](auto const&u)->decltype(auto){return CGAL::exact(u);});} diff --git a/Generator/package_info/Generator/dependencies b/Generator/package_info/Generator/dependencies index 925a743b87c0..04faaf3547dd 100644 --- a/Generator/package_info/Generator/dependencies +++ b/Generator/package_info/Generator/dependencies @@ -1,7 +1,6 @@ Algebraic_foundations BGL Circulator -Filtered_kernel Generator Installation Interval_support diff --git a/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h b/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h index 64c1782bb9cb..46441d6f8e24 100644 --- a/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h +++ b/Heat_method_3/include/CGAL/Heat_method_3/Surface_mesh_geodesic_distances_3.h @@ -25,6 +25,8 @@ #include #include #include +#include + #ifdef CGAL_EIGEN3_ENABLED #include #endif diff --git a/Heat_method_3/package_info/Heat_method_3/dependencies b/Heat_method_3/package_info/Heat_method_3/dependencies index 50488d5544f7..f69807f53d76 100644 --- a/Heat_method_3/package_info/Heat_method_3/dependencies +++ b/Heat_method_3/package_info/Heat_method_3/dependencies @@ -4,7 +4,6 @@ Cartesian_kernel Circulator Distance_2 Distance_3 -Filtered_kernel Heat_method_3 Installation Interval_support diff --git a/Inscribed_areas/package_info/Inscribed_areas/dependencies b/Inscribed_areas/package_info/Inscribed_areas/dependencies index fc0704df4432..da58f1b4f087 100644 --- a/Inscribed_areas/package_info/Inscribed_areas/dependencies +++ b/Inscribed_areas/package_info/Inscribed_areas/dependencies @@ -1,7 +1,6 @@ Algebraic_foundations Circulator Distance_2 -Filtered_kernel Inscribed_areas Installation Interval_support diff --git a/Nef_2/package_info/Nef_2/dependencies b/Nef_2/package_info/Nef_2/dependencies index 1abad5d8686e..cca2b94b65fb 100644 --- a/Nef_2/package_info/Nef_2/dependencies +++ b/Nef_2/package_info/Nef_2/dependencies @@ -5,7 +5,6 @@ Cartesian_kernel Circulator Distance_2 Distance_3 -Filtered_kernel HalfedgeDS Hash_map Homogeneous_kernel diff --git a/Number_types/include/CGAL/utils_classes.h b/Number_types/include/CGAL/utils_classes.h index 06a569a6d8bb..1b4eb95ce89d 100644 --- a/Number_types/include/CGAL/utils_classes.h +++ b/Number_types/include/CGAL/utils_classes.h @@ -266,6 +266,17 @@ class Is_valid }; }; +namespace internal +{ +// utility class to be used for calling exact(Lazy) when doing accumulation with EPECK +template +struct Evaluate +{ + void operator()(const NT&) + {} +}; +} // internal namespace + } //namespace CGAL #endif // CGAL_UTILS_CLASSES_H diff --git a/Polygon/include/CGAL/Polygon_2_algorithms.h b/Polygon/include/CGAL/Polygon_2_algorithms.h index dc8f9c8708f5..40b1e06a9c7e 100644 --- a/Polygon/include/CGAL/Polygon_2_algorithms.h +++ b/Polygon/include/CGAL/Polygon_2_algorithms.h @@ -24,8 +24,8 @@ #include #include #include -#include #include +#include /// namespace CGAL { @@ -143,6 +143,7 @@ area_2( ForwardIterator first, ForwardIterator last, const PolygonTraits& traits) { typedef typename PolygonTraits::FT FT; + internal::Evaluate evaluate; result = FT(0); // check if the polygon is empty if (first == last) return; @@ -154,7 +155,7 @@ area_2( ForwardIterator first, ForwardIterator last, ForwardIterator third = second; while (++third != last) { result = result + compute_area_2(*first, *second, *third); - exact(result); + evaluate(result); second = third; } } @@ -181,6 +182,7 @@ polygon_area_2( ForwardIterator first, ForwardIterator last, const PolygonTraits& traits) { typedef typename PolygonTraits::FT FT; + internal::Evaluate evaluate; FT result = FT(0); // check if the polygon is empty if (first == last) return result; @@ -192,7 +194,7 @@ polygon_area_2( ForwardIterator first, ForwardIterator last, ForwardIterator third = second; while (++third != last) { result = result + compute_area_2(*first, *second, *third); - exact(result); + evaluate(result); second = third; } return result; diff --git a/Polygon/package_info/Polygon/dependencies b/Polygon/package_info/Polygon/dependencies index 7ed360974c5f..3312eb37e462 100644 --- a/Polygon/package_info/Polygon/dependencies +++ b/Polygon/package_info/Polygon/dependencies @@ -1,11 +1,8 @@ Algebraic_foundations Circulator -Filtered_kernel GraphicsView Installation -Interval_support Kernel_23 -Modular_arithmetic Number_types Polygon Profiling_tools diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h index a0841748aee2..879462312271 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/measure.h @@ -23,9 +23,7 @@ #include #include #include - - -#include // needed for CGAL::exact(FT)/CGAL::exact(Lazy_exact_nt) +#include #include #include @@ -261,12 +259,14 @@ face_border_length(typename boost::graph_traits::halfedge_descripto const PolygonMesh& pmesh, const NamedParameters& np = parameters::default_values()) { - typename GetGeomTraits::type::FT result = 0; + using FT = typename GetGeomTraits::type::FT; + ::CGAL::internal::Evaluate evaluate; + FT result = 0; for(typename boost::graph_traits::halfedge_descriptor haf : halfedges_around_face(h, pmesh)) { result += edge_length(haf, pmesh, np); - exact(result); + evaluate(result); } return result; @@ -559,11 +559,14 @@ area(FaceRange face_range, { typedef typename boost::graph_traits::face_descriptor face_descriptor; - typename GetGeomTraits::type::FT result = 0; + using FT = typename GetGeomTraits::type::FT; + FT result = 0; + ::CGAL::internal::Evaluate evaluate; + for(face_descriptor f : face_range) { result += face_area(f, tmesh, np); - exact(result); + evaluate(result); } return result; @@ -676,7 +679,10 @@ volume(const TriangleMesh& tmesh, typedef typename boost::graph_traits::face_descriptor face_descriptor; - typename GetGeomTraits::type::FT volume = 0; + using FT = typename GetGeomTraits::type::FT; + ::CGAL::internal::Evaluate evaluate; + + FT volume = 0; typename CGAL::Kernel_traits::type>::Kernel::Compute_volume_3 cv3; @@ -686,7 +692,7 @@ volume(const TriangleMesh& tmesh, get(vpm, target(halfedge(f, tmesh), tmesh)), get(vpm, target(next(halfedge(f, tmesh), tmesh), tmesh)), get(vpm, target(prev(halfedge(f, tmesh), tmesh), tmesh))); - exact(volume); + evaluate(volume); } return volume; diff --git a/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies b/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies index c84b76944600..e88f195eeab2 100644 --- a/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies +++ b/Set_movable_separability_2/package_info/Set_movable_separability_2/dependencies @@ -1,10 +1,7 @@ Algebraic_foundations Circulator -Filtered_kernel Installation -Interval_support Kernel_23 -Modular_arithmetic Number_types Polygon Profiling_tools diff --git a/Stream_support/package_info/Stream_support/dependencies b/Stream_support/package_info/Stream_support/dependencies index f45a1a985bf5..86fd53b13ace 100644 --- a/Stream_support/package_info/Stream_support/dependencies +++ b/Stream_support/package_info/Stream_support/dependencies @@ -1,7 +1,6 @@ Algebraic_foundations BGL Circulator -Filtered_kernel Installation Interval_support Kernel_23 From 3471add0627f1135e814a861c781e81a74f49e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 14 Mar 2024 16:09:29 +0100 Subject: [PATCH 515/520] simplify disambiguation --- Orthtree/include/CGAL/Orthtree.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 8521e37cfce0..0cba3e781ba7 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -278,9 +278,9 @@ class Orthtree { /*! constructs an orthtree from a set of arguments provided to the traits constructor */ - template - explicit Orthtree(Arg1&& arg1, Arg2&& arg2, Args&& ... args) - : Orthtree(Traits(std::forward(arg1), std::forward(arg2), std::forward(args)...)) + template = 2>> + explicit Orthtree(Args&& ... args) + : Orthtree(Traits(std::forward(args)...)) {} /// copy constructor From f167a90612a917dbaae672869941cc7a5ccdaaa4 Mon Sep 17 00:00:00 2001 From: albert-github Date: Sat, 16 Mar 2024 12:55:59 +0100 Subject: [PATCH 516/520] issue #8075 Documentation in Triangulation package replace the, for doxygen and thus for the output, strange constructs with better constructs. --- Triangulation/doc/Triangulation/Triangulation.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Triangulation/doc/Triangulation/Triangulation.txt b/Triangulation/doc/Triangulation/Triangulation.txt index b239a2c05b0e..2e7c8e538bd8 100644 --- a/Triangulation/doc/Triangulation/Triangulation.txt +++ b/Triangulation/doc/Triangulation/Triangulation.txt @@ -405,7 +405,7 @@ of an infinite full cell is the empty half-space bounded by the affine hull of the finite facet of this cell. The set of full cells that are in conflict with `p` form the conflict zone. The full cells in the conflict zone are removed, leaving a hole that contains `p`. That -hole is ``star shaped'' around `p` and thus is re-triangulated using +hole is “star shaped” around `p` and thus is re-triangulated using `p` as a center vertex. Delaunay triangulations support insertion of points, removal of vertices, From 151b343f3c80b5c23eebe3f4aa2be135069cb7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 21 Mar 2024 15:29:53 +0100 Subject: [PATCH 517/520] readd figs --- .../doc/Polygon_repair/fig/Corine180927.jpg | Bin 0 -> 49878 bytes .../doc/Polygon_repair/fig/Corine2018418.jpg | Bin 0 -> 76156 bytes .../fig/Polygon_repair-small.png | Bin 0 -> 17908 bytes .../fig/Polygon_repair-small.svg | 90 ++++++++ .../doc/Polygon_repair/fig/inout.svg | 214 ++++++++++++++++++ .../doc/Polygon_repair/fig/invalid.svg | 130 +++++++++++ .../doc/Polygon_repair/fig/valid.svg | 88 +++++++ 7 files changed, 522 insertions(+) create mode 100644 Polygon_repair/doc/Polygon_repair/fig/Corine180927.jpg create mode 100644 Polygon_repair/doc/Polygon_repair/fig/Corine2018418.jpg create mode 100644 Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.png create mode 100644 Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.svg create mode 100644 Polygon_repair/doc/Polygon_repair/fig/inout.svg create mode 100644 Polygon_repair/doc/Polygon_repair/fig/invalid.svg create mode 100644 Polygon_repair/doc/Polygon_repair/fig/valid.svg diff --git a/Polygon_repair/doc/Polygon_repair/fig/Corine180927.jpg b/Polygon_repair/doc/Polygon_repair/fig/Corine180927.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3748a819d2e5ef9e854ef4cff499b2ec338a2309 GIT binary patch literal 49878 zcmeFYbyOVBw=X(afZzo8;O?%$Ed=+#1Pd1222XG&cme?uBsde?A$Wig+y);k$b=aP zFoXf_VSocrFocir>edjGw%XR7Qzn8|e_=xrI_-}&%0A*uUZEf`4*umGy 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 literal 0 HcmV?d00001 diff --git a/Polygon_repair/doc/Polygon_repair/fig/Corine2018418.jpg b/Polygon_repair/doc/Polygon_repair/fig/Corine2018418.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aba0e3879a20c94ac90f32a72a97109871903ebe GIT binary patch literal 76156 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⪋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

  • bM;5!aOg#9}6I+mtQai2|>G?op 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 MWBY&W*xxh%1NEMr9RL6T literal 0 HcmV?d00001 diff --git a/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.png b/Polygon_repair/doc/Polygon_repair/fig/Polygon_repair-small.png new file mode 100644 index 0000000000000000000000000000000000000000..634b116b03e59a8bb7e8e9a405b55bc2185b6501 GIT binary patch literal 17908 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 dmiK=t{vQH~upj`S%X + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Polygon_repair/doc/Polygon_repair/fig/inout.svg b/Polygon_repair/doc/Polygon_repair/fig/inout.svg new file mode 100644 index 000000000000..d04cd3d601c6 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/inout.svg @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (a) + + + + + + + (b) + + + + + + + (c) + + + + + + + (d) + + + + + + + (e) + + + + + + + (f) + + + + + + + (g) + + + + + + + (h) + + + + + + diff --git a/Polygon_repair/doc/Polygon_repair/fig/invalid.svg b/Polygon_repair/doc/Polygon_repair/fig/invalid.svg new file mode 100644 index 000000000000..28a48816f750 --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/invalid.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + (b) + + + + + + + (a) + + + + + + + (c) + + + + + + + (f) + + + + + + + (g) + + + + + + + + + + + + + + + + + + (d) + + + + + + + + + + + + + + + (e) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (h) + + + + + + diff --git a/Polygon_repair/doc/Polygon_repair/fig/valid.svg b/Polygon_repair/doc/Polygon_repair/fig/valid.svg new file mode 100644 index 000000000000..524d308b323c --- /dev/null +++ b/Polygon_repair/doc/Polygon_repair/fig/valid.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (b) + + + + + + + (a) + + + + + + + (c) + + + + + + + (d) + + + + + + + (e) + + + + + + From 30c303ff7d2ccd5a51e6e84457149e0938c54c4a Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 21 Mar 2024 16:28:00 +0100 Subject: [PATCH 518/520] Update CHANGES.md --- Installation/CHANGES.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 7932846acc42..d84f8bad7e55 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -35,7 +35,6 @@ Release date: October 2023 - **Breaking change**: Construct_projected_boundary_2 in `EnvelopeTraits_3` is now using `std::variant` instead of `Object` - Passed the base class of `Env_plane_traits_3` as a template parameter with a default value (being the 2D arrangement linear traits). Similarly, passed the base class of `Env_triangle_traits_3` as a template parameter with a default value (being the 2D arrangement segment traits). ->>>>>>> cgal/master ### [Combinatorial Maps](https://doc.cgal.org/6.0/Manual/packages.html#PkgCombinatorialMaps) and [Generalized Maps](https://doc.cgal.org/6.0/Manual/packages.html#PkgGeneralizedMaps) From 88f44a53f981d134a6a4dde3f59206100de000b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sat, 23 Mar 2024 15:41:25 +0100 Subject: [PATCH 519/520] restore changes lost while rebasing... --- .../Polygon_repair/draw_test_polygons.cpp | 23 +++++++++++++++++++ .../test/Polygon_repair/exact_test.cpp | 23 +++++++++++++++++++ .../Polygon_repair/repair_polygon_2_test.cpp | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp index a61e8f80531b..ead5bda9e758 100644 --- a/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp +++ b/Polygon_repair/test/Polygon_repair/draw_test_polygons.cpp @@ -1,6 +1,19 @@ #include #include #include + +// work around for old compilers (Apple clang < 11 for example) +#define HAS_FILESYSTEM 1 +#if defined(__has_include) +#if !__has_include() +#undef HAS_FILESYSTEM +#define HAS_FILESYSTEM 0 +#endif +#endif + + +#if HAS_FILESYSTEM + #include #include @@ -52,3 +65,13 @@ int main() { return 0; } + +#else + +int main() +{ + std::cout << "Warning: filesystem feature is not present on the system, nothing will be tested\n"; + return 0; +} + +#endif diff --git a/Polygon_repair/test/Polygon_repair/exact_test.cpp b/Polygon_repair/test/Polygon_repair/exact_test.cpp index 4182f8faa061..d3d7fceb6733 100644 --- a/Polygon_repair/test/Polygon_repair/exact_test.cpp +++ b/Polygon_repair/test/Polygon_repair/exact_test.cpp @@ -3,6 +3,19 @@ #include #include #include + +// work around for old compilers (Apple clang < 11 for example) +#define HAS_FILESYSTEM 1 +#if defined(__has_include) +#if !__has_include() +#undef HAS_FILESYSTEM +#define HAS_FILESYSTEM 0 +#endif +#endif + + +#if HAS_FILESYSTEM + #include #include @@ -50,3 +63,13 @@ int main() { return 0; } + +#else + +int main() +{ + std::cout << "Warning: filesystem feature is not present on the system, nothing will be tested\n"; + return 0; +} + +#endif diff --git a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp index 3ca4f4225df7..1f7e022becf0 100644 --- a/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp +++ b/Polygon_repair/test/Polygon_repair/repair_polygon_2_test.cpp @@ -6,7 +6,7 @@ // work around for old compilers (Apple clang < 11 for example) #define HAS_FILESYSTEM 1 -#if (__has_include) +#if defined(__has_include) #if !__has_include() #undef HAS_FILESYSTEM #define HAS_FILESYSTEM 0 From a5f7ffb900db763e14a8bd9d53bf96d619a34f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 25 Mar 2024 15:52:57 +0100 Subject: [PATCH 520/520] add missing suffix --- .../developer_scripts/cgal_create_package_dir.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Scripts/developer_scripts/cgal_create_package_dir.py b/Scripts/developer_scripts/cgal_create_package_dir.py index 1847fada3bc7..90900b1304d3 100755 --- a/Scripts/developer_scripts/cgal_create_package_dir.py +++ b/Scripts/developer_scripts/cgal_create_package_dir.py @@ -34,23 +34,20 @@ descrstring = \ r"""// PRETTY PACKAGE NAME should equal the project title in Doxyfile.in -/// \defgroup PkgPACKAGE PRETTY PACKAGE NAME Reference +/// \defgroup PkgPACKAGERef PRETTY PACKAGE NAME Reference /// \defgroup PkgPACKAGEConcepts Concepts -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /// \defgroup PkgPACKAGEAlgorithmClasses Algorithm Classes -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /// \defgroup PkgPACKAGETraitsClasses Traits Classes -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /// \defgroup PkgPACKAGEMiscellaneous Miscellaneous -/// \ingroup PkgPACKAGE +/// \ingroup PkgPACKAGERef /*! -\addtogroup PkgPACKAGE -\todo check generated documentation - \cgalPkgDescriptionBegin{PACKAGE NAME,PkgPACKAGE} \cgalPkgPicture{pkg-small.png} @@ -58,7 +55,7 @@ \cgalPkgAuthors{PACKAGE AUTHOR} \cgalPkgDesc{PACKAGE DESCRIPTION. The package provides ... } -\cgalPkgManuals{Chapter_PACKAGE_NAME,PkgPACKAGE} +\cgalPkgManuals{Chapter_PACKAGE_NAME,PkgPACKAGERef} \cgalPkgSummaryEnd \cgalPkgShortInfoBegin