mirror of https://github.com/CGAL/cgal
373 lines
17 KiB
Plaintext
373 lines
17 KiB
Plaintext
namespace CGAL {
|
|
|
|
/*!
|
|
\mainpage User Manual
|
|
\anchor Chapter_Orthtree
|
|
|
|
\cgalAutoToc
|
|
\authors Jackson Campolattaro, Simon Giraudot, Cédric Portaneri, Tong Zhao, Pierre Alliez
|
|
|
|
\section Section_Orthtree_Introduction Introduction
|
|
|
|
Quadtrees are tree data structures in which each node encloses a
|
|
rectangular section of space, and each internal node has exactly 4
|
|
children. Octrees are a similar data structure in 3D in which each
|
|
node encloses a rectangular cuboid section of space, and each internal node has
|
|
exactly 8 children.
|
|
|
|
We call the generalization of such data structure "orthtrees", as
|
|
orthants are generalizations of quadrants and octants. The term
|
|
"hyperoctree" can also be found in literature to name such data
|
|
structures in dimensions 4 and higher.
|
|
|
|
This package provides a general data structure `Orthtree` along with
|
|
aliases for `Quadtree` and `Octree`. These trees can be constructed
|
|
with custom contents and split predicates, and iterated on with
|
|
various traversal methods. Orthants can be orthotopes and not only hypercubes.
|
|
|
|
\cgalFigureAnchor{Orthtree_fig }
|
|
<center>
|
|
<table border=0>
|
|
<tr>
|
|
<td><img border="0" src="orthtree.png"></td>
|
|
</tr>
|
|
<tr>
|
|
<td><img border="0" src="orthtree_mesh.png"></td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
\cgalFigureCaptionBegin{Orthtree_fig}
|
|
Top: an %orthtree in 3D (%octree) from a point cloud (top);
|
|
Bottom: an %orthtree in 3D (%octree) from the triangle faces of a mesh.
|
|
\cgalFigureCaptionEnd
|
|
|
|
\section Section_Orthtree_Building Building
|
|
|
|
A common purpose for an orthtree is to subdivide a collection of points,
|
|
and the Orthtree package provides a traits class for this purpose.
|
|
The points are not copied: the provided point range is used directly and rearranged by
|
|
the orthtree. Altering the point range after creating the orthtree
|
|
may leave the tree in an invalid state. The constructor returns a tree
|
|
with a single (root) node that contains all the points.
|
|
|
|
The method [refine()](@ref CGAL::Orthtree::refine) must be called to
|
|
subdivide space further. This method uses a split predicate which
|
|
takes a node as input and returns `true` if this node should be
|
|
split, `false` otherwise: this enables users to choose on what
|
|
criterion should the orthtree be refined. Predefined predicates are
|
|
provided for the point-set orthtree,
|
|
including [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth)
|
|
and [Maximum_contained_elements](@ref CGAL::Orthtrees::Maximum_contained_elements).
|
|
|
|
The simplest way to create a point-set orthtree is from a vector of points.
|
|
The constructor generally expects a separate point range and map,
|
|
but the point map defaults to `Identity_property_map` if none is provided.
|
|
|
|
The split predicate is a user-defined functor that determines whether
|
|
a node needs to be split. Custom predicates can easily be defined if
|
|
the existing ones do not match users' needs.
|
|
|
|
\subsection Section_Orthtree_Quadtree Building a Quadtree
|
|
|
|
The `Orthtree` class may be templated with `Orthtree_traits_point<>`
|
|
while specifying a 2d ambient space and thus behave as a %quadtree.
|
|
For convenience, the alias `CGAL::Quadtree` is provided.
|
|
|
|
The following example shows the construction of %quadtree from a vector of `Point_2` objects.
|
|
`quadtree.refine(10, 5)` uses the default split predicate, which
|
|
enforces a max-depth and a maximum of elements contained per node ("bucket size").
|
|
Nodes are split if their depth is less than 10, and they contain more than 5 points.
|
|
|
|
|
|
\cgalExample{Orthtree/quadtree_build_from_point_vector.cpp}
|
|
|
|
\subsection Section_Orthtree_Point_Vector Building an Octree
|
|
|
|
`Orthtree_traits_point<>` can also be templated with dimension 3 and thus
|
|
behave as an %octree. For convenience, the alias `CGAL::Octree` is provided.
|
|
|
|
The following example shows how to create an %octree from a vector of `Point_3` objects.
|
|
As with the %quadtree example, we use the default split predicate.
|
|
In this case the maximum depth is 10, and the bucket size is 1.
|
|
|
|
|
|
\cgalExample{Orthtree/octree_build_from_point_vector.cpp}
|
|
|
|
\subsection Section_Orthtree_Point_Set Building an Octree from a Point_set_3
|
|
|
|
Some data structures such as `Point_set_3` require a non-default point
|
|
map type and object. This example illustrates how to create an octree from a `Point_set_3` loaded from a file.
|
|
It also shows a more explicit way of setting the split predicate when refining the tree.
|
|
|
|
An %octree is constructed from the point set and its corresponding map,
|
|
and then written to the standard output.
|
|
|
|
The split predicate is manually constructed and passed to the refine method.
|
|
In this case, we use a maximum number of contained elements with no corresponding maximum depth.
|
|
This means that nodes will continue to be split until none contain more than 10 points.
|
|
|
|
\cgalExample{Orthtree/octree_build_from_point_set.cpp}
|
|
|
|
\subsection Section_Orthtree_Custom_Split_Precicate Building an Octree with a Custom Split Predicate
|
|
|
|
The following example illustrates how to refine an octree using a
|
|
split predicate that isn't provided by default. This particular
|
|
predicate sets a node's bucket size as a ratio of its depth. For
|
|
example, for a ratio of 2, a node at depth 2 can hold 4 points, a node
|
|
at depth 7 can hold 14.
|
|
|
|
\cgalExample{Orthtree/octree_build_with_custom_split.cpp}
|
|
|
|
\subsection Section_Orthtree_Orthtree_Point_Vector Building an Orthtree
|
|
|
|
An orthtree can also be used with an arbitrary number of dimensions.
|
|
The `Orthtree_traits_point` template can infer the arbitrary dimension count from the d-dimensional kernel.
|
|
|
|
The following example shows how to build a generalized orthtree in dimension 4.
|
|
As `std::vector<Point_d>` is manually filled with 4-dimensional points.
|
|
The vector is used as the point set, and an `Identity_property_map` is automatically
|
|
set as the orthtree's map type, so a map does not need to be provided.
|
|
|
|
\cgalExample{Orthtree/orthtree_build.cpp}
|
|
|
|
\section Section_Orthtree_Properties Properties
|
|
The Orthtree uses a mechanism to attach properties to nodes at run-time which follows \ref sectionSurfaceMesh_properties "Surface mesh properties". Each property is identified by a string and its value type and stored in a consecutive block of memory.
|
|
|
|
\section Section_Orthtree_Traversal Traversal
|
|
|
|
%Traversal is the act of navigating among the nodes of the tree.
|
|
The `Orthtree` class provides a
|
|
number of different solutions for traversing the tree.
|
|
|
|
\subsection Section_Orthtree_Manual_Traveral Manual Traversal
|
|
|
|
Because our orthtree is a form of connected acyclic undirected graph, it is possible to navigate between any two nodes.
|
|
What that means in practice is that given a node on the tree, it is possible to
|
|
access any other node using the right set of operations.
|
|
The `Node_index` type provides a handle on a node, and the `orthtree` class provides methods
|
|
that enable the user to retrieve the indices of each of its children as well as its parent (if they exist).
|
|
|
|
Given any node index `nid`, the `n`th child of that node can be found with `orthtree.child(nid, n)`.
|
|
For an octree, values of `n` from 0-7 provide access to the different children.
|
|
For non-root nodes, it is possible to access parent nodes using the `orthtree.parent()` accessor.
|
|
|
|
To access grandchildren, it isn't necessary to use nested `orthtree.child()` calls.
|
|
Instead, the `orthtree.descendant(node, a, b, ...)` convenience method is provided.
|
|
This retrieves the `b`th child of the `a`th child, to any depth.
|
|
|
|
In most cases, we want to find the descendants of the root node.
|
|
For this case, there is another convenience method `orthtree.node(a, b, c, ...)`.
|
|
This retrieves the node specified by the descent `a`, `b`, `c`.
|
|
It is equivalent to `orthtree.descendant(orthtree.root(), a, b, c, ...)`.
|
|
|
|
The following example demonstrates the use of several of these accessors.
|
|
|
|
\cgalExample{Orthtree/octree_traversal_manual.cpp}
|
|
|
|
\subsection Section_Orthtree_Preorder_Traversal Preorder Traversal
|
|
|
|
It is often useful to be able to iterate over the nodes of the tree in a particular order.
|
|
For example, the stream operator `<<` uses a traversal to print out each node.
|
|
A few traversals are provided, among them [Preorder_traversal](@ref CGAL::Orthtrees::Preorder_traversal)
|
|
and [Postorder_traversal](@ref CGAL::Orthtrees::Postorder_traversal).
|
|
Traversing a tree in preorder means to visit each parent immediately followed by its children,
|
|
whereas in postorder traversal the children are visited first.
|
|
|
|
The following example illustrates how to use the provided traversals.
|
|
|
|
A tree is constructed, and a traversal is used to create a range that can be iterated over using a for-each loop.
|
|
The default output operator for the orthtree uses the preorder traversal to do a pretty-print of the tree structure.
|
|
In this case, we print out the nodes of the tree without indentation instead.
|
|
|
|
\cgalExample{Orthtree/octree_traversal_preorder.cpp}
|
|
|
|
\subsection Section_Orthtree_Custom_Traversal Custom Traversal
|
|
|
|
Users can define their own traversal methods by creating models of the `OrthtreeTraversal` concept.
|
|
The custom traversal must provide a method which returns the starting point of the traversal (`first_index()`)
|
|
and another method which returns the next node in the sequence (`next_index()`).
|
|
`next_index()` returns an empty optional when the end of the traversal is reached.
|
|
The following example shows how to define a custom traversal that only traverses the first branch an octree:
|
|
|
|
\cgalExample{Orthtree/octree_traversal_custom.cpp}
|
|
|
|
\subsection Section_Orthtree_Cmp_Trav Comparison of Traversals
|
|
|
|
Figure \cgalFigureRef{Orthtree_traversal_fig} shows in which order
|
|
nodes are visited depending on the traversal method used.
|
|
|
|
\cgalFigureBegin{Orthtree_traversal_fig, quadtree_traversal.png}
|
|
%Quadtree visualized as a graph. Each node is labeled according to the
|
|
order in which it is visited by the traversal. When using leaves and
|
|
level traversals, the quadtree is only partially traversed.
|
|
\cgalFigureEnd
|
|
|
|
\section Section_Orthtree_Acceleration Acceleration of Common Tasks
|
|
|
|
Once an orthtree is built, its structure can be used to accelerate different tasks.
|
|
|
|
\subsection Section_Orthtree_Nearest_Neighbor Finding the Nearest Neighbor of a Point
|
|
|
|
The naive way of finding the nearest neighbor of a point requires finding the distance to all elements.
|
|
An orthtree can be used to perform the same task in significantly less time.
|
|
For large numbers of elements, this can be a large enough difference to outweigh the time spent building the tree.
|
|
|
|
Note that a kd-tree is expected to outperform the orthtree for this task on points, it should be preferred unless features specific to the orthtree are needed.
|
|
|
|
The following example illustrates how to use an octree to accelerate the search for points close to a location.
|
|
|
|
Points are loaded from a file and an octree is built.
|
|
The nearest neighbor method is invoked for several input points.
|
|
A `k` value of 1 is used to find the single closest point.
|
|
Results are put in a vector, and then printed.
|
|
|
|
\cgalExample{Orthtree/octree_find_nearest_neighbor.cpp}
|
|
|
|
Not all octrees are compatible with nearest neighbor functionality,
|
|
as the idea of a nearest neighbor may not make sense for some tree contents.
|
|
For the nearest neighbor methods to work, the traits class must implement the
|
|
`CollectionPartitioningOrthtreeTraits` concept.
|
|
|
|
\subsection Section_Orthtree_Grade Grading
|
|
|
|
An orthtree is considered "graded" if the difference of depth between two adjacent
|
|
leaves is at most 1 for every pair of leaves.
|
|
|
|
\cgalFigureBegin{Orthtree_quadree_graded_fig, quadtree_graded.png}
|
|
%Quadtree before and after being graded.
|
|
\cgalFigureEnd
|
|
|
|
The following example demonstrates how to use the grade method to eliminate large jumps in depth within the orthtree.
|
|
|
|
A tree is created such that one node is split many more times than those it borders.
|
|
[grade()](@ref CGAL::Orthtree::grade) splits the octree's nodes so that adjacent nodes never have a difference in depth greater than one.
|
|
The tree is printed before and after grading, so that the differences are visible.
|
|
|
|
\cgalExample{Orthtree/octree_grade.cpp}
|
|
|
|
\section Section_Orthtree_Performance Performance
|
|
|
|
\subsection Section_Orthtree_Performance_Construction Tree Construction
|
|
|
|
Tree construction benchmarks were conducted by randomly generating a collection of points,
|
|
and then timing the process of creating a fully refined tree which contains them.
|
|
|
|
Because of its simplicity, an octree can be constructed faster than a kd-tree.
|
|
|
|
\cgalFigureBegin{Orthtree_construction_benchmark_fig, construction_benchmark.png}
|
|
%Plot of the time to construct a tree.
|
|
\cgalFigureEnd
|
|
|
|
\subsection Section_Orthtree_Performance_Nearest_Neighbors Nearest Neighbors
|
|
|
|
%Orthtree nodes are uniform, so orthtrees will tend to have deeper hierarchies than equivalent kd-trees.
|
|
As a result, orthtrees will generally perform worse for nearest neighbor searches.
|
|
Both nearest neighbor algorithms have a theoretical complexity of \cgalBigO{log(n)},
|
|
but the orthtree can generally be expected to have a higher coefficient.
|
|
|
|
\cgalFigureBegin{Orthtree_nearest_neighbor_benchmark_fig, nearest_neighbor_benchmark.png}
|
|
%Plot of the time to find the 10 nearest neighbors of a random point using a pre-constructed tree.
|
|
\cgalFigureEnd
|
|
|
|
The performance difference between the two trees is large,
|
|
but both algorithms compare very favorably to the linear complexity of the naive approach,
|
|
which involves comparing every point to the search point.
|
|
|
|
Using the orthtree for nearest neighbor computations instead of the
|
|
kd-tree can be justified either when few queries are needed (as the
|
|
construction is faster) or when the orthtree is also needed for other
|
|
purposes.
|
|
|
|
\cgalFigureBegin{Orthtree_nearest_neighbor_benchmark_with_naive_fig, nearest_neighbor_benchmark_with_naive.png}
|
|
%Plot of the time to find nearest neighbors using tree methods and a naive approach.
|
|
\cgalFigureEnd
|
|
|
|
For nontrivial point counts, the naive approach's calculation time dwarfs that of either the %orthtree or kd-tree.
|
|
|
|
\section Section_Orthtree_Migration Migrating Code Written Before Release 6.0
|
|
|
|
The orthtree package changed to allow for custom data stored per node in the orthtree. To migrate existing code written before \cgal 6.0 `Orthtree_traits_point` can be used for a point-based orthtrees. The aliases `CGAL::Quadtree` and `CGAL::Octree` have been extended by a boolean template parameter to allow for non-cubic cells, which is the default. The data is passed via the traits class and no longer directly to the orthtree.
|
|
|
|
Former code to declare and define an Octree with cubic cells was as follows:
|
|
\code{.cpp}
|
|
typedef CGAL::Point_set_3<Kernel::Point_3> Point_set;
|
|
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
|
|
...
|
|
Octree octree(points, points.point_map());
|
|
\endcode
|
|
|
|
\cgal 6.0 code with identical behavior is now:
|
|
\code{.cpp}
|
|
typedef CGAL::Point_set_3<Kernel::Point_3> Point_set;
|
|
typedef CGAL::Octree<Kernel, Point_set, Point_map, false> Octree;
|
|
...
|
|
Octree octree(points, points.point_map());
|
|
\endcode
|
|
|
|
The node class does not exist anymore and has been replaced by the lightweight type `Node_index`. All information formerly contained in the node class is now accessible via the `Orthtree` interface.
|
|
|
|
Former node access was as follows:
|
|
\code{.cpp}
|
|
Orthtree::Node root = orthtree.root();
|
|
Orthtree::Node child = root[0];
|
|
bool is_leaf = child.is_leaf();
|
|
|
|
for (Orthtree::Node::const_iterator it = child.begin(); it != child.end(); it++) {
|
|
const Orthtree::Point &p = *it;
|
|
...
|
|
}
|
|
\endcode
|
|
|
|
\cgal 6.0 node access is now:
|
|
\code{.cpp}
|
|
using Point = Kernel::Point_3;
|
|
using Point_set = CGAL::Point_set_3<Point>;
|
|
Point_set points;
|
|
...
|
|
Orthtree::Node_index root = orthtree.root();
|
|
Orthtree::Node_index child = orthtree.child(root, 0);
|
|
bool is_leaf = orthtree.is_leaf(child);
|
|
|
|
for (Octree::Traits::Node_data_element& e : octree.data(child)) {
|
|
// Using a pointmap is necessary when using a Point_set_3.
|
|
Point& p = points.point(e);
|
|
|
|
// If the orthtree is used with a std::vector<Point>, Node_data_element is identical to Point.
|
|
// Point& p = e;
|
|
...
|
|
}
|
|
\endcode
|
|
|
|
The nearest neighbor search behaves as before, however, the output iterator will be required to store `CollectionPartitioningOrthtreeTraits::Node_data_element`. This may be the actual point or, e.g., in case a `Point_set_3` is used, an index which requires the use of a point map to retrieve the point.
|
|
|
|
The provided traversals, i.e., `CGAL::Orthtrees::Leaves_traversal`, `CGAL::Orthtrees::Preorder_traversal`, `CGAL::Orthtrees::Postorder_traversal`, require the orthtree as template parameter now.
|
|
|
|
Former traversal use was as follows:
|
|
\code{.cpp}
|
|
for (Orthtree::Node node : orthtree.traverse<CGAL::Orthtrees::Leaves_traversal>())
|
|
...
|
|
\endcode
|
|
|
|
\cgal 6.0 traversal use is now:
|
|
\code{.cpp}
|
|
for (Orthtree::Node_index node : orthtree.traverse<CGAL::Orthtrees::Leaves_traversal<Orthtree>>())
|
|
...
|
|
\endcode
|
|
|
|
\section Section_Orthtree_History History
|
|
|
|
A prototype code was implemented by Pierre Alliez and improved by Tong
|
|
Zhao and Cédric Portaneri. From this prototype code, the package was
|
|
developed by Jackson Campolattaro as part of the Google Summer of Code 2020.
|
|
Simon Giraudot, supervisor of the GSoC internship, completed and
|
|
finalized the package for integration in CGAL 5.3. Pierre Alliez
|
|
provided kind help and advice all the way through.
|
|
Starting with CGAL 6.0 an API improvement of the Orthtree package was released.
|
|
Most notably, the orthtree nodes do not need to store anything. Data to be stored
|
|
by the node are managed through a mechanism of dynamic properties.
|
|
This improvement was done by Jackson Campolattaro thanks to a funding provided by
|
|
INRIA, together with GeometryFactory.
|
|
|
|
*/
|
|
|
|
} |