cgal/Octree/include/CGAL/Octree/Node.h

347 lines
8.4 KiB
C++

// Copyright (c) 2007-2020 INRIA (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao
#ifndef CGAL_OCTREE_NODE_H
#define CGAL_OCTREE_NODE_H
#include <CGAL/license/Octree.h>
#include <boost/range/iterator_range.hpp>
#include <array>
#include <memory>
#include <bitset>
#include <cassert>
namespace CGAL {
namespace Octree {
namespace Node {
/*!
* \ingroup PkgOctreeClasses
*
* \brief represents a single node of the tree. Alternatively referred to as a cell, octant, or subtree
*
* \details The role of the node isn't fully stable yet
*
* \tparam Point_index is the datatype the node will contain
*/
template<typename Point_index>
class Node {
public:
/// \name Types
/// @{
/*!
* \brief array for containing the child nodes of this node
*/
typedef std::array<Node<Point_index>, 8> Child_list;
/*!
* \brief set of bits representing this node's relationship to its parent
*
* Equivalent to an array of three booleans,
* where index[0] is whether x is greater,
* index[1] is whether y is greater,
* and index[2] is whether z is greater.
* Used to represent a node's relationship to the center of its parent.
*/
typedef std::bitset<3> Index;
/*!
* \brief coordinate location representing this node's relationship with the rest of the tree
*
* Each value (x, y, z) of a location is calculated by doubling the parent's location
* and adding the Index.
* \todo Maybe I should add an example?
*/
typedef std::array<uint32_t, 3> Int_location;
/*!
* \brief a collection of point indices represented by begin and end iterators
*/
typedef boost::iterator_range<Point_index> Point_range;
/// @}
private:
Point_range m_points;
const Node<Point_index> *m_parent;
uint8_t m_depth;
Int_location m_location;
std::unique_ptr<Child_list> m_children;
public:
/// \name Construction
/// @{
/*!
* \brief creates a new node, optionally as the child of a parent
*
* If no parent is provided, the node created is assumed to be the root of a tree.
* This means that the parent reference is a nullptr, and the depth is zero.
* If a parent is provided, the node becomes the child of that parent.
* In that case, an index should be passed, telling this node its relationship to its parent.
* Depth and location are automatically determined in the constructor,
* and should generally be considered immutable after construction.
*
* \param parent A reference to the node containing this one
* \param index This node's relationship to its parent
*/
explicit Node(Node<Point_index> *parent = nullptr, Index index = 0) : m_parent(parent), m_depth(0),
m_location({0, 0, 0}) {
if (parent) {
m_depth = parent->m_depth + 1;
for (int i = 0; i < 3; i++)
m_location[i] = (2 * parent->m_location[i]) + index[i];
}
}
/// @}
/// \name Mutators
/// @{
/*!
* \brief split a node into subnodes
*
* Only leaf nodes should be split.
* When a node is split it is no longer a leaf node.
* 8 Children are constructed automatically, and their values are set.
* Contents of this node are _not_ propagated automatically.
* It's the responsibility of the caller to redistribute the points contained by a node after splitting
*/
void split() {
assert(is_leaf());
m_children = std::make_unique<Child_list>();
for (int index = 0; index < 8; index++) {
(*m_children)[index] = std::move(Node<Point_index>(this, {Index(index)}));
}
}
/*!
* \brief eliminate this node's children, making it a leaf node
*
* When a node is un-split, its children are automatically deleted.
* After un-splitting a node it will be considered a leaf node
*/
void unsplit() {
m_children.reset();
}
/// @}
/// \name Child Accessors
/// @{
/*!
* \brief access the child nodes of this node by their indices
*
* \todo Explain how index values map to the Index type
*
* Retrieves a reference to the child node described by the index.
* The operator can be chained.
* for example, to access the third child of the second child of the fifth child of a node `n`
*
* n[5][2][3];
*
* \param index The index of the child node, as an int
* \return A reference to the node
*/
Node<Point_index> &operator[](int index) {
assert(!is_leaf());
assert(0 <= index && index < 8);
return (*m_children)[index];
}
/*!
* \brief read-only access the child nodes of this node by their indices
*
* \param index The index of the child node, as an int
* \return A const reference to the node
*/
const Node<Point_index> &operator[](int index) const {
assert(!is_leaf());
assert(0 <= index && index < 8);
return (*m_children)[index];
}
/// @}
/// \name Property Accessors
/// @{
/*!
* \brief read-only access to this node's parent
*
* Ownership of a node is not equivalent to ownership of the entire tree,
* so it's not possible to obtain write access to a node's parent,
* only its children.
* Note that the return type is nullable,
* attempting to find the parent of a root node will return null.
* \todo Should I instead assert the node isn't root? (that would make this undefined behavior)
*
* \return A const pointer to the parent of this node (possibly nullptr)
*/
const Node<Point_index> *parent() const { return m_parent; }
/*!
* \brief retrieve this node's depth in the tree
* \return the depth of this node, where root has a depth of 0
*/
const uint8_t &depth() const { return m_depth; }
/*!
* \brief retrieve this node's location in the tree
*
* \todo Should I link to an explanation of the location type?
*
* \return this node's location
*/
Int_location location() const {
return m_location;
}
/*!
* \brief retrieve this node's index in relation to its parent
* \return the index of this nod3
*/
Index index() const {
// TODO: There must be a better way of doing this!
Index result;
result[0] = location()[0] & 1;
result[1] = location()[1] & 1;
result[2] = location()[2] & 1;
return result;
}
/*!
* \brief determine whether this node is a leaf node
* \return whether this node has no children
*/
bool is_leaf() const { return (!m_children); }
/*!
* \brief determine whether this node is the root node
* \return whether this node has no parent
*/
bool is_root() const { return (!m_parent); }
/// @}
/// \name Value Accessors
/// @{
/*!
* \brief access to the content held by this node
* \return a reference to the collection of point indices
*/
Point_range &points() { return m_points; }
/*!
* \brief read-only access to the content held by this node
* \return a read-only reference to the collection of point indices
*/
const Point_range &points() const { return m_points; }
/*!
* \brief check whether this node contains any points
* \return if this node contains no points
*/
bool empty() const {
return m_points.empty();
}
/*!
* \brief count the points contained by this node
* \return the number of points this node owns
*/
std::size_t number_of_points() const {
return std::distance(m_points.begin(), m_points.end());
}
/// @}
/// \name Operators
/// @{
/*!
* \brief compare the topology of this node to another node
*
* \todo
*
* \param rhs
* \return
*/
bool operator==(const Node &rhs) const {
// TODO: Should I compare the values they contain
// if (m_points != rhs.m_points)
// return false;
// If one node is a leaf, and the other isn't, they're not the same
if (is_leaf() != rhs.is_leaf())
return false;
// If both nodes are non-leaf nodes
if (!is_leaf()) {
// Check all the children
for (int i = 0; i < 8; ++i) {
// If any child cell is different, they're not the same
if (!((*m_children)[i] == rhs[i]))
return false;
}
}
// If both nodes are leaf nodes, they must be in the same location
return (location() == rhs.location());
}
bool operator!=(const Node &rhs) const {
return !operator==(rhs);
}
/// @}
};
}
}
}
#endif //CGAL_OCTREE_NODE_H