Make node trivially copiable and simplify APIs

This commit is contained in:
Simon Giraudot 2020-10-26 15:56:42 +01:00
parent 398fbb989c
commit 0747b09e23
6 changed files with 252 additions and 290 deletions

View File

@ -53,15 +53,15 @@ int main(int argc, char **argv) {
std::cout << std::endl; std::cout << std::endl;
// Retrieve one of the deeper children // Retrieve one of the deeper children
const Octree::Node &cur = octree[3][2]; Octree::Node cur = octree[3][2];
std::cout << "Navigation relative to a child node" << std::endl; std::cout << "Navigation relative to a child node" << std::endl;
std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
std::cout << "the third child of the fourth child: " << std::endl; std::cout << "the third child of the fourth child: " << std::endl;
std::cout << cur << std::endl; std::cout << cur << std::endl;
std::cout << "the third child: " << std::endl; std::cout << "the third child: " << std::endl;
std::cout << *cur.parent() << std::endl; std::cout << cur.parent() << std::endl;
std::cout << "the next sibling of the third child of the fourth child: " << std::endl; std::cout << "the next sibling of the third child of the fourth child: " << std::endl;
std::cout << (*cur.parent())[cur.index().to_ulong() + 1] << std::endl; std::cout << cur.parent()[cur.index().to_ulong() + 1] << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -18,6 +18,7 @@
#include <CGAL/Orthtree/Split_predicate.h> #include <CGAL/Orthtree/Split_predicate.h>
#include <CGAL/Orthtree/Traversal.h> #include <CGAL/Orthtree/Traversal.h>
#include <CGAL/Orthtree/Traversal_iterator.h> #include <CGAL/Orthtree/Traversal_iterator.h>
#include <CGAL/Orthtree/IO.h>
#include <CGAL/bounding_box.h> #include <CGAL/bounding_box.h>
@ -107,7 +108,7 @@ public:
/*! /*!
* \brief A predicate that determines whether a node needs to be split when refining a tree * \brief A predicate that determines whether a node needs to be split when refining a tree
*/ */
typedef std::function<bool(const Node &)> Split_predicate; typedef std::function<bool(Node)> Split_predicate;
/*! /*!
* \brief A model of `ConstRange` whose value type is `Node`. * \brief A model of `ConstRange` whose value type is `Node`.
@ -115,7 +116,7 @@ public:
#ifdef DOXYGEN_RUNNING #ifdef DOXYGEN_RUNNING
typedef unspecified_type Node_range; typedef unspecified_type Node_range;
#else #else
typedef boost::iterator_range<Traversal_iterator<const Node>> Node_range; typedef boost::iterator_range<Traversal_iterator<Node> > Node_range;
#endif #endif
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
@ -123,7 +124,7 @@ public:
/*! /*!
* \brief A function that determines the next node in a traversal given the current one * \brief A function that determines the next node in a traversal given the current one
*/ */
typedef std::function<const Node *(const Node *)> Node_traversal_method_const; typedef std::function<Node(Node)> Node_traversal_method_const;
/// \endcond /// \endcond
@ -176,6 +177,7 @@ public:
: m_traits (traits) : m_traits (traits)
, m_range (point_range) , m_range (point_range)
, m_point_map (point_map) , m_point_map (point_map)
, m_root(Node(), 0)
{ {
Array bbox_min; Array bbox_min;
for (FT& f : bbox_min) for (FT& f : bbox_min)
@ -207,7 +209,7 @@ public:
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_min[i] = bbox_centroid[i] - max_length;
bbox_max[i] = bbox_centroid[i] - max_length; bbox_max[i] = bbox_centroid[i] + max_length;
} }
Construct_point_d_from_array construct_point_d_from_array Construct_point_d_from_array construct_point_d_from_array
@ -245,32 +247,32 @@ public:
m_side_per_depth.resize(1); m_side_per_depth.resize(1);
// Initialize a queue of nodes that need to be refined // Initialize a queue of nodes that need to be refined
std::queue<Node *> todo; std::queue<Node> todo;
todo.push(&m_root); todo.push(m_root);
// Process items in the queue until it's consumed fully // Process items in the queue until it's consumed fully
while (!todo.empty()) { while (!todo.empty()) {
// Get the next element // Get the next element
auto current = todo.front(); Node current = todo.front();
todo.pop(); todo.pop();
// Check if this node needs to be processed // 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 // Check if we've reached a new max depth
if (current->depth() == max_depth_reached()) { if (current.depth() == max_depth_reached()) {
// Update the side length map // Update the side length map
m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2); m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2);
} }
// Split the node, redistributing its points to its children // Split the node, redistributing its points to its children
split((*current)); split(current);
// Process each of its children // Process each of its children
for (int i = 0; i < Degree::value; ++i) for (int i = 0; i < Degree::value; ++i)
todo.push(&(*current)[i]); todo.push(current[i]);
} }
} }
@ -299,52 +301,52 @@ public:
void grade() { void grade() {
// Collect all the leaf nodes // Collect all the leaf nodes
std::queue<Node *> leaf_nodes; std::queue<Node> leaf_nodes;
for (auto &leaf : traverse(Orthtrees::Traversal::Leaves())) { for (Node leaf : traverse(Orthtrees::Traversal::Leaves())) {
// TODO: I'd like to find a better (safer) way of doing this // TODO: I'd like to find a better (safer) way of doing this
leaf_nodes.push(const_cast<Node *>(&leaf)); leaf_nodes.push(leaf);
} }
// Iterate over the nodes // Iterate over the nodes
while (!leaf_nodes.empty()) { while (!leaf_nodes.empty()) {
// Get the next node // Get the next node
Node *node = leaf_nodes.front(); Node node = leaf_nodes.front();
leaf_nodes.pop(); leaf_nodes.pop();
// Skip this node if it isn't a leaf anymore // Skip this node if it isn't a leaf anymore
if (!node->is_leaf()) if (!node.is_leaf())
continue; continue;
// Iterate over each of the neighbors // Iterate over each of the neighbors
for (int direction = 0; direction < 6; ++direction) { for (int direction = 0; direction < 6; ++direction) {
// Get the neighbor // Get the neighbor
auto *neighbor = node->adjacent_node(direction); Node neighbor = node.adjacent_node(direction);
// If it doesn't exist, skip it // If it doesn't exist, skip it
if (!neighbor) if (neighbor.is_null())
continue; continue;
// Skip if this neighbor is a direct sibling (it's guaranteed to be the same depth) // 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 // 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; continue;
// If it's already been split, skip it // If it's already been split, skip it
if (!neighbor->is_leaf()) if (!neighbor.is_leaf())
continue; continue;
// Check if the neighbor breaks our grading rule // Check if the neighbor breaks our grading rule
// TODO: could the rule be parametrized? // TODO: could the rule be parametrized?
if ((node->depth() - neighbor->depth()) > 1) { if ((node.depth() - neighbor.depth()) > 1) {
// Split the neighbor // Split the neighbor
split(*neighbor); split(neighbor);
// Add newly created children to the queue // Add newly created children to the queue
for (int i = 0; i < Degree::value; ++i) { for (int i = 0; i < Degree::value; ++i) {
leaf_nodes.push(&(*neighbor)[i]); leaf_nodes.push(neighbor[i]);
} }
} }
} }
@ -357,12 +359,9 @@ public:
/// @{ /// @{
/*! /*!
\brief provides read-only access to the root node, and by \brief returns the root node.
extension the rest of the tree.
\return a const reference to the root node of the tree.
*/ */
const Node &root() const { return m_root; } Node root() const { return m_root; }
/*! /*!
\brief convenience function to access the child nodes of the root \brief convenience function to access the child nodes of the root
@ -373,7 +372,7 @@ public:
\param index the index of the child node. \param index the index of the child node.
\return a reference to the node. \return a reference to the node.
*/ */
const Node &operator[](std::size_t index) const { return m_root[index]; } Node operator[](std::size_t index) const { return m_root[index]; }
/*! /*!
\brief Finds the deepest level reached by a leaf node in this tree. \brief Finds the deepest level reached by a leaf node in this tree.
@ -394,13 +393,13 @@ public:
template<typename Traversal> template<typename Traversal>
Node_range traverse(const Traversal &traversal = Traversal()) const { Node_range traverse(const Traversal &traversal = Traversal()) const {
const Node *first = traversal.first(&m_root); Node first = traversal.first(m_root);
Node_traversal_method_const next = std::bind(&Traversal::template next<Node>, Node_traversal_method_const next = std::bind(&Traversal::template next<Node>,
traversal, _1); traversal, _1);
return boost::make_iterator_range(Traversal_iterator<const Node>(first, next), return boost::make_iterator_range(Traversal_iterator<Node>(first, next),
Traversal_iterator<const Node>()); Traversal_iterator<Node>());
} }
/*! /*!
@ -444,19 +443,19 @@ public:
\param point query point. \param point query point.
\return the node which contains the point. \return the node which contains the point.
*/ */
const Node& locate(const Point &point) const { Node locate(const Point &point) const {
// Make sure the point is enclosed by the orthtree // 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(m_root)));
// Start at the root node // 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 // 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 // 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 // Find the index of the correct sub-node
typename Node::Index index; typename Node::Index index;
@ -465,11 +464,11 @@ public:
index[dimension ++] = (get<0>(r) < get<1>(r)); index[dimension ++] = (get<0>(r) < get<1>(r));
// Find the correct sub-node of the current node // 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 the result
return *node_for_point; return node_for_point;
} }
/*! /*!
@ -560,7 +559,7 @@ public:
// TODO: Document this // TODO: Document this
// TODO: Could this method name be reduced to just "center" ? // 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 // Determine the side length of this node
FT size = m_side_per_depth[node.depth()]; FT size = m_side_per_depth[node.depth()];
@ -570,9 +569,11 @@ public:
std::size_t i = 0; std::size_t i = 0;
for (const FT& f : cartesian_range(m_bbox_min)) for (const FT& f : cartesian_range(m_bbox_min))
{ {
std::cerr << node.location()[i] << " ";
bary[i] = node.location()[i] * size + (size / 2.0) + f; bary[i] = node.location()[i] * size + (size / 2.0) + f;
++ i; ++ i;
} }
std::cerr << std::endl;
// Convert that location into a point // Convert that location into a point
Construct_point_d_from_array construct_point_d_from_array Construct_point_d_from_array construct_point_d_from_array
@ -615,7 +616,7 @@ private: // functions :
} }
void split(Node &node) { void split(Node& node) {
// Make sure the node hasn't already been split // Make sure the node hasn't already been split
assert(node.is_leaf()); assert(node.is_leaf());
@ -625,6 +626,7 @@ private: // functions :
// Find the point to around which the node is split // Find the point to around which the node is split
Point center = barycenter(node); Point center = barycenter(node);
std::cerr << center << std::endl;
// Add the node's points to its children // Add the node's points to its children
reassign_points(node, node.points().begin(), node.points().end(), center); reassign_points(node, node.points().begin(), node.points().end(), center);
@ -709,7 +711,7 @@ private: // functions :
// Fill the list with child nodes // Fill the list with child nodes
for (int index = 0; index < Degree::value; ++index) { for (int index = 0; index < Degree::value; ++index) {
auto &child_node = node[index]; Node child_node = node[index];
// Add a child to the list, with its distance // Add a child to the list, with its distance
children_with_distances.push_back( children_with_distances.push_back(
@ -725,7 +727,7 @@ private: // functions :
// Loop over the children // Loop over the children
for (auto child_with_distance : children_with_distances) { for (auto child_with_distance : children_with_distances) {
auto &child_node = node[child_with_distance.index.to_ulong()]; Node child_node = node[child_with_distance.index.to_ulong()];
// Check whether the bounding box of the child intersects with the search bounds // Check whether the bounding box of the child intersects with the search bounds
if (do_intersect(child_node, search_bounds)) { if (do_intersect(child_node, search_bounds)) {
@ -746,7 +748,7 @@ private: // functions :
// if this node is a leaf, than it's considered an intersecting node // if this node is a leaf, than it's considered an intersecting node
if (node.is_leaf()) { if (node.is_leaf()) {
*output++ = &node; *output++ = node;
return output; return output;
} }

View File

@ -14,7 +14,6 @@
#include <CGAL/license/Orthtree.h> #include <CGAL/license/Orthtree.h>
#include <CGAL/Orthtree/IO.h>
#include <boost/range/iterator_range.hpp> #include <boost/range/iterator_range.hpp>
@ -27,14 +26,15 @@
namespace CGAL { namespace CGAL {
/*! /*!
* \brief represents a single node of the tree. Alternatively referred to as a cell, orthant, or subtree \brief represents a single node of the tree. Alternatively referred to as a cell, orthant, or subtree
*
* \details The role of the node isn't fully stable yet \details The role of the node isn't fully stable yet
*
* \tparam Point_index is the datatype the node will contain \tparam Point_index is the datatype the node will contain
*/ */
template<class Traits, class PointRange, class PointMap> template<typename Traits, typename PointRange, typename PointMap>
class Orthtree<Traits, PointRange, PointMap>::Node { class Orthtree<Traits, PointRange, PointMap>::Node
{
public: public:
@ -79,69 +79,26 @@ public:
*/ */
typedef boost::iterator_range<typename PointRange::iterator> Point_range; typedef boost::iterator_range<typename PointRange::iterator> Point_range;
// TODO: Should I use enum classes?
/*!
* \brief the index of a node relative to its parent (a position defined by the corners of a cube)
*
* Corners are mapped to numbers as 3-bit integers, in "zyx" order.
*
* For example:
* > right-top-back --> x=1, y=1, z=0 --> zyx = 011 --> 3
*
* The following diagram may be a useful reference:
*
* 6 7
* +--------+
* /| /| y+
* / | / | * z+
* 2 +--------+ 3| | *
* | | | | |/
* | +-----|--+ +-----* x+
* | / 4 | / 5
* |/ |/
* +--------+
* 0 1
*
* This lookup table may also be helpful:
*
* | Child | bitset | number | Enum |
* | --------------------- | ------ | ------ | --------------------- |
* | left, bottom, back | 000 | 0 | LEFT_BOTTOM_BACK |
* | right, bottom, back | 001 | 1 | RIGHT_BOTTOM_BACK |
* | left, top, back | 010 | 2 | LEFT_TOP_BACK |
* | right, top, back | 011 | 3 | RIGHT_TOP_BACK |
* | left, bottom, front | 100 | 4 | LEFT_BOTTOM_FRONT |
* | right, bottom, front | 101 | 5 | RIGHT_BOTTOM_FRONT |
* | left, top, front | 110 | 6 | LEFT_TOP_FRONT |
* | right, top, front | 111 | 7 | RIGHT_TOP_FRONT |
*/
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
};
typedef typename Traits::Adjacency Adjacency; typedef typename Traits::Adjacency Adjacency;
/// @} /// @}
private: private:
Point_range m_points; // make Node trivially copiabled
struct Data
{
Point_range points;
Self parent;
std::uint8_t depth;
Int_location location;
std::unique_ptr<Children> children;
const Self *m_parent; Data (Self parent)
: parent (parent), depth (0) { }
};
std::uint8_t m_depth; std::shared_ptr<Data> m_data;
Int_location m_location;
std::unique_ptr<Children> m_children;
public: public:
@ -150,6 +107,9 @@ public:
/// \name Construction /// \name Construction
/// @{ /// @{
// Default creates null node
Node() { }
/*! /*!
\brief creates a new node, optionally as the child of a parent \brief creates a new node, optionally as the child of a parent
@ -163,19 +123,20 @@ public:
\param parent A reference to the node containing this one \param parent A reference to the node containing this one
\param index This node's relationship to its parent \param index This node's relationship to its parent
*/ */
explicit Node(Self *parent = nullptr, Index index = 0) : m_parent(parent), m_depth(0) { explicit Node(Self parent, Index index)
: m_data (new Data(parent)) {
if (parent) { if (!parent.is_null()) {
m_depth = parent->m_depth + 1; m_data->depth = parent.m_data->depth + 1;
for (int i = 0; i < Dimension::value; i++) for (int i = 0; i < Dimension::value; i++)
m_location[i] = (2 * parent->m_location[i]) + index[i]; m_data->location[i] = (2 * parent.m_data->location[i]) + index[i];
} }
else else
for (int i = 0; i < Dimension::value; i++) for (int i = 0; i < Dimension::value; i++)
m_location[i] = 0; m_data->location[i] = 0;
} }
/// @} /// @}
@ -196,10 +157,11 @@ public:
assert(is_leaf()); assert(is_leaf());
m_children = std::make_unique<Children>(); m_data->children = std::make_unique<Children>();
for (int index = 0; index < Degree::value; index++) { for (int index = 0; index < Degree::value; index++) {
(*m_children)[index] = std::move(Self(this, {Index(index)})); (*m_data->children)[index] = std::move(Self(*this, {Index(index)}));
} }
} }
@ -211,7 +173,7 @@ public:
*/ */
void unsplit() { void unsplit() {
m_children.reset(); m_data->children.reset();
} }
/// @} /// @}
@ -223,46 +185,31 @@ public:
/*! /*!
\brief returns this node's parent. \brief returns this node's parent.
\pre `!is_root()` \pre `!is_null()`
*/ */
const Self* parent() const Self parent() const
{ {
CGAL_precondition (!is_root()); CGAL_precondition (!is_null());
return m_parent; return m_data->parent;
} }
/*! /*!
\brief returns the nth child fo this node. \brief returns the nth child fo this node.
\pre `!is_null()`
\pre `!is_leaf()` \pre `!is_leaf()`
\pre `0 <= index && index < Degree::value` \pre `0 <= index && index < Degree::value`
The operator can be chained. For example, `n[5][2][3]` returns the 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`. third child of the second child of the fifth child of a node `n`.
*/ */
Self& operator[] (std::size_t index) { Self operator[](std::size_t index) const {
CGAL_precondition (!is_null());
CGAL_precondition (!is_leaf()); CGAL_precondition (!is_leaf());
CGAL_precondition (0 <= index && index < Degree::value); CGAL_precondition (0 <= index && index < Degree::value);
return (*m_children)[index]; return (*m_data->children)[index];
}
/*!
\brief returns the nth child fo this node.
\pre `!is_leaf()`
\pre `0 <= index && 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 (0 <= index && index < Degree::value);
return (*m_children)[index];
} }
/// @} /// @}
@ -272,22 +219,31 @@ public:
/*! /*!
\brief returns this node's depth. \brief returns this node's depth.
\pre `!is_null()`
*/ */
std::uint8_t depth() const { return m_depth; } std::uint8_t depth() const
{
CGAL_precondition (!is_null());
return m_data->depth;
}
/*! /*!
\brief returns this node's location. \brief returns this node's location.
\pre `!is_null()`
*/ */
Int_location location() const { Int_location location() const
{
return m_location; CGAL_precondition (!is_null());
return m_data->location;
} }
/*! /*!
\brief returns this node's index in relation to its parent. \brief returns this node's index in relation to its parent.
\pre `!is_null()`
*/ */
Index index() const { Index index() const {
CGAL_precondition (!is_null());
// TODO: There must be a better way of doing this! // TODO: There must be a better way of doing this!
Index result; Index result;
@ -299,72 +255,91 @@ public:
} }
/*! /*!
\brief returns `true` if the node has no parent, `false` otherwise. \brief returns `true` if the node is null, `false` otherwise.
*/ */
bool is_root() const { return (!m_parent); } 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();
}
/*! /*!
\brief returns `true` if the node has no children, `false` otherwise. \brief returns `true` if the node has no children, `false` otherwise.
\pre `!is_null()`
*/ */
bool is_leaf() const { return (!m_children); } bool is_leaf() const
{
CGAL_precondition(!is_null());
return (!m_data->children);
}
/*! /*!
* \brief find the directly adjacent node in a specific direction \brief find the directly adjacent node in a specific direction
*
* Adjacent nodes are found according to several properties: \pre `!is_null()`
* - Adjacent nodes may be larger than the seek node, but never smaller \pre `direction.to_ulong < 2 * Dimension::value`
* - A node can have no more than 6 different adjacent nodes (left, right, up, down, front, back)
* - A node is free to have fewer than 6 adjacent nodes Adjacent nodes are found according to several properties:
* (e.g. edge nodes have no neighbors in some directions, the root node has none at all). - Adjacent nodes may be larger than the seek node, but never smaller
* - Adjacent nodes are not required to be leaf nodes - A node can have no more than 6 different adjacent nodes (left, right, up, down, front, back)
* - A node is free to have fewer than 6 adjacent nodes
* (e.g. edge nodes have no neighbors in some directions, the root node has none at all).
* Here's a diagram demonstrating the concept for a quadtree. - Adjacent nodes are not required to be leaf nodes
* Because it's in 2d space, the seek node has only four neighbors (up, down, left, right)
* Here's a diagram demonstrating the concept for a quadtree.
* +---------------+---------------+ Because it's in 2d space, the seek node has only four neighbors (up, down, left, right)
* | | |
* | | | +---------------+---------------+
* | | | | | |
* | A | | | | |
* | | | | | |
* | | | | A | |
* | | | | | |
* +-------+-------+---+---+-------+ | | |
* | | | | | | | | |
* | A | (S) +---A---+ | +-------+-------+---+---+-------+
* | | | | | | | | | | | |
* +---+---+-------+---+---+-------+ | A | (S) +---A---+ |
* | | | | | | | | | | | |
* +---+---+ A | | | +---+---+-------+---+---+-------+
* | | | | | | | | | | | |
* +---+---+-------+-------+-------+ +---+---+ A | | |
* | | | | | |
* (S) : Seek node +---+---+-------+-------+-------+
* A : Adjacent node
* (S) : Seek node
* Note how the top adjacent node is larger than the seek node. A : Adjacent node
* The right adjacent node is the same size, even though it contains further subdivisions.
* Note how the top adjacent node is larger than the seek node.
* This implementation returns a pointer to the adjacent node if it's found. The right adjacent node is the same size, even though it contains further subdivisions.
* If there is no adjacent node in that direction, it returns nullptr.
* This implementation returns a pointer to the adjacent node if it's found.
* \todo explain how direction is encoded If there is no adjacent node in that direction, it returns nullptr.
*
* \param direction which way to find the adjacent node relative to this one \todo explain how direction is encoded
* \return a pointer to the adjacent node if it exists
\param direction which way to find the adjacent node relative to this one
\return a pointer to the adjacent node if it exists
*/ */
const Self *adjacent_node(std::bitset<Dimension::value> direction) const { Self adjacent_node (std::bitset<Dimension::value> direction) const
{
CGAL_precondition(!is_null());
// Direction: LEFT RIGHT DOWN UP BACK FRONT // Direction: LEFT RIGHT DOWN UP BACK FRONT
// direction: 000 001 010 011 100 101 // direction: 000 001 010 011 100 101
// Nodes only have up to 6 different adjacent nodes (since cubes have 6 sides) // Nodes only have up to 2*dim different adjacent nodes (since cubes have 6 sides)
assert(direction.to_ulong() < 6); CGAL_precondition(direction.to_ulong() < Dimension::value * 2);
// The root node has no adjacent nodes! // The root node has no adjacent nodes!
if (is_root()) if (is_root())
return nullptr; return Self();
// The least significant bit indicates the sign (which side of the node) // The least significant bit indicates the sign (which side of the node)
bool sign = direction[0]; bool sign = direction[0];
@ -382,43 +357,29 @@ public:
if (index()[dimension] != sign) { if (index()[dimension] != sign) {
// This means the adjacent node is a direct sibling, the offset can be applied easily! // This means the adjacent node is a direct sibling, the offset can be applied easily!
return &(*parent())[index().to_ulong() + offset]; return parent()[index().to_ulong() + offset];
} }
// Find the parent's neighbor in that direction if it exists // Find the parent's neighbor in that direction if it exists
auto *adjacent_node_of_parent = parent()->adjacent_node(direction); Self adjacent_node_of_parent = parent().adjacent_node(direction);
// If the parent has no neighbor, then this node doesn't have one // If the parent has no neighbor, then this node doesn't have one
if (!adjacent_node_of_parent) if (adjacent_node_of_parent.is_null())
return nullptr; return Node();
// If the parent's adjacent node has no children, then it's this node's adjacent node // 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 adjacent_node_of_parent;
// Return the nearest node of the parent by subtracting the offset instead of adding // Return the nearest node of the parent by subtracting the offset instead of adding
return &(*adjacent_node_of_parent)[index().to_ulong() - offset]; return adjacent_node_of_parent[index().to_ulong() - offset];
} }
/*! /*!
* \brief equivalent to adjacent_node, with a Direction rather than a bitset * \brief equivalent to adjacent_node, with a Direction rather than a bitset
*/ */
const Self *adjacent_node(Adjacency adjacency) const { Self adjacent_node(Adjacency adjacency) const {
return adjacent_node(std::bitset<Dimension::value>(static_cast<int>(adjacency)));
}
/*!
* \brief equivalent to adjacent_node, except non-const
*/
Self *adjacent_node(std::bitset<Dimension::value> direction) {
return const_cast<Self *>(const_cast<const Self *>(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<Dimension::value>(static_cast<int>(adjacency))); return adjacent_node(std::bitset<Dimension::value>(static_cast<int>(adjacency)));
} }
@ -431,44 +392,44 @@ public:
* \brief access to the content held by this node * \brief access to the content held by this node
* \return a reference to the collection of point indices * \return a reference to the collection of point indices
*/ */
Point_range &points() { return m_points; } Point_range &points() { return m_data->points; }
/*! /*!
* \brief access to the points via iterator * \brief access to the points via iterator
* \return the iterator at the start of the collection of point indices held by this node * \return the iterator at the start of the collection of point indices held by this node
*/ */
typename Point_range::iterator begin() { return m_points.begin(); } typename Point_range::iterator begin() { return m_data->points.begin(); }
/*! /*!
* \brief access to the points via iterator * \brief access to the points via iterator
* \return the iterator at the end of the collection of point indices held by this node * \return the iterator at the end of the collection of point indices held by this node
*/ */
typename Point_range::iterator end() { return m_points.end(); } typename Point_range::iterator end() { return m_data->points.end(); }
/*! /*!
* \brief read-only access to the content held by this node * \brief read-only access to the content held by this node
* \return a read-only reference to the collection of point indices * \return a read-only reference to the collection of point indices
*/ */
const Point_range &points() const { return m_points; } const Point_range &points() const { return m_data->points; }
/*! /*!
* \brief read-only access to the points via iterator * \brief read-only access to the points via iterator
* \return the iterator at the start of the collection of point indices held by this node * \return the iterator at the start of the collection of point indices held by this node
*/ */
typename Point_range::iterator begin() const { return m_points.begin(); } typename Point_range::iterator begin() const { return m_data->points.begin(); }
/*! /*!
* \brief read-only access to the points via iterator * \brief read-only access to the points via iterator
* \return the iterator at the end of the collection of point indices held by this node * \return the iterator at the end of the collection of point indices held by this node
*/ */
typename Point_range::iterator end() const { return m_points.end(); } typename Point_range::iterator end() const { return m_data->points.end(); }
/*! /*!
* \brief check whether this node contains any points * \brief check whether this node contains any points
* \return if this node contains no points * \return if this node contains no points
*/ */
bool empty() const { bool empty() const {
return m_points.empty(); return m_data->points.empty();
} }
/*! /*!
@ -476,7 +437,7 @@ public:
* \return the number of points this node owns * \return the number of points this node owns
*/ */
std::size_t size() const { std::size_t size() const {
return std::distance(m_points.begin(), m_points.end()); return std::distance(m_data->points.begin(), m_data->points.end());
} }
/// @} /// @}
@ -495,9 +456,12 @@ public:
bool operator==(const Self &rhs) const { bool operator==(const Self &rhs) const {
// TODO: Should I compare the values they contain // TODO: Should I compare the values they contain
// if (m_points != rhs.m_points) // if (m_data->points != rhs.m_data->points)
// return false; // return false;
if (is_null() || rhs.is_null())
return (is_null() == rhs.is_null());
// If one node is a leaf, and the other isn't, they're not the same // If one node is a leaf, and the other isn't, they're not the same
if (is_leaf() != rhs.is_leaf()) if (is_leaf() != rhs.is_leaf())
return false; return false;
@ -509,7 +473,7 @@ public:
for (int i = 0; i < Degree::value; ++i) { for (int i = 0; i < Degree::value; ++i) {
// If any child cell is different, they're not the same // If any child cell is different, they're not the same
if ((*m_children)[i] != rhs[i]) if ((*m_data->children)[i] != rhs[i])
return false; return false;
} }
} }

View File

@ -25,57 +25,57 @@ namespace Orthtrees {
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
template <typename Node> template <typename Node>
const Node* next_sibling(const Node* n) { Node next_sibling(Node n) {
// Passing null returns the first node // Passing null returns the first node
if (nullptr == n) if (n.is_null())
return nullptr; return Node();
// If this node has no parent, it has no siblings // If this node has no parent, it has no siblings
if (nullptr == n->parent()) if (n.parent().is_null())
return nullptr; return Node();
// Find out which child this is // Find out which child this is
std::size_t index = n->index().to_ulong(); std::size_t index = n.index().to_ulong();
constexpr static int degree = Node::Degree::value; constexpr static int degree = Node::Degree::value;
// Return null if this is the last child // Return null if this is the last child
if (index == degree - 1) if (index == degree - 1)
return nullptr; return Node();
// Otherwise, return the next child // Otherwise, return the next child
return &((*n->parent())[index + 1]); return n.parent()[index + 1];
} }
template <typename Node> template <typename Node>
const Node* next_sibling_up(const Node* n) { Node next_sibling_up(Node n) {
if (!n) if (n.is_null())
return nullptr; return Node();
auto up = n->parent(); Node up = n.parent();
while (nullptr != up) { while (!up.is_null()) {
if (nullptr != next_sibling(up)) if (!next_sibling(up).is_null())
return next_sibling(up); return next_sibling(up);
up = up->parent(); up = up.parent();
} }
return nullptr; return Node();
} }
template <typename Node> template <typename Node>
const Node* deepest_first_child(const Node* n) { Node deepest_first_child(Node n) {
if (!n) if (n.is_null())
return nullptr; return Node();
// Find the deepest child on the left // Find the deepest child on the left
auto first = n; Node first = n;
while (!first->is_leaf()) while (!first.is_leaf())
first = &(*first)[0]; first = first[0];
return first; return first;
} }
@ -97,7 +97,7 @@ struct Preorder {
* \return * \return
*/ */
template <typename Node> template <typename Node>
const Node* first(const Node* root) const { Node first(Node root) const {
return root; return root;
} }
@ -109,25 +109,20 @@ struct Preorder {
* \return * \return
*/ */
template <typename Node> template <typename Node>
const Node* next(const Node* n) const { Node next(Node n) const {
if (n->is_leaf()) { if (n.is_leaf()) {
auto next = next_sibling(n); Node next = next_sibling(n);
if (nullptr == next) {
if (next.is_null())
return next_sibling_up(n); return next_sibling_up(n);
}
return next; return next;
} else {
// Return the first child of this node
return &(*n)[0];
} }
else // Return the first child of this node
return n[0];
} }
}; };
@ -145,7 +140,7 @@ struct Postorder {
* \return * \return
*/ */
template <typename Node> template <typename Node>
const Node* first(const Node* root) const { Node first(Node root) const {
return deepest_first_child(root); return deepest_first_child(root);
} }
@ -158,12 +153,12 @@ struct Postorder {
* \return * \return
*/ */
template <typename Node> template <typename Node>
const Node* next(const Node* n) const { Node next(Node n) const {
auto next = deepest_first_child(next_sibling(n)); Node next = deepest_first_child(next_sibling(n));
if (!next) if (!next)
next = n->parent(); next = n.parent();
return next; return next;
} }
@ -183,7 +178,7 @@ struct Leaves {
* \return * \return
*/ */
template <typename Node> template <typename Node>
const Node* first(const Node* root) const { Node first(Node root) const {
return deepest_first_child(root); return deepest_first_child(root);
} }
@ -196,11 +191,11 @@ struct Leaves {
* \return * \return
*/ */
template <typename Node> template <typename Node>
const Node* next(const Node* n) const { Node next(Node n) const {
auto next = deepest_first_child(next_sibling(n)); Node next = deepest_first_child(next_sibling(n));
if (!next) if (next.is_null())
next = deepest_first_child(next_sibling_up(n)); next = deepest_first_child(next_sibling_up(n));
return next; return next;

View File

@ -44,7 +44,7 @@ public:
* *
* \todo * \todo
*/ */
typedef std::function<Value *(Value *)> Traversal_function; typedef std::function<Value(Value)> Traversal_function;
/// @} /// @}
@ -58,7 +58,7 @@ public:
* *
* \todo * \todo
*/ */
Traversal_iterator() : m_value(nullptr), m_next() {} Traversal_iterator() : m_value(), m_next() {}
/*! /*!
* \brief * \brief
@ -68,7 +68,7 @@ public:
* \param first * \param first
* \param next * \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 @@ private:
} }
Value &dereference() const { Value &dereference() const {
return *m_value; return const_cast<Value&>(m_value);
} }
private: private:
Value *m_value; Value m_value;
Traversal_function m_next; Traversal_function m_next;
}; };
} }

View File

@ -85,7 +85,7 @@ public:
{ {
Point_d operator() (const Array& array) const Point_d operator() (const Array& array) const
{ {
return Point_d ( /* todo */ ); return Point_d (array.begin(), array.end());
} }
}; };
#endif #endif
@ -102,7 +102,8 @@ public:
Bbox_d operator() (const Array& min, Bbox_d operator() (const Array& min,
const Array& max) const const Array& max) const
{ {
return Bbox_d ( /* todo */ ); return Bbox_d (Point_d (min.begin(), min.end()),
Point_d (max.begin(), max.end()));
} }
}; };
#endif #endif