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.
This commit is contained in:
JacksonCampolattaro 2023-03-23 09:58:20 +01:00
parent 6e901604cb
commit 4f6d249c1f
10 changed files with 386 additions and 400 deletions

View File

@ -35,11 +35,13 @@ namespace CGAL {
\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` \tparam PointMap must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Point_3`
*/ */
template <typename GeomTraits, typename PointRange, template <
typename PointMap = Identity_property_map typename GeomTraits,
<typename std::iterator_traits<typename PointRange::iterator>::value_type> > typename PointRange,
typename PointMap = Identity_property_map<typename std::iterator_traits<typename PointRange::iterator>::value_type>
>
#ifdef DOXYGEN_RUNNING #ifdef DOXYGEN_RUNNING
class Octree; class Octree;
#else #else
using Octree = Orthtree<Orthtree_traits_3<GeomTraits>, PointRange, PointMap>; using Octree = Orthtree<Orthtree_traits_3<GeomTraits>, PointRange, PointMap>;
#endif #endif

View File

@ -40,7 +40,8 @@
#include <vector> #include <vector>
#include <math.h> #include <math.h>
namespace CGAL { namespace CGAL
{
/*! /*!
\ingroup PkgOrthtreeClasses \ingroup PkgOrthtreeClasses
@ -63,7 +64,7 @@ namespace CGAL {
\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 `Traits_::Point_d` \tparam PointMap_ must be a model of `ReadablePropertyMap` whose value type is `Traits_::Point_d`
*/ */
template<typename Traits_, typename PointRange_, template <typename Traits_, typename PointRange_,
typename PointMap_ = Identity_property_map<typename Traits_::Point_d> > typename PointMap_ = Identity_property_map<typename Traits_::Point_d> >
class Orthtree class Orthtree
{ {
@ -105,7 +106,7 @@ public:
/*! /*!
* \brief Degree of the tree (number of children of non-leaf nodes). * \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. * \brief The Sub-tree / Orthant type.
@ -123,7 +124,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<Node> > Node_range; typedef boost::iterator_range<Traversal_iterator<const Node>> Node_range;
#endif #endif
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
@ -131,7 +132,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<Node(Node)> Node_traversal_method_const; typedef std::function<const Node *(const Node *)> Node_traversal_method_const;
/// \endcond /// \endcond
@ -193,48 +194,48 @@ public:
PointMap point_map = PointMap(), PointMap point_map = PointMap(),
const FT enlarge_ratio = 1.2, const FT enlarge_ratio = 1.2,
Traits traits = Traits()) Traits traits = Traits())
: 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) , m_root() // todo: can this be default-constructed?
{ {
Array bbox_min; Array bbox_min;
Array bbox_max; Array bbox_max;
// init bbox with first values found // 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; std::size_t i = 0;
for (const FT& x : cartesian_range(p)) for (const FT& x: cartesian_range(p))
{ {
bbox_min[i] = x; bbox_min[i] = x;
bbox_max[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; 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_min[i] = (std::min)(x, bbox_min[i]);
bbox_max[i] = (std::max)(x, bbox_max[i]); bbox_max[i] = (std::max)(x, bbox_max[i]);
++ i; ++i;
} }
} }
Array bbox_centroid; Array bbox_centroid;
FT max_length = FT(0); 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); 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 = (std::max)(max_length, bbox_max[i] - bbox_min[i]);
} }
max_length *= enlarge_ratio / FT(2); 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_min[i] = bbox_centroid[i] - max_length;
bbox_max[i] = bbox_centroid[i] + max_length; bbox_max[i] = bbox_centroid[i] + max_length;
@ -254,47 +255,31 @@ public:
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
// copy constructor // copy constructor
Orthtree (const Orthtree& other) Orthtree(const Orthtree& other)
: m_traits (other.m_traits) : m_traits(other.m_traits)
, m_range (other.m_range) , m_range(other.m_range)
, m_point_map (other.m_point_map) , m_point_map(other.m_point_map)
, m_root (other.m_root.deep_copy()) , m_root(other.m_root.deep_copy())
, m_bbox_min (other.m_bbox_min) , 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) {}
{ }
// move constructor // move constructor
Orthtree (Orthtree&& other) Orthtree(Orthtree&& other)
: m_traits (other.m_traits) : m_traits(other.m_traits)
, m_range (other.m_range) , m_range(other.m_range)
, m_point_map (other.m_point_map) , m_point_map(other.m_point_map)
, m_root (other.m_root) , m_root(other.m_root)
, m_bbox_min (other.m_bbox_min) , 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) {
{ other.m_root = Node{};
other.m_root = Node(Node(), 0);
} }
// Non-necessary but just to be clear on the rule of 5: // Non-necessary but just to be clear on the rule of 5:
// assignment operators deleted (PointRange is a ref) // assignment operators deleted (PointRange is a ref)
Orthtree& operator= (const Orthtree& other) = delete; Orthtree& operator=(const Orthtree& other) = delete;
Orthtree& operator= (Orthtree&& other) = delete;
// Destructor Orthtree& operator=(Orthtree&& other) = delete;
~Orthtree()
{
std::queue<Node> 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();
}
}
// move constructor // move constructor
/// \endcond /// \endcond
@ -320,52 +305,39 @@ public:
void refine(const Split_predicate& split_predicate) { void refine(const Split_predicate& split_predicate) {
// If the tree has already been refined, reset it // If the tree has already been refined, reset it
if (!m_root.is_leaf()){ if (!m_root.is_leaf())
std::queue<Node> 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();
}
m_root.unsplit(); m_root.unsplit();
}
// Reset the side length map, too // Reset the side length map, too
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
Node current = todo.front(); auto 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() == depth()) { if (current->depth() == depth()) {
// 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]);
} }
} }
@ -401,52 +373,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 (Node leaf : traverse(Orthtrees::Leaves_traversal())) { for (const Node &leaf : traverse(Orthtrees::Leaves_traversal())) {
// 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(leaf); leaf_nodes.push(const_cast<Node *>(&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
Node neighbor = node.adjacent_node(direction); auto *neighbor = node->adjacent_node(direction);
// If it doesn't exist, skip it // If it doesn't exist, skip it
if (neighbor.is_null()) if (!neighbor)
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]);
} }
} }
} }
@ -459,9 +431,22 @@ public:
/// @{ /// @{
/*! /*!
\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.
*/ */
Node root() const { return m_root; } 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() { 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
@ -472,9 +457,9 @@ public:
\sa `Node::operator[]()` \sa `Node::operator[]()`
\param index the index of the child node. \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). \brief returns the deepest level reached by a leaf node in this tree (root being level 0).
@ -497,13 +482,13 @@ public:
template<typename Traversal> template<typename Traversal>
Node_range traverse(const Traversal &traversal = Traversal()) const { 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 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<Node>(first, next), return boost::make_iterator_range(Traversal_iterator<const Node>(first, next),
Traversal_iterator<Node>()); Traversal_iterator<const Node>());
} }
/*! /*!
@ -547,19 +532,19 @@ public:
\param point query point. \param point query point.
\return the node which contains the 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 // 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::Local_coordinates index; typename Node::Local_coordinates index;
@ -568,11 +553,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;
} }
/*! /*!
@ -582,15 +567,15 @@ public:
`query`. `query`.
\tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects. \tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects.
\param query a query point. \param query query point.
\param k the number of neighbors. \param k number of neighbors.
\param output the output iterator. \param output output iterator.
*/ */
template<typename OutputIterator> template <typename OutputIterator>
OutputIterator nearest_neighbors (const Point& query, OutputIterator nearest_neighbors(const Point& query,
std::size_t k, std::size_t k,
OutputIterator output) const { OutputIterator output) const {
Sphere query_sphere (query, (std::numeric_limits<FT>::max)()); Sphere query_sphere(query, (std::numeric_limits<FT>::max)());
return nearest_k_neighbors_in_radius(query_sphere, k, output); return nearest_k_neighbors_in_radius(query_sphere, k, output);
} }
@ -604,8 +589,8 @@ public:
\param query query sphere. \param query query sphere.
\param output output iterator. \param output output iterator.
*/ */
template<typename OutputIterator> template <typename OutputIterator>
OutputIterator nearest_neighbors (const Sphere& query, OutputIterator output) const { OutputIterator nearest_neighbors(const Sphere& query, OutputIterator output) const {
Sphere query_sphere = query; Sphere query_sphere = query;
return nearest_k_neighbors_in_radius(query_sphere, return nearest_k_neighbors_in_radius(query_sphere,
(std::numeric_limits<std::size_t>::max)(), output); (std::numeric_limits<std::size_t>::max)(), output);
@ -625,7 +610,7 @@ public:
\param output output iterator. \param output output iterator.
*/ */
template<typename Query, typename OutputIterator> template<typename Query, typename OutputIterator>
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); return intersected_nodes_recursive(query, root(), output);
} }
@ -666,7 +651,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()];
@ -677,7 +662,7 @@ public:
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; bary[i] = FT(node.global_coordinates()[i]) * size + size / FT(2) + f;
++ i; ++i;
} }
// Convert that location into a point // Convert that location into a point
@ -703,7 +688,8 @@ private: // functions :
// Split the point collection around the center point on this dimension // Split the point collection around the center point on this dimension
Range_iterator split_point = std::partition Range_iterator split_point = std::partition
(begin, end, (begin, end,
[&](const Range_type &a) -> bool { [&](const Range_type& a) -> bool
{
// This should be done with cartesian iterator but it seems // This should be done with cartesian iterator but it seems
// complicated to do efficiently // complicated to do efficiently
return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]); return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]);
@ -721,7 +707,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
CGAL_precondition (node.is_leaf()); CGAL_precondition (node.is_leaf());
@ -755,18 +741,19 @@ private: // functions :
Point point; Point point;
FT distance; FT distance;
}; };
struct Node_index_with_distance {
struct Node_index_with_distance
{
typename Node::Local_coordinates index; typename Node::Local_coordinates index;
FT distance; FT distance;
Node_index_with_distance (const typename Node::Local_coordinates& index, Node_index_with_distance(const typename Node::Local_coordinates& index,
const FT& distance) const FT& distance)
: index(index), distance(distance) : 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<Point_with_distance> &results, FT epsilon = 0) const { std::vector<Point_with_distance>& results, FT epsilon = 0) const {
// Check whether the node has children // Check whether the node has children
if (node.is_leaf()) { if (node.is_leaf()) {
@ -820,7 +807,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) {
Node child_node = node[index]; auto &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.emplace_back(typename Node::Local_coordinates(index), children_with_distances.emplace_back(typename Node::Local_coordinates(index),
@ -834,7 +821,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) {
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 // 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)) {
@ -853,9 +840,9 @@ private: // functions :
// Check if the current node intersects with the query // Check if the current node intersects with the query
if (CGAL::do_intersect(query, bbox(node))) { 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()) { if (node.is_leaf()) {
*output++ = node; *output++ = &node;
return output; return output;
} }
@ -884,9 +871,9 @@ private: // functions :
\param k the number of points to find \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 output the output iterator to add the found points to (in order of increasing distance)
*/ */
template<typename OutputIterator> template <typename OutputIterator>
OutputIterator nearest_k_neighbors_in_radius OutputIterator nearest_k_neighbors_in_radius
(Sphere& query_sphere, (Sphere &query_sphere,
std::size_t k, OutputIterator output) const { std::size_t k, OutputIterator output) const {
// Create an empty list of points // Create an empty list of points
@ -907,18 +894,16 @@ private: // functions :
public: public:
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
void dump_to_polylines (std::ostream& os) const void dump_to_polylines(std::ostream& os) const {
{ for (const Node& n: traverse<Orthtrees::Preorder_traversal>())
for (const Node& n : traverse<Orthtrees::Preorder_traversal>())
if (n.is_leaf()) if (n.is_leaf())
{ {
Bbox box = bbox(n); 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 // dump in 3D for visualisation
os << "5 " os << "5 "
<< box.xmin() << " " << box.ymin() << " 0 " << box.xmin() << " " << box.ymin() << " 0 "
@ -927,8 +912,8 @@ public:
<< box.xmax() << " " << box.ymin() << " 0 " << box.xmax() << " " << box.ymin() << " 0 "
<< box.xmin() << " " << box.ymin() << " 0" << std::endl; << 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 // Back face
os << "5 " os << "5 "
<< box.xmin() << " " << box.ymin() << " " << box.zmin() << " " << box.xmin() << " " << box.ymin() << " " << box.zmin() << " "
@ -960,12 +945,12 @@ public:
<< box.xmax() << " " << box.ymax() << " " << box.zmax() << std::endl; << 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 // Create a range of nodes
auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal()); auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal());
// Iterate over the range // Iterate over the range
for (auto &n : nodes) { for (auto& n: nodes)
{
// Show the depth // Show the depth
for (int i = 0; i < n.depth(); ++i) for (int i = 0; i < n.depth(); ++i)
os << ". "; os << ". ";

View File

@ -27,42 +27,20 @@
namespace CGAL { namespace CGAL {
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
namespace Orthtrees namespace Orthtrees {
{
// Non-documented, or testing purpose only // Non-documented, or testing purpose only
struct Node_access struct Node_access {
{
template <typename Node, typename LC> template <typename Node, typename LC>
static Node create_node (Node parent, LC local_coordinates) static Node create_node(Node* parent, LC local_coordinates) {
{
return Node(parent, local_coordinates); return Node(parent, local_coordinates);
} }
template <typename Node> template <typename Node>
static typename Node::Point_range& points(Node node) { return node.points(); } static typename Node::Point_range& points(Node& node) { return node.points(); }
template <typename Node> template <typename Node>
static void split(Node node) { return node.split(); } static void split(Node& node) { return node.split(); }
template <typename Node>
static void free(Node node)
{
typedef Dimension_tag<(2 << (Node::Dimension::value - 1))> Degree;
std::queue<Node> 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();
}
}
}; };
@ -79,9 +57,8 @@ struct Node_access
\cgalModels `ConstRange` \cgalModels `ConstRange`
*/ */
template<typename Traits, typename PointRange, typename PointMap> template <typename Traits, typename PointRange, typename PointMap>
class Orthtree<Traits, PointRange, PointMap>::Node class Orthtree<Traits, PointRange, PointMap>::Node {
{
public: public:
@ -144,20 +121,25 @@ private:
typedef boost::iterator_range<iterator> Point_range; typedef boost::iterator_range<iterator> Point_range;
/// \endcond /// \endcond
// make Node trivially copiabled // make Node trivially copiable
struct Data // struct Data
{ // {
Point_range points; // Point_range points;
Self parent; // Self parent;
std::uint8_t depth; // std::uint8_t depth;
Global_coordinates global_coordinates; // Global_coordinates global_coordinates;
std::unique_ptr<Children> children; // std::unique_ptr<Children> children;
//
// Data (Self parent)
// : parent (parent), depth (0) { }
// };
// Data* m_data;
Data (Self parent) Point_range m_points;
: parent (parent), depth (0) { } Self* m_parent; // todo: use optional<reference_wrapper<Self>> instead of Self *
}; std::uint8_t m_depth;
Global_coordinates m_global_coordinates;
Data* m_data; std::shared_ptr<Children> m_children;
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
@ -173,8 +155,9 @@ private:
* \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_data->points; } Point_range& points() { return m_points; }
const Point_range &points() const { return m_data->points; }
const Point_range& points() const { return m_points; }
/// \name Construction /// \name Construction
/// @{ /// @{
@ -194,42 +177,38 @@ private:
\param parent the node containing this one \param parent 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, Local_coordinates local_coordinates) explicit Node(Self* parent, Local_coordinates local_coordinates)
: m_data (new Data(parent)) { : m_parent(parent) {
if (!parent.is_null()) { if (parent != nullptr) {
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_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++) 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 {
Node deep_copy(Self parent = Node()) const
{
if (is_null())
return Node();
Node out; Node out;
out.m_data = new Data(parent);
out.m_data->points = m_data->points; out.m_parent = m_parent;
out.m_data->depth = m_data->depth; out.m_points = m_points;
out.m_data->global_coordinates = m_data->global_coordinates; out.m_depth = m_depth;
std::unique_ptr<Children> children; out.m_global_coordinates = m_global_coordinates;
if (!is_leaf())
{ if (!is_leaf()) {
out.m_data->children = std::make_unique<Children>(); out.m_children = std::make_shared<Children>();
for (int index = 0; index < Degree::value; index++) 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; return out;
} }
@ -251,10 +230,10 @@ private:
CGAL_precondition (is_leaf()); CGAL_precondition (is_leaf());
m_data->children = std::make_unique<Children>(); m_children = std::make_shared<Children>();
for (int index = 0; index < Degree::value; index++) { 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 @@ private:
*/ */
void unsplit() { void unsplit() {
m_data->children.reset(); m_children.reset();
} }
/// @} /// @}
@ -279,10 +258,12 @@ public:
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
// Default creates null node // 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 // 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 /// \endcond
/// \name Type & Location /// \name Type & Location
@ -291,36 +272,30 @@ public:
/*! /*!
\brief returns `true` if the node is null, `false` otherwise. \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. \brief returns `true` if the node has no parent, `false` otherwise.
\pre `!is_null()` \pre `!is_null()`
*/ */
bool is_root() const bool is_root() const {
{ return m_parent == nullptr;
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()` \pre `!is_null()`
*/ */
bool is_leaf() const bool is_leaf() const {
{ return (!m_children);
CGAL_precondition(!is_null());
return (!m_data->children);
} }
/*! /*!
\brief returns this node's depth. \brief returns this node's depth.
\pre `!is_null()` \pre `!is_null()`
*/ */
std::uint8_t depth() const std::uint8_t depth() const {
{ return m_depth;
CGAL_precondition (!is_null());
return m_data->depth;
} }
/*! /*!
@ -329,12 +304,9 @@ public:
*/ */
Local_coordinates local_coordinates() const { Local_coordinates local_coordinates() const {
CGAL_precondition (!is_null());
// TODO: There must be a better way of doing this!
Local_coordinates result; 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; result[i] = global_coordinates()[i] & 1;
return result; return result;
@ -344,10 +316,8 @@ public:
\brief returns this node's global coordinates. \brief returns this node's global coordinates.
\pre `!is_null()` \pre `!is_null()`
*/ */
Global_coordinates global_coordinates() const Global_coordinates global_coordinates() const {
{ return m_global_coordinates;
CGAL_precondition (!is_null());
return m_data->global_coordinates;
} }
@ -356,18 +326,16 @@ public:
/*! /*!
\brief returns this node's parent. \brief returns this node's parent.
\pre `!is_null()` \pre `!is_root()`
*/ */
Self parent() const const Self* parent() const {
{ CGAL_precondition (!is_root());
CGAL_precondition (!is_null()); return m_parent;
return m_data->parent;
} }
/*! /*!
\brief returns the nth child of this node. \brief returns the nth child of this node.
\pre `!is_null()`
\pre `!is_leaf()` \pre `!is_leaf()`
\pre `0 <= index && index < Degree::value` \pre `0 <= index && index < Degree::value`
@ -418,13 +386,29 @@ public:
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) const { Self& operator[](std::size_t index) {
CGAL_precondition (!is_null());
CGAL_precondition (!is_leaf()); CGAL_precondition (!is_leaf());
CGAL_precondition (index < Degree::value); 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 @@ public:
\return the adjacent node if it exists, a null node otherwise. \return the adjacent node if it exists, a null node otherwise.
*/ */
Self adjacent_node (Local_coordinates direction) const const Self* adjacent_node(Local_coordinates 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
@ -489,8 +471,7 @@ public:
CGAL_precondition(direction.to_ulong() < Dimension::value * 2); 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];
@ -506,24 +487,22 @@ public:
// Check if this child has the opposite sign along the direction's axis // Check if this child has the opposite sign along the direction's axis
if (local_coordinates()[dimension] != sign) { if (local_coordinates()[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()[local_coordinates().to_ulong() + offset]; return &(*parent())[local_coordinates().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
Self adjacent_node_of_parent = parent().adjacent_node(direction); const 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.is_null()) if (!adjacent_node_of_parent) 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[local_coordinates().to_ulong() - offset]; return &(*adjacent_node_of_parent)[local_coordinates().to_ulong() - offset];
} }
@ -531,7 +510,21 @@ public:
\brief equivalent to `adjacent_node()`, with an adjacency direction \brief equivalent to `adjacent_node()`, with an adjacency direction
rather than a bitset. rather than a bitset.
*/ */
Self adjacent_node(Adjacency adjacency) const { 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)));
} }
@ -544,27 +537,27 @@ public:
\brief checks whether the node is empty of points or not. \brief checks whether the node is empty of points or not.
*/ */
bool empty() const { bool empty() const {
return m_data->points.empty(); return m_points.empty();
} }
/*! /*!
\brief returns the number of points of this node. \brief returns the number of points of this node.
*/ */
std::size_t size() const { 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 \brief returns the iterator at the start of the collection of
points held by this node. 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 \brief returns the iterator at the end of the collection of
points held by this node. 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 @@ public:
/*! /*!
* \brief compares the topology of this node to another 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 * \param rhs node to compare with
* \return whether the nodes have different topology. * \return whether the nodes have different topology.
*/ */
bool operator==(const Self &rhs) const { bool operator==(const Self& rhs) const {
return m_data == rhs.m_data;
// 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) // todo: this does what the documentation for operator== claims to do!
{ static bool is_topology_equal(const Self& a, const Self& b) {
CGAL_assertion (!a.is_null() && !b.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 (a.is_leaf() != b.is_leaf()) if (a.is_leaf() != b.is_leaf())
@ -607,8 +605,7 @@ public:
return (a.global_coordinates() == b.global_coordinates()); 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); return internal::print_orthtree_node(os, node);
} }
/// \endcond /// \endcond

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(), m_next() {} Traversal_iterator() : m_value(nullptr), 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 const_cast<Value&>(m_value); return *m_value;
} }
private: private:
Value m_value; Value *m_value;
Traversal_function m_next; Traversal_function m_next;
}; };
} }

View File

@ -24,7 +24,7 @@ namespace CGAL {
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
// Forward declaration // Forward declaration
template<typename T, typename PR, typename PM> template <typename T, typename PR, typename PM>
class Orthtree; class Orthtree;
/// \endcond /// \endcond
@ -33,86 +33,85 @@ namespace Orthtrees {
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL
template <typename Node> template <typename Node>
Node next_sibling(Node n) { const Node* next_sibling(const Node* n) {
// Passing null returns the first node // Passing null returns the first node
if (n.is_null()) if (nullptr == n)
return Node(); return nullptr;
// If this node has no parent, it has no siblings // If this node has no parent, it has no siblings
if (n.parent().is_null()) if (n->is_root())
return Node(); return nullptr;
// Find out which child this is // 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; constexpr static int degree = Node::Degree::value;
// Return null if this is the last child // Return null if this is the last child
if (int(index) == degree - 1) if (int(index) == degree - 1)
return Node(); return nullptr;
// 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>
Node next_sibling_up(Node n) { const Node* next_sibling_up(const Node* n) {
if (n.is_null()) if (!n || n->is_root()) return nullptr;
return Node();
Node up = n.parent(); auto 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(); if (up->is_root()) return nullptr;
up = up->parent();
} }
return Node(); return nullptr;
} }
template <typename Node> template <typename Node>
Node deepest_first_child(Node n) { const Node* deepest_first_child(const Node* n) {
if (n.is_null()) if (!n)
return Node(); return nullptr;
// Find the deepest child on the left // Find the deepest child on the left
Node first = n; auto first = n;
while (!first.is_leaf()) while (!first->is_leaf())
first = first[0]; first = &(*first)[0];
return first; return first;
} }
template <typename Node> template <typename Node>
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()) if (!n)
return Node(); return nullptr;
std::stack<Node> todo; std::stack<const Node*> todo;
todo.push(n); todo.push(n);
if (n.depth() == depth) if (n->depth() == depth)
return n; return n;
while (!todo.empty()) while (!todo.empty()) {
{ const Node* node = todo.top();
Node node = todo.top();
todo.pop(); todo.pop();
if (node.depth() == depth) if (node->depth() == depth)
return node; return node;
if (!node.is_leaf()) if (!node->is_leaf())
for (int i = 0; i < Node::Degree::value; ++ i) for (int i = 0; i < Node::Degree::value; ++i)
todo.push(node[std::size_t(Node::Degree::value - 1 - i)]); todo.push(&((*node)[std::size_t(Node::Degree::value - 1 - i)]));
} }
return Node(); return nullptr;
} }
/// \endcond /// \endcond
@ -128,25 +127,30 @@ Node first_child_at_depth(Node n, std::size_t depth) {
struct Preorder_traversal { struct Preorder_traversal {
template <typename Node> template <typename Node>
Node first(Node root) const { const Node* first(const Node* root) const {
return root; return root;
} }
template <typename Node> template <typename Node>
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_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];
} }
}; };
@ -161,18 +165,18 @@ struct Preorder_traversal {
struct Postorder_traversal { struct Postorder_traversal {
template <typename Node> template <typename Node>
Node first(Node root) const { const Node* first(const Node* root) const {
return deepest_first_child(root); return deepest_first_child(root);
} }
template <typename Node> template <typename Node>
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 = n.parent(); next = n->parent();
return next; return next;
} }
@ -189,17 +193,17 @@ struct Postorder_traversal {
struct Leaves_traversal { struct Leaves_traversal {
template <typename Node> template <typename Node>
Node first(Node root) const { const Node* first(const Node* root) const {
return deepest_first_child(root); return deepest_first_child(root);
} }
template <typename Node> template <typename Node>
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)); next = deepest_first_child(next_sibling_up(n));
return next; return next;
@ -226,29 +230,28 @@ public:
/*! /*!
constructs a `depth`-level traversal. constructs a `depth`-level traversal.
*/ */
Level_traversal (std::size_t depth) : depth(depth) { } Level_traversal(std::size_t depth) : depth(depth) {}
template <typename Node> template <typename Node>
Node first(Node root) const { const Node* first(const Node* root) const {
return first_child_at_depth(root, depth); return first_child_at_depth(root, depth);
} }
template <typename Node> template <typename Node>
Node next(Node n) const { const Node* next(const Node* n) const {
// fixme: leftover from debugging?
std::cerr << depth << " "; std::cerr << depth << " ";
Node next = next_sibling(n); const Node* next = next_sibling(n);
if (next.is_null()) if (!next) {
{ const Node* up = n;
Node up = n; do {
do
{
up = next_sibling_up(up); up = next_sibling_up(up);
if (up.is_null()) if (!up)
return Node(); return nullptr;
next = first_child_at_depth(up, depth); next = first_child_at_depth(up, depth);
} } while (!next);
while (next.is_null());
} }
return next; return next;

View File

@ -10,7 +10,7 @@ typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point; typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set; typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree; Octree;
typedef Octree::Node Node; typedef Octree::Node Node;
typedef Octree::Traits Traits; typedef Octree::Traits Traits;
@ -42,33 +42,36 @@ int main(void) {
std::cout << octree << std::endl; std::cout << octree << std::endl;
// Root node should have no siblings // Root node should have no siblings
assert(octree.root().adjacent_node(0).is_null()); assert(octree.root().adjacent_node(0) == nullptr);
assert(octree.root().adjacent_node(1).is_null()); assert(octree.root().adjacent_node(1) == nullptr);
assert(octree.root().adjacent_node(2).is_null()); assert(octree.root().adjacent_node(2) == nullptr);
assert(octree.root().adjacent_node(3).is_null()); assert(octree.root().adjacent_node(3) == nullptr);
assert(octree.root().adjacent_node(4).is_null()); assert(octree.root().adjacent_node(4) == nullptr);
assert(octree.root().adjacent_node(5).is_null()); assert(octree.root().adjacent_node(5) == nullptr);
// Left Top Front node should have siblings to the Right, Down, and Back // 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.root()[Traits::LEFT_TOP_BACK];
assert(octree.root()[Traits::RIGHT_TOP_BACK] == left_top_back.adjacent_node(Traits::RIGHT)); 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_BOTTOM_BACK] == left_top_back.adjacent_node(Traits::DOWN));
assert(octree.root()[Traits::LEFT_TOP_FRONT] == left_top_back.adjacent_node(Traits::FRONT)); 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::LEFT) == nullptr);
assert(left_top_back.adjacent_node(Traits::UP).is_null()); assert(left_top_back.adjacent_node(Traits::UP) == nullptr);
assert(left_top_back.adjacent_node(Traits::BACK).is_null()); assert(left_top_back.adjacent_node(Traits::BACK) == nullptr);
std::cout << octree.root()[Traits::LEFT_BOTTOM_BACK] << std::endl; 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]; 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::LEFT_BOTTOM_BACK][Traits::LEFT_TOP_BACK] ==
assert(octree.root()[Traits::RIGHT_BOTTOM_BACK] == right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT)); right_top_back_of_left_bottom_back.adjacent_node(Traits::LEFT));
assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT).is_null()); 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::UP).is_null()); assert(right_top_back_of_left_bottom_back.adjacent_node(Traits::RIGHT) != nullptr);
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::UP) != nullptr);
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::DOWN) != nullptr);
assert(!right_top_back_of_left_bottom_back.adjacent_node(Traits::FRONT).is_null()); 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; return 0;
} }

View File

@ -27,10 +27,10 @@ std::size_t count_jumps(Octree &octree) {
auto adjacent_node = node.adjacent_node(direction); auto adjacent_node = node.adjacent_node(direction);
if (adjacent_node.is_null()) if (adjacent_node == nullptr)
continue; continue;
if ((node.depth() - adjacent_node.depth()) > 1) if ((node.depth() - adjacent_node->depth()) > 1)
jumps++; jumps++;
} }
} }

View File

@ -46,14 +46,14 @@ int main(void) {
auto query = Point{1, 1, 1}; auto query = Point{1, 1, 1};
// Get a list of nodes intersected // Get a list of nodes intersected
std::vector<Octree::Node> nodes{}; std::vector<const Octree::Node *> nodes{};
octree.intersected_nodes(query, std::back_inserter(nodes)); octree.intersected_nodes(query, std::back_inserter(nodes));
// A point should only intersect one node // A point should only intersect one node
assert(1 == nodes.size()); assert(1 == nodes.size());
// That node should be the node leaf that contains the point // 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 // Intersection with a sphere
@ -63,15 +63,15 @@ int main(void) {
auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0); auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0);
// Get a list of nodes intersected // Get a list of nodes intersected
std::vector<Octree::Node> nodes{}; std::vector<const Octree::Node *> nodes{};
octree.intersected_nodes(query, std::back_inserter(nodes)); octree.intersected_nodes(query, std::back_inserter(nodes));
// Check the results // Check the results
assert(4 == nodes.size()); assert(4 == nodes.size());
assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[0]); assert(octree[Octree::Traits::RIGHT_TOP_BACK] == *nodes[0]);
assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[1]); assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[1]);
assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[2]); assert(octree[Octree::Traits::LEFT_TOP_FRONT] == *nodes[2]);
assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[3]); assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[3]);
} }
// Intersection with a ray // Intersection with a ray
@ -81,19 +81,19 @@ int main(void) {
auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0}); auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0});
// Get a list of nodes intersected // Get a list of nodes intersected
std::vector<Octree::Node> nodes{}; std::vector<const Octree::Node *> nodes{};
octree.intersected_nodes(query, std::back_inserter(nodes)); octree.intersected_nodes(query, std::back_inserter(nodes));
// Check the results // Check the results
assert(8 == nodes.size()); assert(8 == nodes.size());
assert(octree[Octree::Traits::LEFT_BOTTOM_BACK] == nodes[0]); 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::RIGHT_BOTTOM_BACK][Octree::Traits::LEFT_TOP_FRONT] == *nodes[1]);
assert(octree[Octree::Traits::LEFT_TOP_BACK] == nodes[2]); assert(octree[Octree::Traits::LEFT_TOP_BACK] == *nodes[2]);
assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[3]); assert(octree[Octree::Traits::RIGHT_TOP_BACK] == *nodes[3]);
assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == nodes[4]); assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == *nodes[4]);
assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[5]); assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == *nodes[5]);
assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[6]); assert(octree[Octree::Traits::LEFT_TOP_FRONT] == *nodes[6]);
assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[7]); assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == *nodes[7]);
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -20,7 +20,7 @@ typedef Kernel::Point_3 Point;
typedef Kernel::FT FT; typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set; typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree; Octree;
typedef CGAL::Search_traits_3<Kernel> Kd_tree_traits; typedef CGAL::Search_traits_3<Kernel> Kd_tree_traits;
typedef CGAL::Orthogonal_k_neighbor_search<Kd_tree_traits> Kd_tree_search; typedef CGAL::Orthogonal_k_neighbor_search<Kd_tree_traits> Kd_tree_search;
@ -47,7 +47,7 @@ void naive_vs_octree(std::size_t dataset_size) {
{ {
FT distance_nearest = (std::numeric_limits<FT>::max)(); FT distance_nearest = (std::numeric_limits<FT>::max)();
for (auto &p : points.points()) { for (auto& p: points.points()) {
FT distance_current = CGAL::squared_distance(p, random_point); FT distance_current = CGAL::squared_distance(p, random_point);
if (distance_current < distance_nearest) { if (distance_current < distance_nearest) {
@ -72,7 +72,6 @@ void naive_vs_octree(std::size_t dataset_size) {
octree.refine(10, 20); octree.refine(10, 20);
auto octree_start_time = high_resolution_clock::now(); auto octree_start_time = high_resolution_clock::now();
{ {
// TODO: Write a nearest-neighbor implementation and use it here
std::vector<Point> k_neighbors; std::vector<Point> k_neighbors;
octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors)); octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors));
octree_nearest = *k_neighbors.begin(); 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 kd_tree(points.points().begin(), points.points().end());
kd_tree.build(); kd_tree.build();
auto kd_tree_start_time = high_resolution_clock::now(); 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<float> kd_tree_elapsed_time = high_resolution_clock::now() - kd_tree_start_time; duration<float> 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); kd_tree_nearest_neighbors.push_back(p.first);
std::cout << "Kd_tree --> " std::cout << "Kd_tree --> "
@ -143,6 +142,7 @@ void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) {
int main(void) { int main(void) {
naive_vs_octree(21);
naive_vs_octree(500); naive_vs_octree(500);
naive_vs_octree(1000); naive_vs_octree(1000);
naive_vs_octree(10000); naive_vs_octree(10000);

View File

@ -25,12 +25,11 @@ void test_1_point() {
octree.refine(10, 1); octree.refine(10, 1);
// Check that the topology matches // 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<Node *>(nullptr), 0);
CGAL::Orthtrees::Node_access::points(single_node) CGAL::Orthtrees::Node_access::points(single_node)
= CGAL::Orthtrees::Node_access::points(octree.root()); = CGAL::Orthtrees::Node_access::points(octree.root());
assert(Node::is_topology_equal(single_node, octree.root())); assert(Node::is_topology_equal(single_node, octree.root()));
assert(0 == octree.depth()); assert(0 == octree.depth());
CGAL::Orthtrees::Node_access::free(single_node);
} }
void test_2_points() { void test_2_points() {
@ -45,12 +44,10 @@ void test_2_points() {
octree.refine(10, 1); octree.refine(10, 1);
// The octree should have been split once // 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<Node *>(nullptr), 0);
CGAL::Orthtrees::Node_access::split(other); CGAL::Orthtrees::Node_access::split(other);
assert(Node::is_topology_equal(other, octree.root())); assert(Node::is_topology_equal(other, octree.root()));
assert(1 == octree.depth()); assert(1 == octree.depth());
CGAL::Orthtrees::Node_access::free(other);
} }
void test_4_points() { void test_4_points() {
@ -66,13 +63,12 @@ void test_4_points() {
octree.refine(10, 1); octree.refine(10, 1);
// The octree should have been split once on the first level, and twice on the second // 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<Node *>(nullptr), 0);
CGAL::Orthtrees::Node_access::split(other); CGAL::Orthtrees::Node_access::split(other);
CGAL::Orthtrees::Node_access::split(other[3]); CGAL::Orthtrees::Node_access::split(other[3]);
CGAL::Orthtrees::Node_access::split(other[7]); CGAL::Orthtrees::Node_access::split(other[7]);
assert(Node::is_topology_equal(other, octree.root())); assert(Node::is_topology_equal(other, octree.root()));
assert(2 == octree.depth()); assert(2 == octree.depth());
CGAL::Orthtrees::Node_access::free(other);
} }
int main(void) { int main(void) {