From 4f6d249c1f86f50c1369787c5f6665a80a0211e2 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 23 Mar 2023 09:58:20 +0100 Subject: [PATCH 001/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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/297] 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 c066a18fe27b3fc59b574254341c191c7732d97a Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 18 May 2023 11:36:06 +0200 Subject: [PATCH 044/297] 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 6dc9415e9fd3b4ddd8604f39a994954346fec57c Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 25 May 2023 11:08:07 +0200 Subject: [PATCH 045/297] 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 046/297] 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 047/297] 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 048/297] 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 049/297] 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 050/297] 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 497014022e18e6e057d0ef46545c530989b5d4d3 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 7 Jun 2023 11:26:09 +0200 Subject: [PATCH 051/297] 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 052/297] 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 8b72b0a6b0736091c28731b53234db51841f84dd Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 16 Jun 2023 10:45:55 +0200 Subject: [PATCH 053/297] 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 054/297] 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 055/297] 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 056/297] 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 057/297] 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 058/297] 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 059/297] 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 060/297] 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 061/297] 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 062/297] 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 01aad4af7ac624658808b928c7af8cf2f3ffb731 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 25 Jun 2023 12:42:24 +0200 Subject: [PATCH 063/297] 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 064/297] 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 065/297] 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 066/297] 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 067/297] 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 068/297] 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 069/297] 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 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 070/297] 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 03cda70191de38d7d12fd3cba930280cff51c656 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 9 Jul 2023 17:49:12 +0200 Subject: [PATCH 071/297] 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 072/297] 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 073/297] 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 cfdb167e3528c98ab812285177fdfbd782faf825 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 18 Jul 2023 10:35:48 +0200 Subject: [PATCH 074/297] 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 61a29f0fdb560baff2d01805540a5dd84a9580c9 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 26 Jul 2023 09:57:44 +0200 Subject: [PATCH 075/297] 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 076/297] 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 077/297] 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 078/297] 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 079/297] 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 8fe57f5adf29cbda5f7a7953df6a05efc9379c85 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 27 Jul 2023 09:49:20 +0200 Subject: [PATCH 080/297] 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 081/297] 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 082/297] 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 083/297] 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 07c671e6e951e507397531374a7a78cffa018709 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Tue, 29 Aug 2023 19:13:11 +0200 Subject: [PATCH 084/297] 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 2d8f7d483db00dce36c44c1b543120b375e5024d Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Wed, 30 Aug 2023 15:32:38 +0200 Subject: [PATCH 085/297] 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 086/297] 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 77f240b1b638f68bb1c830b6801d54d57598c951 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 31 Aug 2023 12:48:52 +0200 Subject: [PATCH 087/297] 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 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 088/297] 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 089/297] 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 090/297] 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 091/297] 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 092/297] 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 093/297] 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 094/297] 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 095/297] 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 156675076651b77c57d99b525771f7f1ad498524 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Fri, 1 Sep 2023 17:45:09 +0200 Subject: [PATCH 096/297] 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 99a2d5612f5a595cf1ea788cb8582ba74e804220 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 2 Sep 2023 15:09:16 +0200 Subject: [PATCH 097/297] 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 098/297] 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 fd34fb53029f6f4c8dfd47019dd6a7ae6ff28256 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sat, 2 Sep 2023 16:49:32 +0200 Subject: [PATCH 099/297] `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 100/297] 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: Tue, 5 Sep 2023 15:46:54 +0200 Subject: [PATCH 101/297] 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 102/297] 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 32c6d61f2763ef2ef74a45f29f2380eaf11c91f8 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 7 Sep 2023 11:44:14 +0200 Subject: [PATCH 103/297] 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 104/297] 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 ea59877c195469877892f6d7e3b824f5217125f5 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Thu, 7 Sep 2023 17:19:04 +0200 Subject: [PATCH 105/297] 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 106/297] 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 107/297] 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 108/297] 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 109/297] 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 110/297] 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 111/297] 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 112/297] 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 113/297] 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 114/297] 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 115/297] 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 116/297] 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 117/297] 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 118/297] 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 119/297] 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 120/297] 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 121/297] 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 122/297] 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 123/297] 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 124/297] 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 125/297] 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 126/297] 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 127/297] 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 128/297] 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 129/297] 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 130/297] 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 131/297] 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 132/297] 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 133/297] 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 134/297] 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 135/297] 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 136/297] 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 137/297] 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 138/297] 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 139/297] 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 047a223db0bf6d4ca8e23392d70cd7e041de9682 Mon Sep 17 00:00:00 2001 From: JacksonCampolattaro Date: Sun, 8 Oct 2023 15:05:24 +0200 Subject: [PATCH 140/297] 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 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 141/297] 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 142/297] 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 143/297] 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 144/297] =?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 145/297] =?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 146/297] 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 147/297] 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 de06eb02ffeac158182c551d8db4a1ffdc3f446c Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 2 Jan 2024 20:57:51 +0100 Subject: [PATCH 148/297] 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 d5a764c41c169569a63e1d5524b610dff3e69d41 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 8 Jan 2024 13:13:21 +0100 Subject: [PATCH 149/297] 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 150/297] 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 151/297] 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 152/297] 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 153/297] 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 154/297] 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 155/297] 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 156/297] 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 157/297] 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 158/297] 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 159/297] 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 160/297] 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 161/297] 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 162/297] 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 163/297] 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 164/297] 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 165/297] 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 166/297] 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 167/297] 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 168/297] 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 cdea8cf8bb7f9b63b6377a7d0a7085777aa74bca Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 18 Jan 2024 12:39:46 +0100 Subject: [PATCH 169/297] 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 620a78c7f0b18427654378b8f468792051111b88 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 24 Jan 2024 07:25:58 +0000 Subject: [PATCH 170/297] 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 171/297] 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 172/297] 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 173/297] 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 174/297] 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 175/297] 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 176/297] 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 177/297] 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 178/297] 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 179/297] 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 180/297] 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 181/297] 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 182/297] 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 183/297] 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 184/297] 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 185/297] 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 186/297] 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 187/297] 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 188/297] 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 189/297] 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 190/297] 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 191/297] 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 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 192/297] 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 193/297] 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 194/297] 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 195/297] 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 196/297] 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 197/297] 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 198/297] 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 199/297] 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 200/297] 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 201/297] 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 202/297] 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 203/297] 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 204/297] 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 205/297] 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 206/297] 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 207/297] 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 208/297] 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 209/297] 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 210/297] 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 211/297] 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 212/297] 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 213/297] 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 214/297] 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 215/297] 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 216/297] 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 217/297] 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 218/297] 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 219/297] 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 220/297] 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 221/297] 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 222/297] 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 223/297] 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 224/297] 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 225/297] 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 226/297] 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 75d1519e26ae1614f3649ea6b08da8e12975be0f Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 7 Feb 2024 15:18:36 +0100 Subject: [PATCH 227/297] 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 228/297] 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 229/297] 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 230/297] 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 231/297] 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 232/297] 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 233/297] 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 234/297] 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 235/297] 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 236/297] 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 237/297] 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 238/297] 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 239/297] 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 240/297] 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 241/297] 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 c580e30407d49e330092e6e52fb8f88cabc3f85c Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 19 Feb 2024 12:10:59 +0100 Subject: [PATCH 242/297] 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 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 243/297] 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 244/297] 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 245/297] 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 246/297] 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 247/297] 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 248/297] 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 249/297] 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 250/297] 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 251/297] 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 252/297] 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 253/297] 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 254/297] 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 255/297] 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 9ee06f2dd0245ff0dd750468f7b78dcab686ec38 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 22 Feb 2024 15:51:46 +0100 Subject: [PATCH 256/297] 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 aec58185bd92e5d087a036b1c6e5dcb47148e7ff Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Mon, 26 Feb 2024 14:20:06 +0100 Subject: [PATCH 257/297] 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 15:08:58 +0100 Subject: [PATCH 258/297] 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 259/297] 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 260/297] 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 261/297] 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 262/297] 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 3251248b68c8b813c8d2a50d88f0b9ba086f4f47 Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 27 Feb 2024 18:01:53 +0100 Subject: [PATCH 263/297] 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 264/297] 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 b3b9bf0d98c06f37fc781c07c10879d97499794b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Wed, 28 Feb 2024 15:49:30 +0100 Subject: [PATCH 265/297] 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 266/297] 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 267/297] 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 268/297] 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 269/297] 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 270/297] 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 271/297] 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 272/297] 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 273/297] 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 274/297] 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 275/297] 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 276/297] 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 277/297] 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 278/297] 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 279/297] 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 280/297] 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 281/297] 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 282/297] 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 283/297] 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 284/297] 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 285/297] 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 286/297] 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 287/297] 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 288/297] 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 289/297] 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 7bf227d5e69e16fd487296eee1bda88fd937b37b Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 5 Mar 2024 10:51:48 +0100 Subject: [PATCH 290/297] 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 291/297] 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 292/297] 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 293/297] 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 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 294/297] 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 295/297] 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 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 296/297] 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 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 297/297] 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