Delete independent `Node` type, move relevant typedefs to Orthtree.h

This commit is contained in:
JacksonCampolattaro 2023-06-25 13:54:19 +02:00
parent 01aad4af7a
commit c41faf1274
5 changed files with 55 additions and 324 deletions

View File

@ -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

View File

@ -48,7 +48,7 @@
\cgalCRPSection{Traits}
- `CGAL::Orthtree_traits_2<GeomTraits>`
- `CGAL::Orthtree_traits_3<GeomTraits>`
- `CGAL::Orthtree_traits_point_set_3<GeomTraits>`
- `CGAL::Orthtree_traits_d<GeomTraits, Dimension>`
\cgalCRPSection{Split Predicates}

View File

@ -85,6 +85,7 @@ public:
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 @@ public:
/// \endcond
/// @}
/// \name Types specific to Point_set_3
/// todo: should be moved to Traits
/// @{
typedef boost::iterator_range<typename PointRange::iterator> Node_point_range;
/// @}
/// \name Public Types
/// @{
@ -111,15 +118,36 @@ public:
*/
typedef std::size_t Node_index;
typedef Properties::Property_container<Node_index> Node_property_container;
/*!
* \brief Optional index of a node in the tree.
*/
typedef boost::optional<Node_index> Maybe_node_index;
// todo: maybe this could be private?
typedef Properties::Property_container<Node_index> 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<Dimension::value> 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<std::uint32_t, Dimension::value> Global_coordinates;
/*!
* \brief The Sub-tree / Orthant type.
* todo: this should be removed
*/
class Node;
@ -163,9 +191,9 @@ private: // data members :
PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */
Node_property_container m_node_properties;
Node_property_container::Array <boost::iterator_range<typename PointRange::iterator>>& m_node_points;
Node_property_container::Array <Node_point_range>& m_node_points;
Node_property_container::Array <std::uint8_t>& m_node_depths;
Node_property_container::Array <std::array<std::uint32_t, Dimension::value>>& m_node_coordinates;
Node_property_container::Array <Global_coordinates>& m_node_coordinates;
Node_property_container::Array <Maybe_node_index>& m_node_parents;
Node_property_container::Array <Maybe_node_index>& m_node_children;
@ -212,9 +240,9 @@ public:
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<boost::iterator_range<typename PointRange::iterator>>("points")),
m_node_points(m_node_properties.add_property<Node_point_range>("points")),
m_node_depths(m_node_properties.add_property<std::uint8_t>("depths", 0)),
m_node_coordinates(m_node_properties.add_property<std::array<std::uint32_t, Dimension::value>>("coordinates")),
m_node_coordinates(m_node_properties.add_property<Global_coordinates>("coordinates")),
m_node_parents(m_node_properties.add_property<Maybe_node_index>("parents")),
m_node_children(m_node_properties.add_property<Maybe_node_index>("children")) {
@ -275,9 +303,9 @@ public:
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<boost::iterator_range<typename PointRange::iterator>>("points")),
m_node_points(m_node_properties.get_property<Node_point_range>("points")),
m_node_depths(m_node_properties.get_property<std::uint8_t>("depths")),
m_node_coordinates(m_node_properties.get_property<std::array<std::uint32_t, Dimension::value>>("coordinates")),
m_node_coordinates(m_node_properties.get_property<Global_coordinates>("coordinates")),
m_node_parents(m_node_properties.get_property<Maybe_node_index>("parents")),
m_node_children(m_node_properties.get_property<Maybe_node_index>("children")) {}
@ -286,9 +314,9 @@ public:
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<boost::iterator_range<typename PointRange::iterator>>("points")),
m_node_points(m_node_properties.get_property<Node_point_range>("points")),
m_node_depths(m_node_properties.get_property<std::uint8_t>("depths")),
m_node_coordinates(m_node_properties.get_property<std::array<std::uint32_t, Dimension::value>>("coordinates")),
m_node_coordinates(m_node_properties.get_property<Global_coordinates>("coordinates")),
m_node_parents(m_node_properties.get_property<Maybe_node_index>("parents")),
m_node_children(m_node_properties.get_property<Maybe_node_index>("children")) {
@ -312,10 +340,9 @@ public:
/*!
\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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
\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 @@ public:
/*!
\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<Dimension::value>(static_cast<int>(adjacency)));
}
@ -1286,6 +1312,4 @@ public:
} // namespace CGAL
#include <CGAL/Orthtree/Node.h>
#endif // CGAL_ORTHTREE_H

View File

@ -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 <CGAL/license/Orthtree.h>
#include <CGAL/Orthtree.h>
#include <boost/range/iterator_range.hpp>
#include <boost/core/span.hpp>
#include <array>
#include <memory>
#include <bitset>
#include <cassert>
#include <iostream>
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 <typename Traits, typename PointRange, typename PointMap>
class Orthtree<Traits, PointRange, PointMap>::Node {
public:
/// \name Types
/// @{
typedef Orthtree<Traits, PointRange, PointMap> 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<Traits, PointRange, PointMap>::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<Dimension::value> 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<std::uint32_t, Dimension::value> 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<iterator> 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

View File

@ -1,55 +0,0 @@
#include <CGAL/Orthtree/Node.h>
#include <CGAL/Octree/IO.h>
#include <iostream>
#include <cassert>
typedef CGAL::Orthtree::Node<std::vector<int>::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;
}