Merge pull request #4852 from JacksonCampolattaro/gsoc2020-Octree-campolattaro

Add Octree Package
This commit is contained in:
Sebastien Loriot 2021-04-17 14:26:01 +02:00 committed by GitHub
commit 77e5d1d26f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 56396 additions and 1578 deletions

View File

@ -103,4 +103,4 @@ Hyperbolic_triangulation_2
Periodic_4_hyperbolic_triangulation_2
Surface_mesh_approximation
Surface_mesh_topology
Orthtree

View File

@ -130,6 +130,7 @@
\package_listing{SearchStructures}
\package_listing{Box_intersection_d}
\package_listing{AABB_tree}
\package_listing{Orthtree}
\package_listing{Spatial_sorting}
\package_listing{Optimal_bounding_box}

View File

@ -15,6 +15,15 @@ Release date: June 2021
A comprehensive list of the supported file formats is available in the Stream_support package [here](https://doc.cgal.org/5.3/Stream_support/index.html#IOstreamSupportedFormats); inversely, the following [page](https://doc.cgal.org/5.3/Stream_support/IOStreamSupportedFileFormats.html) can be used to find out which CGAL data structures can be used given a specific file format.
### [Quadtrees, Octrees, and Orthtrees](https://doc.cgal.org/5.3/Manual/packages.html#PkgOrthree) (new package)
- This package implements a tree data structure in which each node
encloses a hypercubic section of space and each non-leave node has
hypercubic children whose edge lengths are half its edge
length. Such a data structure is known as a quadtree in 2D, an
octree in 3D, and is generalized as an "orthtree" in higher
dimensions.
### [Polygon Mesh Processing](https://doc.cgal.org/5.3/Manual/packages.html#PkgPolygonMeshProcessing)
- Added the class `CGAL::Polyhedral_envelope` providing a way to quickly check if a primitive (point, segment, or triangle)

View File

@ -0,0 +1,54 @@
// Copyright (c) 2016 GeometryFactory SARL (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL$
// $Id$
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Andreas Fabri
//
// Warning: this file is generated, see include/CGAL/licence/README.md
#ifndef CGAL_LICENSE_ORTHTREE_H
#define CGAL_LICENSE_ORTHTREE_H
#include <CGAL/config.h>
#include <CGAL/license.h>
#ifdef CGAL_ORTHTREE_COMMERCIAL_LICENSE
# if CGAL_ORTHTREE_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
# if defined(CGAL_LICENSE_WARNING)
CGAL_pragma_warning("Your commercial license for CGAL does not cover "
"this release of the package.")
# endif
# ifdef CGAL_LICENSE_ERROR
# error "Your commercial license for CGAL does not cover this release \
of the package. \
You get this error, as you defined CGAL_LICENSE_ERROR."
# endif // CGAL_LICENSE_ERROR
# endif // CGAL_ORTHTREE_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
#else // no CGAL_ORTHTREE_COMMERCIAL_LICENSE
# if defined(CGAL_LICENSE_WARNING)
CGAL_pragma_warning("\nThe macro CGAL_ORTHTREE_COMMERCIAL_LICENSE is not defined."
"\nYou use the CGAL package under "
"the terms of the GPLv3+.")
# endif // CGAL_LICENSE_WARNING
# ifdef CGAL_LICENSE_ERROR
# error "The macro CGAL_ORTHTREE_COMMERCIAL_LICENSE is not defined.\
You use the CGAL package under the terms of \
the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR."
# endif // CGAL_LICENSE_ERROR
#endif // no CGAL_ORTHTREE_COMMERCIAL_LICENSE
#endif // CGAL_LICENSE_ORTHTREE_H

View File

@ -32,6 +32,7 @@ Minkowski_sum_3 3D Minkowski Sum of Polyhedra
Nef_2 2D Boolean Operations on Nef Polygons
Nef_3 3D Boolean Operations on Nef Polyhedra
Nef_S2 2D Boolean Operations on Nef Polygons Embedded on the Sphere
Orthtree Quadtrees, Octrees, and Orthrees
Optimal_bounding_box Optimal Bounding Box
Optimal_transportation_reconstruction_2 Optimal Transportation Curve Reconstruction
Partition_2 2D Polygon Partitioning

View File

@ -0,0 +1,13 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
cmake_minimum_required(VERSION 3.1...3.14)
project(Orthtree_benchmarks)
# TODO: I shouldn't leave this here
set(CMAKE_CXX_STANDARD 14)
find_package(CGAL REQUIRED QUIET OPTIONAL_COMPONENTS Core)
create_single_source_cgal_program("construction.cpp")
create_single_source_cgal_program("nearest_neighbor.cpp")

View File

@ -0,0 +1,74 @@
#define CGAL_TRACE_STREAM std::cerr
#include "util.h"
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Octree.h>
#include <CGAL/Search_traits_3.h>
#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <iostream>
#include <chrono>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
typedef CGAL::Search_traits_3<Kernel> Kd_tree_traits;
typedef CGAL::Orthogonal_k_neighbor_search<Kd_tree_traits> Kd_tree_search;
typedef Kd_tree_search::Tree Kdtree;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::milliseconds;
int main(int argc, char **argv) {
// Set output file
std::ofstream file;
file.open((argc > 1) ? argv[1] : "../construction_benchmark.csv");
// Add header for CSV
file << "Number of Points,Octree,kDTree \n";
// Perform tests for various dataset sizes
for (size_t num_points = 10; num_points < 10000000; num_points *= 1.1) {
// Create a collection of the right number of points
auto points = generate<Kernel>(num_points);
auto octreePoints = points;
auto octreeTime = bench<milliseconds>(
[&] {
// Build the tree
Octree octree(octreePoints, octreePoints.point_map());
octree.refine();
}
);
auto kdtreePoints = points;
auto kdtreeTime = bench<milliseconds>(
[&] {
// Build the tree
Kdtree kdtree(kdtreePoints.points().begin(), kdtreePoints.points().end());
kdtree.build();
}
);
file << num_points << ",";
file << octreeTime.count() << ",";
file << kdtreeTime.count() << "\n";
std::cout << num_points << std::endl;
}
file.close();
return 0;
}

View File

@ -0,0 +1,133 @@
#define CGAL_TRACE_STREAM std::cerr
#include "util.h"
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Octree.h>
#include <CGAL/Search_traits_3.h>
#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <iostream>
#include <chrono>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
typedef CGAL::Search_traits_3<Kernel> Kd_tree_traits;
typedef CGAL::Orthogonal_k_neighbor_search<Kd_tree_traits> Kd_tree_search;
typedef Kd_tree_search::Tree Kdtree;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;
int main(int argc, char **argv) {
int num_runs = 100;
size_t k = 10;
// Set output file
std::ofstream file;
file.open((argc > 1) ? argv[1] : "../nearest_neighbor_benchmark.csv");
// Add header for CSV
file << "Number of Points,Octree,kDTree \n";
// Perform tests for various dataset sizes
for (size_t num_points = 100; num_points < 100000; num_points *= 1.05) {
// We want the average of several runs for each point count, for cleaner results
float octreeAverage = 0;
float kdtreeAverage = 0;
float naiveAverage = 0;
// Repeat the tests, generating a new point set for each run
for (int i = 0; i < num_runs; ++i) {
// Create a collection of the right number of points
auto points = generate<Kernel>(num_points);
// Create a search point
auto search_point = *(generate<Kernel>().points().end() - 1);
// Build the kd tree from the point set
Kdtree kdtree(points.points().begin(), points.points().end());
kdtree.build();
// Time how long it takes to find neighbors using the kd tree
auto kdtreeTime = bench<microseconds>(
[&] {
Kd_tree_search search(kdtree, search_point, k);
}
);
// Time how long it takes to find neighbors using a naive approach
// auto naiveTime = bench<microseconds>(
// [&] {
//
// std::vector<Point> nearest_neighbors;
//
// // Iterate over every point
// for (auto &point : points.points()) {
//
// // Find out how this point ranks in comparison with other points we've saved
// auto iter = nearest_neighbors.begin();
// for (; iter < nearest_neighbors.end() &&
// CGAL::squared_distance(point, search_point) <
// CGAL::squared_distance(*iter, search_point);
// iter++) {}
//
// // Add the point to the list (it'll usually be at the end)
// nearest_neighbors.insert(iter, point);
//
// // Never keep more than k neighbors
// if (nearest_neighbors.size() > k)
// nearest_neighbors.resize(k);
//
// }
//
// }
// );
// Build the octree from points (this had to be done second because it rearranges the point set)
Octree octree(points, points.point_map());
octree.refine();
// Time how long it takes to find neighbors using the octree
auto octreeTime = bench<microseconds>(
[&] {
std::vector<Point> nearest_neighbors;
octree.nearest_neighbors(search_point, k, std::back_inserter(nearest_neighbors));
}
);
// Incorporate our results into the average
octreeAverage += (float) octreeTime.count() / (float) num_runs;
kdtreeAverage += (float) kdtreeTime.count() / (float) num_runs;
// naiveAverage += (float) naiveTime.count() / (float) num_runs;
// A simple progress indication
std::cout << ".";
}
file << num_points << ",";
file << octreeAverage << ",";
file << kdtreeAverage << ",";
// file << naiveAverage << ",";
file << "\n";
std::cout << num_points << std::endl;
}
file.close();
return 0;
}

View File

@ -0,0 +1,17 @@
reset
set terminal png size 800,500
set output 'construction_benchmark.png'
set grid
set style data lines
#set logscale x
set title 'Time to construct a tree from points'
set xlabel "Number of points"
set ylabel "Time (ms)"
set key autotitle columnhead
set datafile separator ","
plot for [col=2:10] 'construction_benchmark.csv' using 1:col

View File

@ -0,0 +1,17 @@
reset
set terminal png size 800,500
set output 'nearest_neighbor_benchmark.png'
set grid
set style data lines
#set logscale x
set title 'Time to find the neighbors of a point using a tree'
set xlabel "Number of points"
set ylabel "Time (us)"
set key autotitle columnhead
set datafile separator ","
plot for [col=2:10] 'nearest_neighbor_benchmark.csv' using 1:col

View File

@ -0,0 +1,45 @@
#ifndef OCTREE_UTIL_H
#define OCTREE_UTIL_H
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
#include <CGAL/point_generators_3.h>
#include <chrono>
template<class Kernel>
CGAL::Point_set_3<typename Kernel::Point_3> generate(size_t num_points = 1) {
typedef typename Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
// Create an empty point set
Point_set points;
points.reserve(num_points);
// Fill the point set with random points
CGAL::Random_points_on_sphere_3<Point> generator;
for (size_t i = 0; i < num_points; ++i)
points.insert(*generator++);
return points;
}
template<typename Time_unit>
Time_unit bench(const std::function<void(void)> &f, size_t repetitions = 1) {
// Start the timer
auto start = std::chrono::high_resolution_clock::now();
// Run the function being benchmarked as many times as requested
for (int i = 0; i < repetitions; ++i)
f();
// End the timer
auto end = std::chrono::high_resolution_clock::now();
// Return the elapsed time
return std::chrono::duration_cast<Time_unit>(end - start) / repetitions;
}
#endif //OCTREE_UTIL_H

View File

@ -0,0 +1,60 @@
/*!
\ingroup PkgOrthtreeConcepts
\cgalConcept
The concept `OrthtreeTraits` defines the requirements for the
template parameter of the `CGAL::Orthtree` class.
\cgalHasModel `CGAL::Orthtree_traits_2<GeomTraits>`
\cgalHasModel `CGAL::Orthtree_traits_3<GeomTraits>`
\cgalHasModel `CGAL::Orthtree_traits_d<GeomTraits,Dimension>`
*/
class OrthtreeTraits
{
public:
/// \name Types
/// @{
typedef unspecified_type Dimension; ///< Dimension type (see `CGAL::Dimension_tag`).
typedef unspecified_type Bbox_d; ///< Bounding box type.
typedef unspecified_type FT; ///< The number type of the %Cartesian coordinates of types `Point_d`
typedef unspecified_type Point_d; ///< Point type.
typedef unspecified_type Sphere_d; ///< The sphere type for neighbor queries.
/*!
A random access iterator type to enumerate the
%Cartesian coordinates of a point.
*/
typedef unspecified_type Cartesian_const_iterator_d;
typedef std::array<FT, Dimension::value> Array; ///< Array used for easy point constructions.
typedef unspecified_type Adjacency; ///< Specify the adjacency directions
/*!
Functor with an operator to construct a `Point_d` from an `Array` object.
*/
typedef unspecified_type Construct_point_d_from_array;
/*!
Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points).
*/
typedef unspecified_type Construct_bbox_d;
/// @}
/// \name Operations
/// @{
/*!
Function used to construct an object of type `Construct_point_d_from_array`.
*/
Construct_point_d_from_array construct_point_d_from_array_object() const;
/*!
Function used to construct an object of type `Construct_bbox_d`.
*/
Construct_bbox_d construct_bbox_d_object() const;
/// @}
};

View File

@ -0,0 +1,32 @@
/*!
\ingroup PkgOrthtreeConcepts
\cgalConcept
\brief a traversal provides the functions needed to traverse the
nodes of an orthtree.
A traversal is used to iterate on a tree with a user-selected order
(e.g. preorder, postorder).
\cgalHasModel `CGAL::Orthtrees::Preorder_traversal`
\cgalHasModel `CGAL::Orthtrees::Postorder_traversal`
\cgalHasModel `CGAL::Orthtrees::Leaves_traversal`
\cgalHasModel `CGAL::Orthtrees::Level_traversal`
*/
class OrthtreeTraversal {
public:
using Node = unspecified_type; ///< A specialization of [CGAL::Orthtree<Traits,PointRange,PointMap>::Node](@ref CGAL::Orthtree::Node)
/*!
\brief returns the first node to iterate to, given the root of the Orthtree.
*/
Node first (Node root) const;
/*!
\brief returns the next node to iterate to, given the previous node.
*/
Node next(Node n) const;
};

View File

@ -0,0 +1,9 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Quadtrees, Octrees, and Orthtrees"
EXTRACT_ALL = false
HIDE_UNDOC_MEMBERS = true
HIDE_UNDOC_CLASSES = true
EXAMPLE_PATH = ${CGAL_PACKAGE_DIR}/examples

View File

@ -0,0 +1,270 @@
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
square 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 cubic 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 point ranges and split predicates, and iterated on with
various traversal methods.
\cgalFigureBegin{Orthtree_fig, orthtree.png}
Building an %orthtree in 3D (%octree) from a point cloud.
\cgalFigureEnd
\section Section_Orthtree_Building Building
An orthtree is created using a set of points. The points are not
copied: the provided point range is used directly and is rearranged by
the orthtree. Altering the point range after creating the orthtree
might leave it 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 such as [Maximum_depth](@ref CGAL::Orthtrees::Maximum_depth) or [Maximum_number_of_inliers](@ref CGAL::Orthtrees::Maximum_number_of_inliers).
The simplest way to create an orthtree is using 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_2` and thus
behave as a %quadtree. For convenience, the alias `Quadtree` is provided.
The following example shows how to create a %quadtree object from a
vector of `Point_2` objects and refine it, which means constructing
the tree's space subdivision itself, using a maximum depth of 10 and a
maximum number of inliers per node (bucket size) of 5. The refinement
is stopped as soon as one of the conditions is violated: if a node has
more inliers than `bucket_size` but is already at `max_depth`, it is
not split. Similarly, a node that is at a depth smaller than
`max_depth` but already has fewer inliers than `bucket_size` is not
split.
\cgalExample{Orthtree/quadtree_build_from_point_vector.cpp}
\subsection Section_Orthtree_Point_Vector Building an Octree
The `Orthtree` class may be templated with `Orthtree_traits_3` and thus
behave as an %octree. For convenience, the alias `Octree` is provided.
The following example shows how to create an %octree from a vector of
`Point_3` objects:
\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 map.
The tree is refined with a maximum depth (deepest node allowed) of 10,
and a bucket size (maximum number of points contained by a single node) of 20.
The tree is then written to the standard output.
The split predicate is manually constructed and passed to the refine method.
\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
The following example shows how to build an generalized orthtree in dimension 4.
A `std::vector<Point_d>` is manually filled with points.
The vector is used as the point set,
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_Traversal Traversal
\note For simplicity, the rest of the user manual will only use
octrees, but all the presented features also apply to quadtrees and
higher dimension orthtrees.
%Traversal is the act of navigating among the nodes of the tree.
The `Orthtree` and [Node](@ref CGAL::Orthtree::Node) classes provide 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` class provides functions that enable the user to access each of its children, as well as its parent (if it exists).
The following example demonstrates ways of accessing different nodes of a tree, given a reference to one.
From the root node, children can be accessed using the subscript operator `CGAL::Orthtree::Node::operator[]()`.
For an octree, values from 0-7 provide access to the different children.
For non-root nodes, it is possible to access parent nodes using the [parent()](@ref CGAL::Orthtree::Node::parent) accessor.
These accessors and operators can be chained to access any node in the tree in a single line of code, as shown in the following example:
\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).
To traverse a tree in preorder is 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 following example shows how to define a
custom traversal that only traverses the first branch of the octree:
\cgalExample{Orthtree/octree_traversal_custom.cpp}
\subsection 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 labelled 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 every other point.
An orthtree can be used to perform the same task in significantly less time.
For large numbers of points, 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,
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}
\subsection Section_Orthtree_Grade Grading
An orthtree is 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 O(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_History History
A prototype code was provided by Tong Zhao and Cédric Portaneri. From
this prototype code, the package was developed by Jackson Campolatarro
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.
*/
}

View File

@ -0,0 +1,65 @@
/// \defgroup PkgOrthtreeRef Quadtree\, Octree and Orthtree Reference
/// \defgroup PkgOrthtreeClasses Classes
/// \ingroup PkgOrthtreeRef
/// \defgroup PkgOrthtreeTraits Traits
/// \ingroup PkgOrthtreeClasses
/// \defgroup PkgOrthtreeSplitPredicates Split Predicates
/// \ingroup PkgOrthtreeClasses
/// \defgroup PkgOrthtreeTraversal Traversal
/// \ingroup PkgOrthtreeClasses
/// \defgroup PkgOrthtreeConcepts Concepts
/// \ingroup PkgOrthtreeRef
/*!
\addtogroup PkgOrthtreeRef
\cgalPkgDescriptionBegin{Quadtrees\, Octrees\, and Orthtrees,PkgOrthtree}
\cgalPkgPicture{octree_thumbnail.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Jackson Campolattaro, Simon Giraudot, Cédric Portaneri, Tong Zhao, Pierre Alliez}
\cgalPkgDesc{The Orthtree package provides a data structure that subdivides space, with specializations for 2D (Quadtree) and 3D (Octree), along with a collection of algorithms for operating on these structures.}
\cgalPkgManuals{Chapter_Orthtree,PkgOrthtreeRef}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{5.3}
\cgalPkgBib{cgal:cpz-o}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
- `OrthtreeTraits`
- `OrthtreeTraversal`
\cgalCRPSection{Classes}
- `CGAL::Quadtree<GeomTraits, PointRange, PointMap>`
- `CGAL::Octree<GeomTraits, PointRange, PointMap>`
- `CGAL::Orthtree<Traits, PointRange, PointMap>`
\cgalCRPSection{Traits}
- `CGAL::Orthtree_traits_2<GeomTraits>`
- `CGAL::Orthtree_traits_3<GeomTraits>`
- `CGAL::Orthtree_traits_d<GeomTraits, Dimension>`
\cgalCRPSection{Split Predicates}
- `CGAL::Orthtrees::Maximum_number_of_inliers`
- `CGAL::Orthtrees::Maximum_depth`
- `CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers`
\cgalCRPSection{%Traversal}
- `CGAL::Orthtrees::Preorder_traversal`
- `CGAL::Orthtrees::Postorder_traversal`
- `CGAL::Orthtrees::Leaves_traversal`
- `CGAL::Orthtrees::Level_traversal`
*/

View File

@ -0,0 +1,10 @@
Circulator
Manual
Kernel_23
Kernel_d
Number_types
Point_set_3
Property_map
STL_Extension
Spatial_searching
Stream_support

View File

@ -0,0 +1,12 @@
/*!
\example Orthtree/octree_build_from_point_set.cpp
\example Orthtree/octree_build_from_point_vector.cpp
\example Orthtree/octree_build_with_custom_split.cpp
\example Orthtree/octree_find_nearest_neighbor.cpp
\example Orthtree/octree_traversal_manual.cpp
\example Orthtree/octree_traversal_preorder.cpp
\example Orthtree/octree_traversal_custom.cpp
\example Orthtree/octree_grade.cpp
\example Orthtree/quadtree_build_from_point_vector.cpp
\example Orthtree/orthtree_build.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -0,0 +1,30 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
cmake_minimum_required(VERSION 3.1...3.14)
project(Orthtree_Examples)
find_package(CGAL REQUIRED QUIET OPTIONAL_COMPONENTS Core)
if (CGAL_FOUND)
create_single_source_cgal_program("octree_build_from_point_set.cpp")
create_single_source_cgal_program("octree_build_from_point_vector.cpp")
create_single_source_cgal_program("octree_build_with_custom_split.cpp")
create_single_source_cgal_program("octree_find_nearest_neighbor.cpp")
create_single_source_cgal_program("octree_traversal_custom.cpp")
create_single_source_cgal_program("octree_traversal_manual.cpp")
create_single_source_cgal_program("octree_traversal_preorder.cpp")
create_single_source_cgal_program("octree_grade.cpp")
create_single_source_cgal_program("quadtree_build_from_point_vector.cpp")
find_package(Eigen3 3.1.91) #(requires 3.2.0 or greater)
include(CGAL_Eigen_support)
if (TARGET CGAL::Eigen_support)
create_single_source_cgal_program("orthtree_build.cpp")
target_link_libraries(orthtree_build PUBLIC CGAL::Eigen_support)
endif()
else ()
message(WARNING
"This program requires the CGAL library, and will not be compiled.")
endif ()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
int main(int argc, char **argv) {
// Point set will be used to hold our points
Point_set points;
// Load points from a file.
std::ifstream stream((argc > 1) ? argv[1] : "data/cube.pwn");
stream >> points;
if (0 == points.number_of_points()) {
std::cerr << "Error: cannot read file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl;
// Create an octree from the points
Octree octree(points, points.point_map());
// Build the octree with a small bucket size, using a more verbose method
octree.refine(CGAL::Orthtrees::Maximum_depth_and_maximum_number_of_inliers(5, 10));
// Print out the tree
std::cout << octree;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,36 @@
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef std::list<Point> Point_vector;
typedef CGAL::Octree<Kernel, Point_vector> Octree;
int main() {
// Here, our point set is a vector
Point_vector points;
// Add a few points to the vector
points.emplace_back(1, 1, 1);
points.emplace_back(2, 1, -11);
points.emplace_back(2, 1, 1);
points.emplace_back(1, -2, 1);
points.emplace_back(1, 1, 1);
points.emplace_back(-1, 1, 1);
// Create an octree from the points
Octree octree(points);
// Build the octree
octree.refine(10, 20);
// Print out the tree
std::cout << octree;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,57 @@
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
// Split Predicate
// The predicate is a functor which returns a boolean value, whether a node needs to be split or not
struct Split_by_ratio {
std::size_t ratio;
Split_by_ratio(std::size_t ratio) : ratio(ratio) {}
template<class Node>
bool operator()(const Node &n) const {
return n.size() > (ratio * n.depth());
}
};
int main(int argc, char **argv) {
// Point set will be used to hold our points
Point_set points;
// Load points from a file.
std::ifstream stream((argc > 1) ? argv[1] : "data/cube.pwn");
stream >> points;
if (0 == points.number_of_points()) {
std::cerr << "Error: cannot read file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "loaded " << points.number_of_points() << " points" << std::endl;
// Create an octree from the points
Octree octree(points, points.point_map());
// Build the octree using our custom split predicate
octree.refine(Split_by_ratio(2));
// Print out the tree
std::cout << octree;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,59 @@
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
#include <boost/iterator/function_output_iterator.hpp>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
int main(int argc, char **argv) {
// Point set will be used to hold our points
Point_set points;
// Load points from a file.
std::ifstream stream((argc > 1) ? argv[1] : "data/cube.pwn");
stream >> points;
if (0 == points.number_of_points()) {
std::cerr << "Error: cannot read file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "loaded " << points.number_of_points() << " points" << std::endl;
// Create an octree from the points
Octree octree(points, points.point_map());
// Build the octree
octree.refine(10, 20);
// Find the nearest points to a few locations
std::vector<Point> points_to_find = {
{0, 0, 0},
{1, 1, 1},
{-1, -1, -1},
{-0.46026, -0.25353, 0.32051},
{-0.460261, -0.253533, 0.320513}
};
for (const Point& p : points_to_find)
octree.nearest_neighbors
(p, 1, // k=1 to find the single closest point
boost::make_function_output_iterator
([&](const Point& nearest)
{
std::cout << "the nearest point to (" << p <<
") is (" << nearest << ")" << std::endl;
}));
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,50 @@
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef std::vector<Point> Point_vector;
typedef CGAL::Octree<Kernel, Point_vector> Octree;
int main() {
// Here, our point set is a vector
Point_vector points;
// Add a few points to the vector, most of which are in one region
points.emplace_back(1, 1, 1);
points.emplace_back(2, 1, -11);
points.emplace_back(2, 1, 1);
points.emplace_back(1, -2, 1);
points.emplace_back(1, 1, 1);
points.emplace_back(-1, 1, 1);
points.emplace_back(-1.1, 1, 1);
points.emplace_back(-1.01, 1, 1);
points.emplace_back(-1.001, 1, 1);
points.emplace_back(-1.0001, 1, 1);
points.emplace_back(-1.0001, 1, 1);
// Create an octree from the points
Octree octree(points);
// Build the octree with a small bucket size, so we get a deep node
octree.refine(10, 2);
// Print out the tree
std::cout << "\nUn-graded tree" << std::endl;
std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
std::cout << octree << std::endl;
// Grade the tree to eliminate large jumps in depth
octree.grade();
// Print out the tree again
std::cout << "\nGraded tree" << std::endl;
std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
std::cout << octree << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,59 @@
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
typedef Octree::Node Node;
struct First_branch_traversal
{
Node first (Node root) const
{
return root;
}
Node next (Node n) const
{
if (n.is_leaf())
return Node();
return n[0];
}
};
int main(int argc, char **argv) {
// Point set will be used to hold our points
Point_set points;
// Load points from a file.
std::ifstream stream((argc > 1) ? argv[1] : "data/cube.pwn");
stream >> points;
if (0 == points.number_of_points()) {
std::cerr << "Error: cannot read file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl;
// Create an octree from the points
Octree octree(points, points.point_map());
// Build the octree
octree.refine();
// Print out the first branch using custom traversal
for (auto &node : octree.traverse<First_branch_traversal>())
std::cout << node << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,67 @@
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
int main(int argc, char **argv) {
// Point set will be used to hold our points
Point_set points;
// Load points from a file.
std::ifstream stream((argc > 1) ? argv[1] : "data/cube.pwn");
stream >> points;
if (0 == points.number_of_points()) {
std::cerr << "Error: cannot read file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl;
// Create an octree from the points
Octree octree(points, points.point_map());
// Build the octree using the default arguments
octree.refine();
// Print out a few nodes
std::cout << "Navigation relative to the root node" << std::endl;
std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
std::cout << "the root node: " << std::endl;
std::cout << octree.root() << std::endl;
std::cout << "the first child of the root node: " << std::endl;
std::cout << octree.root()[0] << std::endl;
std::cout << "the fifth child: " << std::endl;
std::cout << octree.root()[4] << std::endl;
std::cout << "the fifth child, accessed without the root keyword: " << std::endl;
std::cout << octree[4] << std::endl;
std::cout << "the second child of the fourth child: " << std::endl;
std::cout << octree.root()[4][1] << std::endl;
std::cout << "the second child of the fourth child, accessed without the root keyword: " << std::endl;
std::cout << octree[4][1] << std::endl;
std::cout << std::endl;
// Retrieve one of the deeper children
Octree::Node cur = octree[3][2];
std::cout << "Navigation relative to a child node" << std::endl;
std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl;
std::cout << "the third child of the fourth child: " << std::endl;
std::cout << cur << std::endl;
std::cout << "the third child: " << 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 << cur.parent()[cur.local_coordinates().to_ulong() + 1] << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,46 @@
#include <fstream>
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Octree.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef Point_set::Point_map Point_map;
typedef CGAL::Octree<Kernel, Point_set, Point_map> Octree;
typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal;
int main(int argc, char **argv) {
// Point set will be used to hold our points
Point_set points;
// Load points from a file.
std::ifstream stream((argc > 1) ? argv[1] : "data/cube.pwn");
stream >> points;
if (0 == points.number_of_points()) {
std::cerr << "Error: cannot read file" << std::endl;
return EXIT_FAILURE;
}
std::cout << "loaded " << points.number_of_points() << " points\n" << std::endl;
// Create an octree from the points
Octree octree(points, points.point_map());
// Build the octree
octree.refine();
// Print out the octree using preorder traversal
for (Octree::Node node : octree.traverse<Preorder_traversal>()) {
std::cout << node << std::endl;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,34 @@
#include <iostream>
#include <CGAL/Epick_d.h>
#include <CGAL/Orthtree.h>
#include <CGAL/Orthtree_traits_d.h>
#include <CGAL/Random.h>
// Type Declarations
typedef CGAL::Dimension_tag<4> Dimension;
typedef CGAL::Epick_d<Dimension> Kernel;
typedef Kernel::Point_d Point_d;
typedef std::vector<Point_d> Point_vector;
typedef CGAL::Orthtree_traits_d<Kernel, Dimension> Traits;
typedef CGAL::Orthtree<Traits, Point_vector> Orthtree;
int main()
{
CGAL::Random r;
Point_vector points_dd;
for (std::size_t i = 0; i < 5; ++ i)
{
std::array<double, Dimension::value> init;
for (double& v : init)
v = r.get_double(-1., 1.);
points_dd.emplace_back (init.begin(), init.end());
}
Orthtree orthtree(points_dd);
orthtree.refine(10, 5);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,26 @@
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Quadtree.h>
#include <CGAL/Random.h>
// Type Declarations
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_2 Point_2;
typedef std::vector<Point_2> Point_vector;
typedef CGAL::Quadtree<Kernel, Point_vector> Quadtree;
int main()
{
CGAL::Random r;
Point_vector points_2d;
for (std::size_t i = 0; i < 5; ++ i)
points_2d.emplace_back(r.get_double(-1., 1.),
r.get_double(-1., 1.));
Quadtree quadtree(points_2d);
quadtree.refine(10, 5);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot
#ifndef CGAL_OCTREE_H
#define CGAL_OCTREE_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Orthtree.h>
#include <CGAL/Orthtree_traits_3.h>
namespace CGAL {
/*!
\ingroup PkgOrthtreeClasses
\brief Alias that specializes the `Orthtree` class to a 3D octree.
These two types are exactly equivalent:
- `Octree<GeomTraits, PointRange, PointMap>`
- `Orthtree<Orthtree_traits_3<GeomTraits>, PointRange, PointMap>`.
\warning This is a not a real class but an alias, please refer to
the documentation of `Orthtree`.
\tparam GeomTraits must be a model of `Kernel`
\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`
*/
template <typename GeomTraits, typename PointRange,
typename PointMap = Identity_property_map
<typename std::iterator_traits<typename PointRange::iterator>::value_type> >
#ifdef DOXYGEN_RUNNING
class Octree;
#else
using Octree = Orthtree<Orthtree_traits_3<GeomTraits>, PointRange, PointMap>;
#endif
} // namespace CGAL
#endif // CGAL_OCTREE_H

View File

@ -0,0 +1,973 @@
// 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, Simon Giraudot, Cédric Portaneri, Tong Zhao
#ifndef CGAL_ORTHTREE_H
#define CGAL_ORTHTREE_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Orthtree/Cartesian_ranges.h>
#include <CGAL/Orthtree/Split_predicates.h>
#include <CGAL/Orthtree/Traversals.h>
#include <CGAL/Orthtree/Traversal_iterator.h>
#include <CGAL/Orthtree/IO.h>
#include <CGAL/property_map.h>
#include <CGAL/intersections.h>
#include <CGAL/squared_distance_3.h>
#include <CGAL/Dimension.h>
#include <boost/function.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
#include <fstream>
#include <ostream>
#include <functional>
#include <bitset>
#include <stack>
#include <queue>
#include <vector>
#include <math.h>
namespace CGAL {
/*!
\ingroup PkgOrthtreeClasses
\brief A data structure using an axis-aligned hybercubic
decomposition of dD space for efficient point access and
computations.
\details It builds a hierarchy of nodes which subdivide the space
based on a collection of points. Each node represents an
axis-aligned hypercubic region of space. A node contains the range
of points that are present in the region it defines, and it may
contain \f$2^{dim}\f$ other nodes which further subdivide the
region.
\sa `CGAL::Quadtree`
\sa `CGAL::Octree`
\tparam Traits_ must be a model of `OrthtreeTraits`
\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`
*/
template<typename Traits_, typename PointRange_,
typename PointMap_ = Identity_property_map<typename Traits_::Point_d> >
class Orthtree
{
public:
/// \name Template Types
/// @{
typedef Traits_ Traits; ///< Geometry traits
typedef PointRange_ PointRange; ///< Point range
typedef PointMap_ PointMap; ///< Point map
/// @}
/// \name Traits Types
/// @{
typedef typename Traits::Dimension Dimension; ///< Dimension of the tree
typedef typename Traits::FT FT; ///< Number type.
typedef typename Traits::Point_d Point; ///< Point type.
typedef typename Traits::Bbox_d Bbox; ///< Bounding box type.
typedef typename Traits::Sphere_d Sphere; ///< Sphere type.
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Array Array; ///< Array type.
typedef typename Traits::Construct_point_d_from_array
Construct_point_d_from_array;
typedef typename Traits::Construct_bbox_d
Construct_bbox_d;
/// \endcond
/// @}
/// \name Public Types
/// @{
/*!
* \brief Self typedef for convenience.
*/
typedef Orthtree<Traits, PointRange, PointMap> Self;
/*!
* \brief Degree of the tree (number of children of non-leaf nodes).
*/
typedef Dimension_tag<(2 << (Dimension::value-1))> Degree;
/*!
* \brief The Sub-tree / Orthant type.
*/
class Node;
/*!
* \brief A predicate that determines whether a node must be split when refining a tree.
*/
typedef std::function<bool(Node)> Split_predicate;
/*!
* \brief A model of `ConstRange` whose value type is `Node`.
*/
#ifdef DOXYGEN_RUNNING
typedef unspecified_type Node_range;
#else
typedef boost::iterator_range<Traversal_iterator<Node> > Node_range;
#endif
/// \cond SKIP_IN_MANUAL
/*!
* \brief A function that determines the next node in a traversal given the current one.
*/
typedef std::function<Node(Node)> Node_traversal_method_const;
/// \endcond
/// \cond SKIP_IN_MANUAL
typedef typename PointRange::iterator Range_iterator;
typedef typename std::iterator_traits<Range_iterator>::value_type Range_type;
typedef Orthtrees::internal::Cartesian_ranges<Traits> Cartesian_ranges;
/// \endcond
/// @}
private: // data members :
Traits m_traits; /* the tree traits */
PointRange& m_range; /* input point range */
PointMap m_point_map; /* property map: `value_type of InputIterator` -> `Point` (Position) */
Node m_root; /* root node of the orthtree */
Point m_bbox_min; /* input bounding box min value */
std::vector<FT> m_side_per_depth; /* side length per node's depth */
Cartesian_ranges cartesian_range; /* a helper to easily iterator on coordinates of points */
public:
/// \name Constructor
/// @{
/*!
\brief creates an orthtree from a collection of points.
The constructed orthtree has a root node, with no children, that
contains the points passed. That root node has a bounding box that
encloses all of the points passed, with padding according to the
`enlarge_ratio`.
That root node is built as a `n`-cube (square in 2D, cube in 3D,
etc.) whose edge size is the longest bounding box edge multiplied
by `enlarge_ratio`. Using an `enlarge_ratio>1.0` prevents some
points from being exactly on the border of some cells, which can
lead to over-refinement.
This single-node orthtree is valid and compatible
with all Orthtree functionality, but any performance benefits are
unlikely to be realized until `refine()` is called.
\warning The input point range is not copied. It is used directly
and is rearranged by the `Orthtree`. Altering the point range
after creating the orthtree might leave it in an invalid state.
\param point_range input point range.
\param point_map property map to access the input points.
\param enlarge_ratio ratio to which the bounding box should be enlarged.
\param traits the traits object.
*/
Orthtree(PointRange& point_range,
PointMap point_map = PointMap(),
const FT enlarge_ratio = 1.2,
Traits traits = Traits())
: m_traits (traits)
, m_range (point_range)
, m_point_map (point_map)
, m_root(Node(), 0)
{
Array bbox_min;
Array bbox_max;
// init bbox with first values found
{
const Point& p = get (m_point_map, *(point_range.begin()));
std::size_t i = 0;
for (const FT& x : cartesian_range(p))
{
bbox_min[i] = x;
bbox_max[i] = x;
++ i;
}
}
for (const Range_type& r : point_range)
{
const Point& p = get (m_point_map, r);
std::size_t i = 0;
for (const FT& x : cartesian_range(p))
{
bbox_min[i] = (std::min)(x, bbox_min[i]);
bbox_max[i] = (std::max)(x, bbox_max[i]);
++ i;
}
}
Array bbox_centroid;
FT max_length = FT(0);
for (std::size_t i = 0 ; i < Dimension::value; ++ i)
{
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 *= enlarge_ratio / FT(2);
for (std::size_t i = 0 ; i < Dimension::value; ++ i)
{
bbox_min[i] = bbox_centroid[i] - max_length;
bbox_max[i] = bbox_centroid[i] + max_length;
}
Construct_point_d_from_array construct_point_d_from_array
= m_traits.construct_point_d_from_array_object();
// save orthtree attributes
m_bbox_min = construct_point_d_from_array(bbox_min);
m_side_per_depth.push_back(bbox_max[0] - bbox_min[0]);
m_root.points() = {point_range.begin(), point_range.end()};
}
/// @}
/// \cond SKIP_IN_MANUAL
// copy constructor
Orthtree (const Orthtree& other)
: m_traits (other.m_traits)
, m_range (other.m_range)
, m_point_map (other.m_point_map)
, m_root (other.m_root.deep_copy())
, m_bbox_min (other.m_bbox_min)
, m_side_per_depth(other.m_side_per_depth)
{ }
// move constructor
Orthtree (Orthtree&& other)
: m_traits (other.m_traits)
, m_range (other.m_range)
, m_point_map (other.m_point_map)
, m_root (other.m_root)
, m_bbox_min (other.m_bbox_min)
, m_side_per_depth(other.m_side_per_depth)
{
other.m_root = Node(Node(), 0);
}
// Non-necessary but just to be clear on the rule of 5:
// assignement operators deleted (PointRange is a ref)
Orthtree& operator= (const Orthtree& other) = delete;
Orthtree& operator= (Orthtree&& other) = delete;
// Destructor
~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
/// \endcond
/// \name Tree Building
/// @{
/*!
\brief recursively subdivides the orthtree until it meets the given criteria.
The split predicate is a `std::function` that takes a `Node` and
returns a Boolean value (where `true` implies that a `Node` needs to
be split, `false` that the `Node` should be a leaf).
This function may be called several times with different
predicates: in that case, nodes already split are left unaltered,
while nodes that were not split and for which `split_predicate`
returns `true` are split.
\param split_predicate determines whether or not a node needs to
be subdivided.
*/
void refine(const Split_predicate& split_predicate) {
// If the tree has already been refined, reset it
if (!m_root.is_leaf())
m_root.unsplit();
// Reset the side length map, too
m_side_per_depth.resize(1);
// Initialize a queue of nodes that need to be refined
std::queue<Node> todo;
todo.push(m_root);
// Process items in the queue until it's consumed fully
while (!todo.empty()) {
// Get the next element
Node current = todo.front();
todo.pop();
// Check if this node needs to be processed
if (split_predicate(current)) {
// Check if we've reached a new max depth
if (current.depth() == depth()) {
// Update the side length map
m_side_per_depth.push_back(*(m_side_per_depth.end() - 1) / 2);
}
// Split the node, redistributing its points to its children
split(current);
// Process each of its children
for (int i = 0; i < Degree::value; ++i)
todo.push(current[i]);
}
}
}
/*!
\brief Convenience overload that refines an orthtree using a
maximum depth and maximum number of points in a node as split
predicate.
This is equivalent to calling
`refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth,
bucket_size))`.
The refinement is stopped as soon as one of the conditions is
violated: if a node has more inliers than `bucket_size` but is
already at `max_depth`, it is not split. Similarly, a node that is
at a depth smaller than `max_depth` but already has fewer inliers
than `bucket_size`, it is not split.
\param max_depth deepest a tree is allowed to be (nodes at this depth will not be split).
\param bucket_size maximum points a node is allowed to contain.
*/
void refine(size_t max_depth = 10, size_t bucket_size = 20) {
refine(Orthtrees::Maximum_depth_and_maximum_number_of_inliers(max_depth, bucket_size));
}
/*!
\brief refines the orthtree such that the difference of depth
between two immediate neighbor leaves is never more than 1.
*/
void grade() {
// Collect all the leaf nodes
std::queue<Node> leaf_nodes;
for (Node leaf : traverse(Orthtrees::Leaves_traversal())) {
// TODO: I'd like to find a better (safer) way of doing this
leaf_nodes.push(leaf);
}
// Iterate over the nodes
while (!leaf_nodes.empty()) {
// Get the next node
Node node = leaf_nodes.front();
leaf_nodes.pop();
// Skip this node if it isn't a leaf anymore
if (!node.is_leaf())
continue;
// Iterate over each of the neighbors
for (int direction = 0; direction < 6; ++direction) {
// Get the neighbor
Node neighbor = node.adjacent_node(direction);
// If it doesn't exist, skip it
if (neighbor.is_null())
continue;
// 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
if (neighbor.parent() == node.parent())
continue;
// If it's already been split, skip it
if (!neighbor.is_leaf())
continue;
// Check if the neighbor breaks our grading rule
// TODO: could the rule be parametrized?
if ((node.depth() - neighbor.depth()) > 1) {
// Split the neighbor
split(neighbor);
// Add newly created children to the queue
for (int i = 0; i < Degree::value; ++i) {
leaf_nodes.push(neighbor[i]);
}
}
}
}
}
/// @}
/// \name Accessors
/// @{
/*!
\brief returns the root node.
*/
Node root() const { return m_root; }
/*!
\brief Convenience function to access the child nodes of the root
node by their indices.
`my_tree[5]` is equivalent to `my_tree.root()[5]`.
\sa `Node::operator[]()`
\param index the index of the child node.
\return the accessed node.
*/
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).
*/
std::size_t depth() const { return m_side_per_depth.size() - 1; }
/*!
\brief constructs a node range using a tree-traversal function.
This method allows to iterate on the nodes of the tree with a
user-selected order (preorder, postorder, leaves-only, etc.).
\tparam Traversal model of `OrthtreeTraversal` that provides functions
compatible with the type of the orthree
\param traversal the instance of `Traversal` used
\return a forward input iterator over the nodes of the tree
*/
template<typename Traversal>
Node_range traverse(const Traversal &traversal = Traversal()) const {
Node first = traversal.first(m_root);
Node_traversal_method_const next
= [&](const Node& n) -> Node { return traversal.next(n); };
return boost::make_iterator_range(Traversal_iterator<Node>(first, next),
Traversal_iterator<Node>());
}
/*!
\brief constructs the bounding box of a node.
\note The object constructed is not the bounding box of the point
subset inside the node, but the bounding box of the node itself
(cubic).
*/
Bbox bbox(const Node &node) const {
// Determine the side length of this node
FT size = m_side_per_depth[node.depth()];
// Determine the location this node should be split
Array min_corner;
Array max_corner;
for (int i = 0; i < Dimension::value; i++) {
min_corner[i] = m_bbox_min[i] + (node.global_coordinates()[i] * size);
max_corner[i] = min_corner[i] + size;
}
// Create the bbox
Construct_bbox_d construct_bbox
= m_traits.construct_bbox_d_object();
return construct_bbox(min_corner, max_corner);
}
/// @}
/// \name Queries
/// @{
/*!
\brief finds the leaf node which contains the point `p`.
Traverses the orthtree and finds the deepest cell that has a
domain enclosing the point passed. The point passed must be within
the region enclosed by the orthtree (bbox of the root node).
\param point query point.
\return the node which contains the point.
*/
Node locate(const Point &point) const {
// Make sure the point is enclosed by the orthtree
CGAL_precondition (CGAL::do_intersect(point, bbox(m_root)));
// Start at the root node
auto node_for_point = m_root;
// Descend the tree until reaching a leaf node
while (!node_for_point.is_leaf()) {
// Find the point to split around
Point center = barycenter(node_for_point);
// Find the index of the correct sub-node
typename Node::Local_coordinates index;
std::size_t dimension = 0;
for (const auto& r : cartesian_range(center, point))
index[dimension ++] = (get<0>(r) < get<1>(r));
// Find the correct sub-node of the current node
node_for_point = node_for_point[index.to_ulong()];
}
// Return the result
return node_for_point;
}
/*!
\brief finds the `k` nearest neighbors of `query`.
Nearest neighbors are outputted in order of increasing distance to
`query`.
\tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects.
\param query a query point.
\param k the number of neighbors.
\param output the output iterator.
*/
template<typename OutputIterator>
OutputIterator nearest_neighbors (const Point& query,
std::size_t k,
OutputIterator output) const {
Sphere query_sphere (query, (std::numeric_limits<FT>::max)());
return nearest_k_neighbors_in_radius(query_sphere, k, output);
}
/*!
\brief finds the points in `sphere`.
Nearest neighbors are outputted in order of increasing distance to
the center of `sphere`.
\tparam OutputIterator a model of `OutputIterator` that accept `Point_d` objects.
\param query query sphere.
\param output output iterator.
*/
template<typename OutputIterator>
OutputIterator nearest_neighbors (const Sphere& query, OutputIterator output) const {
Sphere query_sphere = query;
return nearest_k_neighbors_in_radius(query_sphere,
(std::numeric_limits<std::size_t>::max)(), output);
}
/*!
\brief finds the leaf nodes that intersect with any primitive.
\note this function requires the function
`bool CGAL::do_intersect(QueryType, Traits::Bbox_d)` to be defined.
This function finds all the intersecting nodes and returns them as const pointers.
\tparam Query the primitive class (e.g. sphere, ray)
\tparam OutputIterator a model of `OutputIterator` that accepts `Node` objects
\param query the intersecting primitive.
\param output output iterator.
*/
template<typename Query, typename OutputIterator>
OutputIterator intersected_nodes (const Query& query, OutputIterator output) const {
return intersected_nodes_recursive(query, root(), output);
}
/// @}
/// \name Operators
/// @{
/*!
\brief compares the topology of the orthtree with that of `rhs`.
Trees may be considered equivalent even if they contain different points.
Equivalent trees must have the same bounding box and the same node structure.
Node structure is evaluated by comparing the root nodes using the node equality operator.
*/
bool operator==(const Self &rhs) const {
// Identical trees should have the same bounding box
if (rhs.m_bbox_min != m_bbox_min || rhs.m_side_per_depth[0] != m_side_per_depth[0])
return false;
// Identical trees should have the same depth
if (rhs.depth() != depth())
return false;
// If all else is equal, recursively compare the trees themselves
return Node::is_topology_equal(rhs.m_root, m_root);
}
/*!
\brief compares the topology of the orthtree with that of `rhs`.
*/
bool operator!=(const Self &rhs) const {
return !operator==(rhs);
}
/// @}
// TODO: Document this
// TODO: Could this method name be reduced to just "center" ?
Point barycenter(const Node& node) const {
// Determine the side length of this node
FT size = m_side_per_depth[node.depth()];
// Determine the location this node should be split
Array bary;
std::size_t i = 0;
for (const FT& f : cartesian_range(m_bbox_min))
{
bary[i] = FT(node.global_coordinates()[i]) * size + size / FT(2) + f;
++ i;
}
// Convert that location into a point
Construct_point_d_from_array construct_point_d_from_array
= m_traits.construct_point_d_from_array_object();
return construct_point_d_from_array(bary);
}
private: // functions :
void reassign_points(Node &node, Range_iterator begin, Range_iterator end, const Point &center,
std::bitset<Dimension::value> coord = {},
std::size_t dimension = 0) {
// Root case: reached the last dimension
if (dimension == Dimension::value) {
node[coord.to_ulong()].points() = {begin, end};
return;
}
// Split the point collection around the center point on this dimension
Range_iterator split_point = std::partition
(begin, end,
[&](const Range_type &a) -> bool {
// This should be done with cartesian iterator but it seems
// complicated to do efficiently
return (get(m_point_map, a)[int(dimension)] < center[int(dimension)]);
});
// Further subdivide the first side of the split
std::bitset<Dimension::value> coord_left = coord;
coord_left[dimension] = false;
reassign_points(node, begin, split_point, center, coord_left, dimension + 1);
// Further subdivide the second side of the split
std::bitset<Dimension::value> coord_right = coord;
coord_right[dimension] = true;
reassign_points(node, split_point, end, center, coord_right, dimension + 1);
}
void split(Node& node) {
// Make sure the node hasn't already been split
CGAL_precondition (node.is_leaf());
// Split the node to create children
node.split();
// Find the point to around which the node is split
Point center = barycenter(node);
// Add the node's points to its children
reassign_points(node, node.points().begin(), node.points().end(), center);
}
bool do_intersect(const Node &node, const Sphere &sphere) const {
// Create a cubic bounding box from the node
Bbox node_cube = bbox(node);
// Check for overlap between the node's box and the sphere as a box, to quickly catch some cases
// FIXME: Activating this causes slower times
// if (!do_overlap(node_cube, sphere.bbox()))
// return false;
// Check for intersection between the node and the sphere
return CGAL::do_intersect(node_cube, sphere);
}
// TODO: There has to be a better way than using structs like these!
struct Point_with_distance {
Point point;
FT distance;
};
struct Node_index_with_distance {
typename Node::Local_coordinates index;
FT distance;
Node_index_with_distance (const typename Node::Local_coordinates& index,
const FT& distance)
: index(index), distance(distance)
{ }
};
void nearest_k_neighbors_recursive(Sphere& search_bounds, const Node &node,
std::vector<Point_with_distance> &results, FT epsilon = 0) const {
// Check whether the node has children
if (node.is_leaf()) {
// Base case: the node has no children
// Loop through each of the points contained by the node
// Note: there might be none, and that should be fine!
for (auto point_index : node.points()) {
// Retrieve each point from the orthtree's point map
auto point = get(m_point_map, point_index);
// Pair that point with its distance from the search point
Point_with_distance current_point_with_distance =
{point, squared_distance(point, search_bounds.center())};
// Check if the new point is within the bounds
if (current_point_with_distance.distance < search_bounds.squared_radius()) {
// Check if the results list is full
if (results.size() == results.capacity()) {
// Delete a point if we need to make room
results.pop_back();
}
// Add the new point
results.push_back(current_point_with_distance);
// Sort the list
std::sort(results.begin(), results.end(), [=](auto &left, auto &right) {
return left.distance < right.distance;
});
// Check if the results list is full
if (results.size() == results.capacity()) {
// Set the search radius
search_bounds = Sphere(search_bounds.center(), results.back().distance + epsilon);
}
}
}
} else {
// Recursive case: the node has children
// Create a list to map children to their distances
std::vector<Node_index_with_distance> children_with_distances;
children_with_distances.reserve(Degree::value);
// Fill the list with child nodes
for (int index = 0; index < Degree::value; ++index) {
Node child_node = node[index];
// Add a child to the list, with its distance
children_with_distances.emplace_back(typename Node::Local_coordinates(index),
CGAL::squared_distance(search_bounds.center(), barycenter(child_node)));
}
// Sort the children by their distance from the search point
std::sort(children_with_distances.begin(), children_with_distances.end(), [=](auto &left, auto &right) {
return left.distance < right.distance;
});
// Loop over the children
for (auto child_with_distance : children_with_distances) {
Node child_node = node[child_with_distance.index.to_ulong()];
// Check whether the bounding box of the child intersects with the search bounds
if (do_intersect(child_node, search_bounds)) {
// Recursively invoke this function
nearest_k_neighbors_recursive(search_bounds, child_node, results);
}
}
}
}
template<typename Query, typename Node_output_iterator>
Node_output_iterator intersected_nodes_recursive(const Query &query, const Node &node,
Node_output_iterator output) const {
// Check if the current node intersects with the query
if (CGAL::do_intersect(query, bbox(node))) {
// if this node is a leaf, than it's considered an intersecting node
if (node.is_leaf()) {
*output++ = node;
return output;
}
// Otherwise, each of the children need to be checked
for (int i = 0; i < Degree::value; ++i) {
intersected_nodes_recursive(query, node[i], output);
}
}
return output;
}
/*!
\brief finds the `k` points within a specific radius that are nearest to `query`.
This function guarantees that there are no closer points than the ones returned,
but it does not guarantee that it will return at least `k` points.
For a query where the search radius encloses `k` or fewer points, all enclosed points will be returned.
If the search radius passed is too small, no points may be returned.
This function is useful when the user already knows how sparse the points are,
or if they do not care about points that are too far away.
Setting a small radius may have performance benefits.
\tparam OutputIterator must be a model of `OutputIterator` that accepts points
\param search_point the location to find points near
\param search_radius_squared the size of the region to search within
\param k the number of points to find
\param output the output iterator to add the found points to (in order of increasing distance)
*/
template<typename OutputIterator>
OutputIterator nearest_k_neighbors_in_radius
(Sphere& query_sphere,
std::size_t k, OutputIterator output) const {
// Create an empty list of points
std::vector<Point_with_distance> points_list;
if (k != (std::numeric_limits<std::size_t>::max)())
points_list.reserve(k);
// Invoking the recursive function adds those points to the vector (passed by reference)
nearest_k_neighbors_recursive(query_sphere, m_root, points_list);
// Add all the points found to the output
for (auto &item : points_list)
*output++ = item.point;
return output;
}
public:
/// \cond SKIP_IN_MANUAL
void dump_to_polylines (std::ostream& os) const
{
for (const Node& n : traverse<Orthtrees::Preorder_traversal>())
if (n.is_leaf())
{
Bbox box = bbox(n);
dump_box_to_polylines (box, os);
}
}
void dump_box_to_polylines (const Bbox_2& box, std::ostream& os) const
{
// dump in 3D for visualisation
os << "5 "
<< box.xmin() << " " << box.ymin() << " 0 "
<< box.xmin() << " " << box.ymax() << " 0 "
<< box.xmax() << " " << box.ymax() << " 0 "
<< box.xmax() << " " << box.ymin() << " 0 "
<< box.xmin() << " " << box.ymin() << " 0" << std::endl;
}
void dump_box_to_polylines (const Bbox_3& box, std::ostream& os) const
{
// Back face
os << "5 "
<< box.xmin() << " " << box.ymin() << " " << box.zmin() << " "
<< box.xmin() << " " << box.ymax() << " " << box.zmin() << " "
<< box.xmax() << " " << box.ymax() << " " << box.zmin() << " "
<< box.xmax() << " " << box.ymin() << " " << box.zmin() << " "
<< box.xmin() << " " << box.ymin() << " " << box.zmin() << std::endl;
// Front face
os << "5 "
<< box.xmin() << " " << box.ymin() << " " << box.zmax() << " "
<< box.xmin() << " " << box.ymax() << " " << box.zmax() << " "
<< box.xmax() << " " << box.ymax() << " " << box.zmax() << " "
<< box.xmax() << " " << box.ymin() << " " << box.zmax() << " "
<< box.xmin() << " " << box.ymin() << " " << box.zmax() << std::endl;
// Traversal edges
os << "2 "
<< box.xmin() << " " << box.ymin() << " " << box.zmin() << " "
<< box.xmin() << " " << box.ymin() << " " << box.zmax() << std::endl;
os << "2 "
<< box.xmin() << " " << box.ymax() << " " << box.zmin() << " "
<< box.xmin() << " " << box.ymax() << " " << box.zmax() << std::endl;
os << "2 "
<< box.xmax() << " " << box.ymin() << " " << box.zmin() << " "
<< box.xmax() << " " << box.ymin() << " " << box.zmax() << std::endl;
os << "2 "
<< box.xmax() << " " << box.ymax() << " " << box.zmin() << " "
<< box.xmax() << " " << box.ymax() << " " << box.zmax() << std::endl;
}
friend std::ostream& operator<< (std::ostream& os, const Self& orthtree)
{
// Create a range of nodes
auto nodes = orthtree.traverse(Orthtrees::Preorder_traversal());
// Iterate over the range
for (auto &n : nodes) {
// Show the depth
for (int i = 0; i < n.depth(); ++i)
os << ". ";
// Print the node
os << n << std::endl;
}
return os;
}
/// \endcond
}; // end class Orthtree
} // namespace CGAL
#include <CGAL/Orthtree/Node.h>
#endif // CGAL_ORTHTREE_H

View File

@ -0,0 +1,65 @@
// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot
#ifndef CGAL_ORTHTREE_CARTESIAN_RANGE_H
#define CGAL_ORTHTREE_CARTESIAN_RANGE_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Iterator_range.h>
#include <boost/iterator/zip_iterator.hpp>
namespace CGAL
{
namespace Orthtrees
{
namespace internal
{
template <typename Traits>
struct Cartesian_ranges
{
typedef typename Traits::Point_d Point;
typedef typename Traits::Cartesian_const_iterator_d Cartesian_const_iterator;
using Range_single = CGAL::Iterator_range<Cartesian_const_iterator>;
Range_single operator() (const Point& p) const
{
return CGAL::make_range (p.cartesian_begin(), p.cartesian_end());
}
using Range_pair
= CGAL::Iterator_range
<boost::zip_iterator
<boost::tuple<Cartesian_const_iterator, Cartesian_const_iterator> > >;
Range_pair operator() (const Point& a, const Point& b) const
{
return CGAL::make_range
(boost::make_zip_iterator
(boost::make_tuple (a.cartesian_begin(), b.cartesian_begin())),
boost::make_zip_iterator
(boost::make_tuple (a.cartesian_end(), b.cartesian_end())));
}
};
} // namespace internal
} // namespace Orthtrees
} // namespace CGAL
#endif // CGAL_ORTHTREE_CARTESIAN_RANGE_H

View File

@ -0,0 +1,75 @@
// Copyright (c) 2007-2020 INRIA (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao
#ifndef CGAL_ORTHTREE_IO_H
#define CGAL_ORTHTREE_IO_H
#include <CGAL/license/Orthtree.h>
#include <iostream>
#include <ostream>
namespace CGAL
{
namespace internal
{
template<typename Node>
std::ostream& print_orthtree_node(std::ostream& os, const Node& node)
{
// Show the depth of the node
// for (int i = 0; i < node.depth(); ++i)
// os << ". ";
// Wrap information in brackets
os << "{ ";
// Index identifies which child this is
os << "(";
for (std::size_t i = 0; i < node.local_coordinates().size(); ++ i)
os << node.local_coordinates()[i];
os << ") ";
// Location
os << "( ";
for (const auto& b : node.global_coordinates())
os << b << " ";
os << ") ";
// Depth
os << "("
<< +node.depth() // The + forces printing as an int instead of a char
<< ") ";
os << "("
<< node.size()
<< ") ";
// // If a node has points, indicate how many
// if (!node.is_empty())
// os << "[" << node.num_points() << " points] ";
// If a node is a leaf, mark it
os << (node.is_leaf() ? "[leaf] " : "");
// If a node is root, mark it
os << (node.is_root() ? "[root] " : "");
// Wrap information in brackets
os << "}";
return os;
}
} // internal
} // CGAL
#endif //CGAL_ORTHTREE_IO_H

View File

@ -0,0 +1,599 @@
// Copyright (c) 2007-2020 INRIA (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao
#ifndef CGAL_ORTHTREE_NODE_H
#define CGAL_ORTHTREE_NODE_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Orthtree.h>
#include <boost/range/iterator_range.hpp>
#include <array>
#include <memory>
#include <bitset>
#include <cassert>
#include <iostream>
namespace CGAL {
/// \cond SKIP_IN_MANUAL
namespace Orthtrees
{
// Non-documented, or testing purpose only
struct Node_access
{
template <typename Node, typename LC>
static Node create_node (Node parent, LC local_coordinates)
{
return Node(parent, local_coordinates);
}
template <typename Node>
static typename Node::Point_range& points(Node node) { return node.points(); }
template <typename Node>
static void split(Node node) { return node.split(); }
};
} // namespace Orthtrees
/// \endcond
/*!
\brief represents a single node of the tree. Alternatively referred
to as a cell, orthant, or sub-tree.
A `Node` is a lightweight object and thus generally passed by
copy. It is also a model of `ConstRange` with value type `Traits::Point_d`.
\cgalModels `ConstRange`
*/
template<typename Traits, typename PointRange, typename PointMap>
class Orthtree<Traits, PointRange, PointMap>::Node
{
public:
/// \name Types
/// @{
typedef Orthtree<Traits, PointRange, PointMap> Enclosing; ///< Orthtree type (enclosing class).
typedef typename Enclosing::Dimension Dimension; ///< Dimension type.
typedef typename Enclosing::Degree Degree; ///< Degree type.
/*!
\brief Self typedef for convenience.
*/
typedef typename Orthtree<Traits, PointRange, PointMap>::Node Self;
/// \cond SKIP_IN_MANUAL
/*!
* \brief Array for containing the child nodes of this node.
*/
typedef std::array<Self, Degree::value> Children;
/// \endcond
/*!
\brief Set of bits representing this node's relationship to its parent.
Equivalent to an array of Booleans, where index[0] is whether `x`
is greater, index[1] is whether `y` is greater, index[2] is whether
`z` is greater, and so on for higher dimensions if needed.
Used to represent a node's relationship to the center of its parent.
*/
typedef std::bitset<Dimension::value> Local_coordinates;
/*!
\brief Coordinates representing this node's relationship
with the rest of the tree.
Each value `(x, y, z, ...)` of global coordinates is calculated by doubling
the parent's global coordinates and adding the local coordinates.
*/
typedef std::array<std::uint32_t, Dimension::value> Global_coordinates;
typedef typename PointRange::const_iterator const_iterator; ///< constant iterator type.
/*!
\brief Adjacency directions.
*/
typedef typename Traits::Adjacency Adjacency;
/// @}
private:
/// \cond SKIP_IN_MANUAL
/*!
\brief point range type.
*/
typedef typename PointRange::iterator iterator; ///< constant iterator type.
typedef boost::iterator_range<iterator> Point_range;
/// \endcond
// make Node trivially copiabled
struct Data
{
Point_range points;
Self parent;
std::uint8_t depth;
Global_coordinates global_coordinates;
std::unique_ptr<Children> children;
Data (Self parent)
: parent (parent), depth (0) { }
};
Data* m_data;
/// \cond SKIP_IN_MANUAL
// Only the Orthtree class has access to the non-default
// constructor, mutators, etc.
friend Enclosing;
// Hidden class to access methods for testing purposes
friend Orthtrees::Node_access;
/*!
* \brief Access to the content held by this node
* \return a reference to the collection of point indices
*/
Point_range &points() { return m_data->points; }
const Point_range &points() const { return m_data->points; }
/// \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 `parent.is_null()` returns
`true`, and the depth is zero. If a parent is provided, the node
becomes the child of that parent. In that case, an index should
be passed, telling this node its relationship to its parent.
Depth and global coordinates are automatically determined in the
constructor, and should generally be considered immutable after
construction.
\param parent the node containing this one
\param index this node's relationship to its parent
*/
explicit Node(Self parent, Local_coordinates local_coordinates)
: m_data (new Data(parent)) {
if (!parent.is_null()) {
m_data->depth = parent.m_data->depth + 1;
for (int i = 0; i < Dimension::value; i++)
m_data->global_coordinates[i] = (2 * parent.m_data->global_coordinates[i]) + local_coordinates[i];
}
else
for (int i = 0; i < Dimension::value; i++)
m_data->global_coordinates[i] = 0;
}
void free() { delete m_data; }
Node deep_copy(Self parent = Node()) const
{
if (is_null())
return Node();
Node out;
out.m_data = new Data(parent);
out.m_data->points = m_data->points;
out.m_data->depth = m_data->depth;
out.m_data->global_coordinates = m_data->global_coordinates;
std::unique_ptr<Children> children;
if (!is_leaf())
{
out.m_data->children = std::make_unique<Children>();
for (int index = 0; index < Degree::value; index++)
(*out.m_data->children)[index] = (*this)[index].deep_copy(out);
}
return out;
}
/// @}
/// \name Mutators
/// @{
/*!
\brief splits a node into subnodes.
Only leaf nodes should be split.
When a node is split it is no longer a leaf node.
A number of `Degree::value` children are constructed automatically, and their values are set.
Contents of this node are _not_ propagated automatically.
It is the responsibility of the caller to redistribute the points contained by a node after splitting
*/
void split() {
CGAL_precondition (is_leaf());
m_data->children = std::make_unique<Children>();
for (int index = 0; index < Degree::value; index++) {
(*m_data->children)[index] = std::move(Self(*this, {Local_coordinates(index)}));
}
}
/*!
* \brief eliminates 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_data->children.reset();
}
/// @}
/// \endcond
public:
/// \cond SKIP_IN_MANUAL
// Default creates null node
Node() : m_data(nullptr) { }
// Comparison operator
bool operator< (const Node& other) const { return m_data < other.m_data; }
/// \endcond
/// \name Type & Location
/// @{
/*!
\brief returns `true` if the node is null, `false` otherwise.
*/
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.
\pre `!is_null()`
*/
bool is_leaf() const
{
CGAL_precondition(!is_null());
return (!m_data->children);
}
/*!
\brief returns this node's depth.
\pre `!is_null()`
*/
std::uint8_t depth() const
{
CGAL_precondition (!is_null());
return m_data->depth;
}
/*!
\brief returns this node's local coordinates (in relation to its parent).
\pre `!is_null()`
*/
Local_coordinates local_coordinates() const {
CGAL_precondition (!is_null());
// TODO: There must be a better way of doing this!
Local_coordinates result;
for (std::size_t i = 0; i < Dimension::value; ++ i)
result[i] = global_coordinates()[i] & 1;
return result;
}
/*!
\brief returns this node's global coordinates.
\pre `!is_null()`
*/
Global_coordinates global_coordinates() const
{
CGAL_precondition (!is_null());
return m_data->global_coordinates;
}
/// \name Adjacency
/// @{
/*!
\brief returns this node's parent.
\pre `!is_null()`
*/
Self parent() const
{
CGAL_precondition (!is_null());
return m_data->parent;
}
/*!
\brief returns the nth child fo this node.
\pre `!is_null()`
\pre `!is_leaf()`
\pre `0 <= index && index < Degree::value`
The orthtree subdivides the space in 2 on each dimension
available, so a child node can be accessed by selecting a Boolean
value for each dimension. The `index` parameter is thus
interpreted as a bitmap, where each bit matches a dimension
(starting by the least significant bit for coordinate X).
For example, in the case of an octree (dimension 3):
- index 0 (000 in binary) is the children on the "minimum corner" (xmin, ymin, zmin)
- index 1 (001 in binary) is on (xmax, ymin, zmin)
- index 2 (010 in binary) is on (xmin, ymax, zmin)
- index 6 (101 in binary) is on (xmax, ymin, zmax)
Diagram of a quadtree:
```
Children of the current node:
Y axis
A
| +-------------------+-------------------+
| | Coord: Ymax Xmin | Coord: Ymax Xmax |
| | Bitmap: 1 0 | Bitmap: 1 1 |
| | | |
| | -> index = 2 | -> index = 3 |
| | | |
| | | |
| | | |
| | | |
| +-------------------+-------------------+
| | Coord: Ymin Xmin | Coord: Ymin Xmax |
| | Bitmap: 0 0 | Bitmap: 0 1 |
| | | |
| | -> index = 0 | -> index = 1 |
| | | |
| | | |
| | | |
| | | |
| +-------------------+-------------------+
|
+--------------------------------------------> X axis
0
```
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`.
*/
Self operator[](std::size_t index) const {
CGAL_precondition (!is_null());
CGAL_precondition (!is_leaf());
CGAL_precondition (index < Degree::value);
return (*m_data->children)[index];
}
/*!
\brief finds the directly adjacent node in a specific direction
\pre `!is_null()`
\pre `direction.to_ulong < 2 * Dimension::value`
Adjacent nodes are found according to several properties:
- adjacent nodes may be larger than the seek node, but never smaller
- a node has at most `2 * Dimension::value` different adjacent nodes (in 3D: left, right, up, down, front, back)
- adjacent nodes are not required to be leaf nodes
Here's a diagram demonstrating the concept for a Quadtree:
```
+---------------+---------------+
| | |
| | |
| | |
| A | |
| | |
| | |
| | |
+-------+-------+---+---+-------+
| | | | | |
| A | (S) +---A---+ |
| | | | | |
+---+---+-------+---+---+-------+
| | | | | |
+---+---+ A | | |
| | | | | |
+---+---+-------+-------+-------+
```
- (S) : Seek node
- A : Adjacent node
Note how the top adjacent node is larger than the seek node. The
right adjacent node is the same size, even though it contains
further subdivisions.
This implementation returns the adjacent node if it's found. If
there is no adjacent node in that direction, it returns a null
node.
\param direction which way to find the adjacent node relative to
this one. Each successive bit selects the direction for the
corresponding dimension: for an Octree in 3D, 010 means: negative
direction in X, position direction in Y, negative direction in Z.
\return the adjacent node if it exists, a null node otherwise.
*/
Self adjacent_node (Local_coordinates direction) const
{
CGAL_precondition(!is_null());
// Direction: LEFT RIGHT DOWN UP BACK FRONT
// direction: 000 001 010 011 100 101
// Nodes only have up to 2*dim different adjacent nodes (since cubes have 6 sides)
CGAL_precondition(direction.to_ulong() < Dimension::value * 2);
// The root node has no adjacent nodes!
if (is_root())
return Self();
// The least significant bit indicates the sign (which side of the node)
bool sign = direction[0];
// The first two bits indicate the dimension/axis (x, y, z)
uint8_t dimension = uint8_t((direction >> 1).to_ulong());
// Create an offset so that the bit-significance lines up with the dimension (e.g. 1, 2, 4 --> 001, 010, 100)
int8_t offset = (uint8_t) 1 << dimension;
// Finally, apply the sign to the offset
offset = (sign ? offset : -offset);
// Check if this child has the opposite sign along the direction's axis
if (local_coordinates()[dimension] != sign) {
// This means the adjacent node is a direct sibling, the offset can be applied easily!
return parent()[local_coordinates().to_ulong() + offset];
}
// Find the parent's neighbor in that direction if it exists
Self adjacent_node_of_parent = parent().adjacent_node(direction);
// If the parent has no neighbor, then this node doesn't have one
if (adjacent_node_of_parent.is_null())
return 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())
return adjacent_node_of_parent;
// Return the nearest node of the parent by subtracting the offset instead of adding
return adjacent_node_of_parent[local_coordinates().to_ulong() - offset];
}
/*!
\brief equivalent to `adjacent_node()`, with an adjacency direction
rather than a bitset.
*/
Self adjacent_node(Adjacency adjacency) const {
return adjacent_node(std::bitset<Dimension::value>(static_cast<int>(adjacency)));
}
/// @}
/// \name Point Range
/// @{
/*!
\brief checks whether the node is empty of points or not.
*/
bool empty() const {
return m_data->points.empty();
}
/*!
\brief returns the number of points of this node.
*/
std::size_t size() const {
return std::size_t(std::distance(m_data->points.begin(), m_data->points.end()));
}
/*!
\brief returns the iterator at the start of the collection of
points held by this node.
*/
const_iterator begin() const { return m_data->points.begin(); }
/*!
\brief returns the iterator at the end of the collection of
points held by this node.
*/
const_iterator end() const { return m_data->points.end(); }
/// @}
/// \cond SKIP_IN_MANUAL
/// \name Operators
/// @{
/*!
* \brief compares the topology of this node to another node.
*
* \todo
*
* \param rhs node to compare with
* \return whether the nodes have different topology.
*/
bool operator==(const Self &rhs) const {
return m_data == rhs.m_data;
}
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 (a.is_leaf() != b.is_leaf())
return false;
// If both nodes are non-leaf nodes
if (!a.is_leaf()) {
// Check all the children
for (int i = 0; i < Degree::value; ++i) {
// If any child cell is different, they're not the same
if (!is_topology_equal(a[i], b[i]))
return false;
}
}
// If both nodes are leaf nodes, they must be in the same location
return (a.global_coordinates() == b.global_coordinates());
}
friend std::ostream& operator<< (std::ostream& os, const Self& node)
{
return internal::print_orthtree_node(os, node);
}
/// \endcond
};
}
#endif //CGAL_ORTHTREE_NODE_H

View File

@ -0,0 +1,116 @@
// Copyright (c) 2007-2020 INRIA (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao
#ifndef CGAL_ORTHTREE_SPLIT_PREDICATES_H
#define CGAL_ORTHTREE_SPLIT_PREDICATES_H
#include <CGAL/license/Orthtree.h>
#include <iostream>
namespace CGAL {
namespace Orthtrees {
/*!
\ingroup PkgOrthtreeSplitPredicates
\brief A class used to choose when a node should be split depending on the number of inliers.
This is a bucket size predicate that considers a node should be
split if it contains more than a certain number of items.
*/
class Maximum_number_of_inliers {
std::size_t m_bucket_size;
public:
/*!
\brief creates a predicate based on the number of inliers (bucket size).
*/
Maximum_number_of_inliers(std::size_t bucket_size) :
m_bucket_size(bucket_size) {}
/*!
\brief returns `true` if `n` should be split, `false` otherwise.
*/
template<typename Node>
bool operator()(const Node &n) const {
return (n.size() > m_bucket_size);
}
};
/*!
\ingroup PkgOrthtreeSplitPredicates
\brief A class used to choose when a node should be split depending on the depth.
This predicate makes a node be split if its depth is lower than a certain limit.
*/
class Maximum_depth {
std::size_t m_max_depth;
public:
/*!
\brief creates a maximum depth predicate.
*/
Maximum_depth(std::size_t max_depth) : m_max_depth(max_depth) {}
/*!
\brief returns `true` if `n` should be split, `false` otherwise.
*/
template<typename Node>
bool operator()(const Node &n) const {
return n.depth() < m_max_depth;
}
};
/*!
\ingroup PkgOrthtreeSplitPredicates
\brief A class used to choose when a node should be split depending on the depth and the number of inliers.
This predicate makes a note split if it contains more than a
certain number of items and if its depth is lower than a certain
limit.
The refinement is stopped as soon as one of the conditions is
violated: if a node has more inliers than `bucket_size` but is
already at `max_depth`, it is not split. Similarly, a node that is
at a depth smaller than `max_depth` but already has fewer inliers
than `bucket_size`, it is not split.
*/
class Maximum_depth_and_maximum_number_of_inliers {
std::size_t m_max_depth, m_bucket_size;
public:
/*! \brief creates a predicate using maximum depth or bucket size.
*/
Maximum_depth_and_maximum_number_of_inliers(std::size_t max_depth, std::size_t bucket_size) :
m_max_depth(max_depth), m_bucket_size(bucket_size) {}
/*!
\brief returns `true` if `n` should be split, `false` otherwise.
*/
template<typename Node>
bool operator()(const Node &n) const {
std::size_t num_points = n.size();
std::size_t depth = n.depth();
return (num_points > m_bucket_size && depth < m_max_depth);
}
};
} // Orthtrees
} // CGAL
#endif //CGAL_ORTHTREE_SPLIT_PREDICATES_H

View File

@ -0,0 +1,99 @@
// Copyright (c) 2007-2020 INRIA (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao
#ifndef CGAL_ORTHTREE_TRAVERSAL_ITERATOR_H
#define CGAL_ORTHTREE_TRAVERSAL_ITERATOR_H
#include <CGAL/license/Orthtree.h>
#include <boost/function.hpp>
#include <boost/iterator/iterator_facade.hpp>
/// \cond SKIP_IN_MANUAL
namespace CGAL {
/*!
* \ingroup PkgOrthtreeClasses
*
* \brief
*
* \todo
*
* \tparam Value
*/
template<class Value>
class Traversal_iterator :
public boost::iterator_facade<Traversal_iterator<Value>, Value, boost::forward_traversal_tag> {
public:
/// \name Types
/// @{
/*!
* \brief
*
* \todo
*/
typedef std::function<Value(Value)> Traversal_function;
/// @}
public:
/// \name Creation
/// @{
/*!
* \brief
*
* \todo
*/
Traversal_iterator() : m_value(), m_next() {}
/*!
* \brief
*
* \todo
*
* \param first
* \param next
*/
Traversal_iterator(Value first, const Traversal_function &next) : m_value(first), m_next(next) {}
/// @}
private:
friend class boost::iterator_core_access;
bool equal(Traversal_iterator<Value> const &other) const {
return m_value == other.m_value;
}
void increment() {
m_value = m_next(m_value);
}
Value &dereference() const {
return const_cast<Value&>(m_value);
}
private:
Value m_value;
Traversal_function m_next;
};
}
/// \endcond
#endif //CGAL_ORTHTREE_TRAVERSAL_ITERATOR_H

View File

@ -0,0 +1,261 @@
// Copyright (c) 2007-2020 INRIA (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Jackson Campolattaro, Cédric Portaneri, Tong Zhao
#ifndef CGAL_ORTHTREE_TRAVERSALS_H
#define CGAL_ORTHTREE_TRAVERSALS_H
#include <CGAL/license/Orthtree.h>
#include <iostream>
#include <boost/range/iterator_range.hpp>
#include <CGAL/Orthtree/Traversal_iterator.h>
#include <stack>
namespace CGAL {
/// \cond SKIP_IN_MANUAL
// Forward declaration
template<typename T, typename PR, typename PM>
class Orthtree;
/// \endcond
namespace Orthtrees {
/// \cond SKIP_IN_MANUAL
template <typename Node>
Node next_sibling(Node n) {
// Passing null returns the first node
if (n.is_null())
return Node();
// If this node has no parent, it has no siblings
if (n.parent().is_null())
return Node();
// Find out which child this is
std::size_t index = n.local_coordinates().to_ulong();
constexpr static int degree = Node::Degree::value;
// Return null if this is the last child
if (int(index) == degree - 1)
return Node();
// Otherwise, return the next child
return n.parent()[index + 1];
}
template <typename Node>
Node next_sibling_up(Node n) {
if (n.is_null())
return Node();
Node up = n.parent();
while (!up.is_null()) {
if (!next_sibling(up).is_null())
return next_sibling(up);
up = up.parent();
}
return Node();
}
template <typename Node>
Node deepest_first_child(Node n) {
if (n.is_null())
return Node();
// Find the deepest child on the left
Node first = n;
while (!first.is_leaf())
first = first[0];
return first;
}
template <typename Node>
Node first_child_at_depth(Node n, std::size_t depth) {
if (n.is_null())
return Node();
std::stack<Node> todo;
todo.push(n);
if (n.depth() == depth)
return n;
while (!todo.empty())
{
Node node = todo.top();
todo.pop();
if (node.depth() == depth)
return node;
if (!node.is_leaf())
for (int i = 0; i < Node::Degree::value; ++ i)
todo.push(node[std::size_t(Node::Degree::value - 1 - i)]);
}
return Node();
}
/// \endcond
/*!
\ingroup PkgOrthtreeTraversal
\brief A class used for performing a preorder traversal.
A preorder traversal starts from the root towards the leaves.
\cgalModels `OrthtreeTraversal`
*/
struct Preorder_traversal {
template <typename Node>
Node first(Node root) const {
return root;
}
template <typename Node>
Node next(Node n) const {
if (n.is_leaf()) {
Node next = next_sibling(n);
if (next.is_null())
return next_sibling_up(n);
return next;
}
else // Return the first child of this node
return n[0];
}
};
/*!
\ingroup PkgOrthtreeTraversal
\brief A class used for performing a postorder traversal.
A postorder traversal starts from the leaves towards the root.
\cgalModels `OrthtreeTraversal`
*/
struct Postorder_traversal {
template <typename Node>
Node first(Node root) const {
return deepest_first_child(root);
}
template <typename Node>
Node next(Node n) const {
Node next = deepest_first_child(next_sibling(n));
if (next.is_null())
next = n.parent();
return next;
}
};
/*!
\ingroup PkgOrthtreeTraversal
\brief A class used for performing a traversal on leaves only.
All non-leave nodes are ignored.
\cgalModels `OrthtreeTraversal`
*/
struct Leaves_traversal {
template <typename Node>
Node first(Node root) const {
return deepest_first_child(root);
}
template <typename Node>
Node next(Node n) const {
Node next = deepest_first_child(next_sibling(n));
if (next.is_null())
next = deepest_first_child(next_sibling_up(n));
return next;
}
};
/*!
\ingroup PkgOrthtreeTraversal
\brief A class used for performing a traversal of a specific depth level.
All trees at another depth are ignored. If the selected depth is
higher than the maximum depth of the orthtree, no node will be traversed.
\cgalModels `OrthtreeTraversal`
*/
struct Level_traversal {
private:
const std::size_t depth;
public:
/*!
constructs a `depth`-level traversal.
*/
Level_traversal (std::size_t depth) : depth(depth) { }
template <typename Node>
Node first(Node root) const {
return first_child_at_depth(root, depth);
}
template <typename Node>
Node next(Node n) const {
std::cerr << depth << " ";
Node next = next_sibling(n);
if (next.is_null())
{
Node up = n;
do
{
up = next_sibling_up(up);
if (up.is_null())
return Node();
next = first_child_at_depth(up, depth);
}
while (next.is_null());
}
return next;
}
};
} // Orthtree
} // CGAL
#endif //CGAL_ORTHTREE_TRAVERSALS_H

View File

@ -0,0 +1,143 @@
// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot
#ifndef CGAL_ORTHTREE_TRAITS_2_H
#define CGAL_ORTHTREE_TRAITS_2_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Dimension.h>
#include <CGAL/Bbox_2.h>
namespace CGAL
{
/*!
\ingroup PkgOrthtreeTraits
The class `Orthtree_traits_2` can be used as a template parameter of
the `Orthtree` class.
\tparam GeomTraits model of `Kernel`.
\cgalModels `OrthtreeTraits`
\sa `CGAL::Quadtree`
\sa `CGAL::Orthtree_traits_3`
\sa `CGAL::Orthtree_traits_d`
*/
template <typename GeomTraits>
struct Orthtree_traits_2
{
public:
/// \name Types
/// @{
typedef Dimension_tag<2> Dimension; ///< Dimension type.
typedef Bbox_2 Bbox_d; ///< Bounding box type.
typedef typename GeomTraits::FT FT; ///< Number type.
typedef typename GeomTraits::Point_2 Point_d; ///< Point type.
typedef typename GeomTraits::Circle_2 Sphere_d; ///< Sphere type.
typedef typename GeomTraits::Cartesian_const_iterator_2 Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates.
typedef std::array<FT, Dimension::value> Array; ///< Array type.
/*!
* \brief Two directions along each axis in Cartesian space, relative to a node.
*
* Directions are mapped to numbers as 2-bit integers.
*
* The first bit indicates the axis (0 = x, 1 = y),
* the second bit indicates the direction along that axis (0 = -, 1 = +).
*
* The following diagram may be a useful reference:
*
* 3 *
* |
* | y+
* | *
* 0 *------+------* 1 |
* | |
* | +-----* x+
* |
* * 2
*
* This lookup table may also be helpful:
*
* | Direction | bitset | number | Enum |
* | --------- | ------ | ------ | ----- |
* | `-x` | 00 | 0 | LEFT |
* | `+x` | 01 | 1 | RIGHT |
* | `-y` | 10 | 2 | DOWN |
* | `+y` | 11 | 3 | UP |
*/
enum Adjacency
{
LEFT,
RIGHT,
DOWN,
UP
};
#ifdef DOXYGEN_RUNNING
/*!
Functor with an operator to construct a `Point_d` from an `Array` object.
*/
typedef unspecified_type Construct_point_d_from_array;
#else
struct Construct_point_d_from_array
{
Point_d operator() (const Array& array) const
{
return Point_d (array[0], array[1]);
}
};
#endif
#ifdef DOXYGEN_RUNNING
/*!
Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points).
*/
typedef unspecified_type Construct_bbox_d;
#else
struct Construct_bbox_d
{
Bbox_d operator() (const Array& min,
const Array& max) const
{
return Bbox_d (min[0], min[1], max[0], max[1]);
}
};
#endif
/// @}
/// \name Operations
/// @{
/*!
Function used to construct an object of type `Construct_point_d_from_array`.
*/
Construct_point_d_from_array construct_point_d_from_array_object() const
{ return Construct_point_d_from_array(); }
/*!
Function used to construct an object of type `Construct_bbox_d`.
*/
Construct_bbox_d construct_bbox_d_object() const
{ return Construct_bbox_d(); }
/// @}
};
}
#endif // CGAL_ORTHTREE_TRAITS_2_H

View File

@ -0,0 +1,160 @@
// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot
#ifndef CGAL_ORTHTREE_TRAITS_3_H
#define CGAL_ORTHTREE_TRAITS_3_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Dimension.h>
#include <CGAL/Bbox_3.h>
namespace CGAL
{
/*!
\ingroup PkgOrthtreeTraits
The class `Orthtree_traits_3` can be used as a template parameter of
the `Orthtree` class.
\tparam GeomTraits model of `Kernel`.
\cgalModels `OrthtreeTraits`
\sa `CGAL::Octree`
\sa `CGAL::Orthtree_traits_2`
\sa `CGAL::Orthtree_traits_d`
*/
template <typename GeomTraits>
struct Orthtree_traits_3
{
public:
/// \name Types
/// @{
typedef Dimension_tag<3> Dimension; ///< Dimension type.
typedef Bbox_3 Bbox_d; ///< Bounding box type.
typedef typename GeomTraits::FT FT; ///< Number type.
typedef typename GeomTraits::Point_3 Point_d; ///< Point type.
typedef typename GeomTraits::Sphere_3 Sphere_d; ///< Sphere type.
typedef typename GeomTraits::Cartesian_const_iterator_3 Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates.
typedef std::array<FT, Dimension::value> Array; ///< Array type.
/*!
* \brief Two directions along each axis in Cartesian space, relative to a node.
*
* Directions are mapped to numbers as 3-bit integers,
* though the numbers 6 and 7 are not used because there are only 6 different directions.
*
* The first two bits indicate the axis (00 = x, 01 = y, 10 = z),
* the third bit indicates the direction along that axis (0 = -, 1 = +).
*
* The following diagram may be a useful reference:
*
* 3 *
* | * 5
* | / y+
* |/ * z+
* 0 *------+------* 1 | *
* /| |/
* / | +-----* x+
* 4 * |
* * 2
*
* This lookup table may also be helpful:
*
* | Direction | bitset | number | Enum |
* | --------- | ------ | ------ | ----- |
* | `-x` | 000 | 0 | LEFT |
* | `+x` | 001 | 1 | RIGHT |
* | `-y` | 010 | 2 | DOWN |
* | `+y` | 011 | 3 | UP |
* | `-z` | 100 | 4 | BACK |
* | `+z` | 101 | 5 | FRONT |
*/
enum Adjacency
{
LEFT,
RIGHT,
DOWN,
UP,
BACK,
FRONT
};
/// \cond SKIP_IN_MANUAL
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
};
/// \endcond
#ifdef DOXYGEN_RUNNING
/*!
Functor with an operator to construct a `Point_d` from an `Array` object.
*/
typedef unspecified_type Construct_point_d_from_array;
#else
struct Construct_point_d_from_array
{
Point_d operator() (const Array& array) const
{
return Point_d (array[0], array[1], array[2]);
}
};
#endif
#ifdef DOXYGEN_RUNNING
/*!
Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points).
*/
typedef unspecified_type Construct_bbox_d;
#else
struct Construct_bbox_d
{
Bbox_d operator() (const Array& min,
const Array& max) const
{
return Bbox_d (min[0], min[1], min[2], max[0], max[1], max[2]);
}
};
#endif
/// @}
/// \name Operations
/// @{
/*!
Function used to construct an object of type `Construct_point_d_from_array`.
*/
Construct_point_d_from_array construct_point_d_from_array_object() const
{ return Construct_point_d_from_array(); }
/*!
Function used to construct an object of type `Construct_bbox_d`.
*/
Construct_bbox_d construct_bbox_d_object() const
{ return Construct_bbox_d(); }
/// @}
};
}
#endif // CGAL_ORTHTREE_TRAITS_3_H

View File

@ -0,0 +1,137 @@
// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot
#ifndef CGAL_ORTHTREE_TRAITS_D_H
#define CGAL_ORTHTREE_TRAITS_D_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Dimension.h>
namespace CGAL
{
/*!
\ingroup PkgOrthtreeTraits
The class `Orthtree_traits_d` can be used as a template parameter of
the `Orthtree` class.
\tparam GeomTraits model of `Kernel`.
\tparam DimensionTag specialization of `CGAL::Dimension_tag`.
\cgalModels `OrthtreeTraits`
\sa `CGAL::Orthtree`
\sa `CGAL::Orthtree_traits_2`
\sa `CGAL::Orthtree_traits_3`
*/
template <typename GeomTraits, typename DimensionTag>
struct Orthtree_traits_d
{
public:
/// \name Types
/// @{
typedef DimensionTag Dimension; ///< Dimension type.
typedef typename GeomTraits::FT FT; ///< Number type.
typedef typename GeomTraits::Point_d Point_d; ///< Point type.
typedef typename GeomTraits::Sphere_d Sphere_d; ///< Sphere type.
typedef typename GeomTraits::Cartesian_const_iterator_d Cartesian_const_iterator_d; ///< An iterator over the %Cartesian coordinates.
typedef std::array<FT, Dimension::value> Array; ///< Array type.
#ifdef DOXYGEN_RUNNING
typedef unspecified_type Bbox_d; ///< Bounding box type.
#else
class Bbox_d
{
Point_d m_min, m_max;
public:
Bbox_d (const Point_d& pmin, const Point_d& pmax)
: m_min (pmin), m_max (pmax)
{ }
const Point_d& min BOOST_PREVENT_MACRO_SUBSTITUTION () { return m_min; }
const Point_d& max BOOST_PREVENT_MACRO_SUBSTITUTION () { return m_max; }
};
#endif
/*!
Adjacency type.
\note This type is used to identify adjacency directions with
easily understandable keywords (left, right, up, etc.) and is thus
mainly useful for `Orthtree_traits_2` and `Orthtree_traits_3`. In
higher dimensions, such keywords do not exist and this type is
simply an integer. Conversions from this integer to bitsets still
works but do not provide any easier API for adjacency selection.
*/
typedef int Adjacency;
#ifdef DOXYGEN_RUNNING
/*!
Functor with an operator to construct a `Point_d` from an `Array` object.
*/
typedef unspecified_type Construct_point_d_from_array;
#else
struct Construct_point_d_from_array
{
Point_d operator() (const Array& array) const
{
return Point_d (array.begin(), array.end());
}
};
#endif
#ifdef DOXYGEN_RUNNING
/*!
Functor with an operator to construct a `Bbox_d` from two `Array` objects (coordinates of minimum and maximum points).
*/
typedef unspecified_type Construct_bbox_d;
#else
struct Construct_bbox_d
{
Bbox_d operator() (const Array& min,
const Array& max) const
{
return Bbox_d (Point_d (min.begin(), min.end()),
Point_d (max.begin(), max.end()));
}
};
#endif
/// @}
/// \name Operations
/// @{
/*!
Function used to construct an object of type `Construct_point_d_from_array`.
*/
Construct_point_d_from_array construct_point_d_from_array_object() const
{ return Construct_point_d_from_array(); }
/*!
Function used to construct an object of type `Construct_bbox_d`.
*/
Construct_bbox_d construct_bbox_d_object() const
{ return Construct_bbox_d(); }
/// @}
};
}
#endif // CGAL_ORTHTREE_TRAITS_D_H

View File

@ -0,0 +1,50 @@
// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot
#ifndef CGAL_QUADTREE_H
#define CGAL_QUADTREE_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Orthtree.h>
#include <CGAL/Orthtree_traits_2.h>
namespace CGAL {
/*!
\ingroup PkgOrthtreeClasses
\brief Alias that specializes the `Orthtree` class to a 2D quadtree.
These two types are exactly equivalent:
- `Quadtree<GeomTraits, PointRange, PointMap>`
- `Orthtree<Orthtree_traits_2<GeomTraits>, PointRange, PointMap>`.
\warning This is a not a real class but an alias, please refer to
the documentation of `Orthtree`.
\tparam GeomTraits must be a model of `Kernel`
\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_2`
*/
template <typename GeomTraits, typename PointRange,
typename PointMap = Identity_property_map
<typename std::iterator_traits<typename PointRange::iterator>::value_type> >
#ifdef DOXYGEN_RUNNING
class Quadtree;
#else
using Quadtree = Orthtree<Orthtree_traits_2<GeomTraits>, PointRange, PointMap>;
#endif
} // namespace CGAL
#endif // CGAL_OCTREE_H

View File

@ -0,0 +1 @@
INRIA Sophia-Antipolis (France)

View File

@ -0,0 +1,15 @@
Algebraic_foundations
Distance_2
Distance_3
Installation
Intersections_2
Intersections_3
Interval_support
Kernel_23
Modular_arithmetic
Number_types
Orthtree
Profiling_tools
Property_map
STL_Extension
Stream_support

View File

@ -0,0 +1 @@
GPL (v3 or later)

View File

@ -0,0 +1,2 @@
Pierre Alliez <Pierre.Alliez@sophia.inria.fr>
Simon Giraudot <Simon.Giraudot@geometryfactory.com>

View File

@ -0,0 +1,21 @@
# Created by the script cgal_create_cmake_script
# This is the CMake script for compiling a CGAL application.
cmake_minimum_required(VERSION 3.1...3.14)
project(Orthtree_Tests)
find_package(CGAL REQUIRED QUIET OPTIONAL_COMPONENTS Core)
create_single_source_cgal_program("test_octree_equality.cpp")
create_single_source_cgal_program("test_octree_refine.cpp")
create_single_source_cgal_program("test_octree_grade.cpp")
create_single_source_cgal_program("test_octree_locate.cpp")
create_single_source_cgal_program("test_octree_bbox.cpp")
create_single_source_cgal_program("test_octree_nearest_neighbor.cpp")
create_single_source_cgal_program("test_octree_traverse.cpp")
create_single_source_cgal_program("test_octree_intersecting.cpp")
create_single_source_cgal_program("test_octree_copy_move_constructors.cpp")
create_single_source_cgal_program("test_octree_kernels.cpp")
create_single_source_cgal_program("test_node_index.cpp")
create_single_source_cgal_program("test_node_adjacent.cpp")

View File

@ -0,0 +1,55 @@
#include <CGAL/Orthtree/Node.h>
#include <CGAL/Octree/IO.h>
#include <iostream>
#include <cassert>
typedef CGAL::Orthtree::Node<std::vector<int>::iterator> Node;
int main(void) {
// Build a new node
Node n = Node();
// Check that its values are correct
assert(n.is_root());
assert(n.is_leaf());
assert(!n.parent());
assert(n.depth() == 0);
assert(n.location()[0] == 0 && n.location()[1] == 0 && n.location()[2] == 0);
// Split the node
n.split();
// Check that it's children's values are also correct
for (std::size_t i = 0; i < 8; ++i) {
assert(!n[i].is_root());
assert(n[i].is_leaf());
assert(*n[i].parent() == n);
assert(n[i].depth() == 1);
}
// Check that the parent has updated
assert(n.is_root());
assert(!n.is_leaf());
// Split one of the children
n[1].split();
// Check each of that child's children
for (std::size_t i = 0; i < 8; ++i) {
assert(!n[1][i].is_root());
assert(n[1][i].is_leaf());
assert(*n[1][i].parent() == n[1]);
assert(*n[1][i].parent()->parent() == n);
assert(n[1][i].depth() == 2);
}
// Check that the child's values have updated
assert(!n[1].is_root());
assert(!n[1].is_leaf());
return 0;
}

View File

@ -0,0 +1,74 @@
#define CGAL_TRACE_STREAM std::cerr
#include <CGAL/Octree.h>
#include <CGAL/Orthtree/Traversals.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree;
typedef Octree::Node Node;
typedef Octree::Traits Traits;
int main(void) {
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
points.insert({-1, 1, -1});
points.insert({1, 1, -1});
points.insert({-1, -1, 1});
points.insert({1, -1, 1});
points.insert({-1, 1, 1});
points.insert({1, 1, 1});
points.insert({-1, -1, -1.1});
points.insert({-1, -1, -1.2});
points.insert({-1, -1, -1.3});
points.insert({-1, -1, -1.4});
points.insert({-1, -1, -1.5});
points.insert({-1, -1, -1.6});
points.insert({-1, -1, -1.7});
points.insert({-1, -1, -1.8});
points.insert({-1, -1, -1.9});
Octree octree(points, points.point_map());
octree.refine(10, 1);
std::cout << octree << std::endl;
// Root node should have no siblings
assert(octree.root().adjacent_node(0).is_null());
assert(octree.root().adjacent_node(1).is_null());
assert(octree.root().adjacent_node(2).is_null());
assert(octree.root().adjacent_node(3).is_null());
assert(octree.root().adjacent_node(4).is_null());
assert(octree.root().adjacent_node(5).is_null());
// Left Top Front node should have siblings to the Right, Down, and 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::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(left_top_back.adjacent_node(Traits::LEFT).is_null());
assert(left_top_back.adjacent_node(Traits::UP).is_null());
assert(left_top_back.adjacent_node(Traits::BACK).is_null());
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];
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::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::RIGHT).is_null());
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::DOWN).is_null());
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::FRONT).is_null());
return 0;
}

View File

@ -0,0 +1,48 @@
#define CGAL_TRACE_STREAM std::cerr
#include <CGAL/Octree.h>
#include <CGAL/Orthtree/Traversals.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree;
int main(void) {
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
points.insert({-1, 1, -1});
points.insert({1, 1, -1});
points.insert({-1, -1, 1});
points.insert({1, -1, 1});
points.insert({-1, 1, 1});
points.insert({1, 1, 1});
points.insert({-1, -1, -1.1});
points.insert({-1, -1, -1.2});
points.insert({-1, -1, -1.3});
points.insert({-1, -1, -1.4});
points.insert({-1, -1, -1.5});
points.insert({-1, -1, -1.6});
points.insert({-1, -1, -1.7});
points.insert({-1, -1, -1.8});
points.insert({-1, -1, -1.9});
Octree octree(points, points.point_map());
octree.refine(10, 1);
std::cout << "root: " << octree.root().local_coordinates() << std::endl;
std::cout << "first child: " << octree.root()[0].local_coordinates() << std::endl;
std::cout << "fifth child: " << octree.root()[4].local_coordinates() << std::endl;
std::cout << "fifth child of first child: " << octree.root()[0][4].local_coordinates() << std::endl;
// TODO
return 0;
}

View File

@ -0,0 +1,109 @@
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <cassert>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree;
void test_1_node() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Compare the top (only) node
assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1, -1, -1, -1, -1, -1));
}
void test_9_nodes() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, 1, 1});
// Create the octree
Octree octree(points, points.point_map(), 1.1);
octree.refine(10, 1);
// Compare the top node
assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 1.1, 1.1, 1.1));
// Compare the child nodes
assert(octree.bbox(octree.root()[0]) == CGAL::Bbox_3(-1.1, -1.1, -1.1, 0, 0, 0));
assert(octree.bbox(octree.root()[1]) == CGAL::Bbox_3(0, -1.1, -1.1, 1.1, 0, 0));
assert(octree.bbox(octree.root()[2]) == CGAL::Bbox_3(-1.1, 0, -1.1, 0, 1.1, 0));
assert(octree.bbox(octree.root()[3]) == CGAL::Bbox_3(0, 0, -1.1, 1.1, 1.1, 0));
assert(octree.bbox(octree.root()[4]) == CGAL::Bbox_3(-1.1, -1.1, 0, 0, 0, 1.1));
assert(octree.bbox(octree.root()[5]) == CGAL::Bbox_3(0, -1.1, 0, 1.1, 0, 1.1));
assert(octree.bbox(octree.root()[6]) == CGAL::Bbox_3(-1.1, 0, 0, 0, 1.1, 1.1));
assert(octree.bbox(octree.root()[7]) == CGAL::Bbox_3(0, 0, 0, 1.1, 1.1, 1.1));
}
void test_25_nodes() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, 1, 1});
points.insert({-1, -1, -0.5});
points.insert({1, 0.5, 1});
// Create the octree
Octree octree(points, points.point_map(), 1.5);
octree.refine(10, 1);
// Compare the top node
assert(octree.bbox(octree.root()) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 1.5, 1.5, 1.5));
// Compare the child nodes
assert(octree.bbox(octree.root()[0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, 0, 0, 0));
assert(octree.bbox(octree.root()[1]) == CGAL::Bbox_3(0, -1.5, -1.5, 1.5, 0, 0));
assert(octree.bbox(octree.root()[2]) == CGAL::Bbox_3(-1.5, 0, -1.5, 0, 1.5, 0));
assert(octree.bbox(octree.root()[3]) == CGAL::Bbox_3(0, 0, -1.5, 1.5, 1.5, 0));
assert(octree.bbox(octree.root()[4]) == CGAL::Bbox_3(-1.5, -1.5, 0, 0, 0, 1.5));
assert(octree.bbox(octree.root()[5]) == CGAL::Bbox_3(0, -1.5, 0, 1.5, 0, 1.5));
assert(octree.bbox(octree.root()[6]) == CGAL::Bbox_3(-1.5, 0, 0, 0, 1.5, 1.5));
assert(octree.bbox(octree.root()[7]) == CGAL::Bbox_3(0, 0, 0, 1.5, 1.5, 1.5));
// Compare children of the first child
assert(octree.bbox(octree.root()[0][0]) == CGAL::Bbox_3(-1.5, -1.5, -1.5, -0.75, -0.75, -0.75));
assert(octree.bbox(octree.root()[0][1]) == CGAL::Bbox_3(-0.75, -1.5, -1.5, 0, -0.75, -0.75));
assert(octree.bbox(octree.root()[0][2]) == CGAL::Bbox_3(-1.5, -0.75, -1.5, -0.75, 0, -0.75));
assert(octree.bbox(octree.root()[0][3]) == CGAL::Bbox_3(-0.75, -0.75, -1.5, 0, 0, -0.75));
assert(octree.bbox(octree.root()[0][4]) == CGAL::Bbox_3(-1.5, -1.5, -0.75, -0.75, -0.75, 0));
assert(octree.bbox(octree.root()[0][5]) == CGAL::Bbox_3(-0.75, -1.5, -0.75, 0, -0.75, 0));
assert(octree.bbox(octree.root()[0][6]) == CGAL::Bbox_3(-1.5, -0.75, -0.75, -0.75, 0, 0));
assert(octree.bbox(octree.root()[0][7]) == CGAL::Bbox_3(-0.75, -0.75, -0.75, 0, 0, 0));
// Compare children of the last child
assert(octree.bbox(octree.root()[7][0]) == CGAL::Bbox_3(0, 0, 0, 0.75, 0.75, 0.75));
assert(octree.bbox(octree.root()[7][1]) == CGAL::Bbox_3(0.75, 0, 0, 1.5, 0.75, 0.75));
assert(octree.bbox(octree.root()[7][2]) == CGAL::Bbox_3(0, 0.75, 0, 0.75, 1.5, 0.75));
assert(octree.bbox(octree.root()[7][3]) == CGAL::Bbox_3(0.75, 0.75, 0, 1.5, 1.5, 0.75));
assert(octree.bbox(octree.root()[7][4]) == CGAL::Bbox_3(0, 0, 0.75, 0.75, 0.75, 1.5));
assert(octree.bbox(octree.root()[7][5]) == CGAL::Bbox_3(0.75, 0, 0.75, 1.5, 0.75, 1.5));
assert(octree.bbox(octree.root()[7][6]) == CGAL::Bbox_3(0, 0.75, 0.75, 0.75, 1.5, 1.5));
assert(octree.bbox(octree.root()[7][7]) == CGAL::Bbox_3(0.75, 0.75, 0.75, 1.5, 1.5, 1.5));
}
int main(void) {
test_1_node();
test_9_nodes();
test_25_nodes();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,51 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <cassert>
#include <CGAL/point_generators_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> Octree;
int main(void)
{
std::size_t nb_pts = 100;
Point_set points;
CGAL::Random_points_in_cube_3<Point> generator;
points.reserve(nb_pts);
for (std::size_t i = 0; i < nb_pts; ++i)
points.insert(*(generator++));
Octree base (points, points.point_map());
assert (base.root().is_leaf()); // base is not refined yet
Octree copy1 (base);
assert (copy1.root().is_leaf()); // copy1 is thus not refined either
assert (base == copy1); // base should be equal to copy1
base.refine();
assert (!base.root().is_leaf()); // base is now refined
assert (copy1.root().is_leaf()); // copy1 should be unaffected and still unrefined
assert (base != copy1); // base should be different from copy1
Octree copy2 (base);
assert (!copy2.root().is_leaf()); // copy2 should be refined
assert (base == copy2); // base should be equal to copy2
Octree move (std::move(base));
assert (!move.root().is_leaf()); // move should be refined
assert (base.root().is_leaf()); // base should be back to init state (unrefined)
assert (copy1.root().is_leaf()); // copy1 still unaffected and still unrefined
assert (!copy2.root().is_leaf()); // copy2 unaffected by move and still refined
assert (move == copy2); // move should be equal to copy2
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,112 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree;
void test_identical_trees() {
// Create a simple point set
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
points.insert({-1, 1, -1});
points.insert({1, 1, -1});
points.insert({-1, -1, 1});
points.insert({1, -1, 1});
points.insert({-1, 1, 1});
points.insert({1, 1, 1});
// Create a pair of trees from the same point set
Octree a(points, points.point_map());
Octree b(points, points.point_map());
// Refine both trees using the same criteria
a.refine(10, 1);
b.refine(10, 1);
// Check if those trees are considered equal
assert(a == b);
}
void test_identical_contents_different_criteria() {
// Create a simple point set
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
points.insert({-1, 1, -1});
points.insert({1, 1, -1});
points.insert({-1, -1, 1});
points.insert({1, -1, 1});
points.insert({-1, 1, 1});
points.insert({1, 1, 1});
// Create a pair of trees from the same point set
Octree a(points, points.point_map());
Octree b(points, points.point_map());
// Refine both trees using different criteria
a.refine(10, 1);
b.refine(10, 9);
// Check if those trees are considered equal
assert(a != b);
}
void test_different_contents_identical_criteria() {
// Create a couple of simple point sets
Point_set points_a;
points_a.insert({-1, -1, -1});
points_a.insert({1, -1, -1});
points_a.insert({-1, 1, -1});
points_a.insert({1, 1, -1});
points_a.insert({-1, -1, 1});
points_a.insert({1, -1, 1});
points_a.insert({-1, 1, 1});
points_a.insert({1, 1, 1});
Point_set points_b;
points_b.insert({-1, -1, -1});
points_b.insert({1, -1, -1});
points_b.insert({-1, 1, -1});
points_b.insert({1, 1, -1});
points_b.insert({-1, -1, 1});
points_b.insert({1, -1, 1});
points_b.insert({-1, 1, 1});
points_b.insert({1, 1, 2});
// Create a pair of trees from the different point sets
Octree a(points_a, points_a.point_map());
Octree b(points_b, points_b.point_map());
// Refine both trees using the same criteria
a.refine(10, 1);
b.refine(10, 1);
// Check if those trees are considered equal
assert(a != b);
}
int main(void) {
test_identical_trees();
test_identical_contents_different_criteria();
test_different_contents_identical_criteria();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,75 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Orthtree/Traversals.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <cassert>
#include <CGAL/point_generators_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> Octree;
typedef CGAL::Orthtrees::Leaves_traversal Leaves_traversal;
std::size_t count_jumps(Octree &octree) {
std::size_t jumps = 0;
for (auto &node : octree.traverse(Leaves_traversal())) {
for (int direction = 0; direction < 6; ++direction) {
auto adjacent_node = node.adjacent_node(direction);
if (adjacent_node.is_null())
continue;
if ((node.depth() - adjacent_node.depth()) > 1)
jumps++;
}
}
return jumps;
}
void test(std::size_t dataset_size) {
// Create a dataset
Point_set points;
CGAL::Random_points_in_cube_3<Point> generator;
points.reserve(dataset_size);
for (std::size_t i = 0; i < dataset_size; ++i)
points.insert(*(generator++));
// Build an octree
Octree octree(points, points.point_map());
// Refine the octree
octree.refine();
// Count the jumps in depth
auto jumps = count_jumps(octree);
std::cout << "un-graded octree has " << jumps << " jumps" << std::endl;
assert(jumps > 0);
// Grade the octree
octree.grade();
// Count the jumps in depth
jumps = count_jumps(octree);
std::cout << "graded octree has " << jumps << " jumps" << std::endl;
assert(jumps == 0);
}
int main(void) {
test(100000);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,100 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Orthtree/Traversals.h>
#include <CGAL/Simple_cartesian.h>
#include <cassert>
#include <CGAL/point_generators_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef std::vector<Point> Point_vector;
typedef CGAL::Octree<Kernel, Point_vector> Octree;
int main(void) {
// Fill a vector with points
Point_vector points;
points.emplace_back(1, 1, 1);
points.emplace_back(-1, 1, 1);
points.emplace_back(1, -1, 1);
points.emplace_back(-1, -1, 1);
points.emplace_back(1, 1, -1);
points.emplace_back(-1, 1, -1);
points.emplace_back(1, -1, -1);
points.emplace_back(-1, -1, -1);
points.emplace_back(0.9, -1, -1);
points.emplace_back(0.9, -0.9, -1);
points.emplace_back(0.9, -0.95, -1);
points.emplace_back(0.9, -0.9, -0.9);
points.emplace_back(0.9, -0.95, -0.9);
points.emplace_back(0.9, -1, -1);
points.emplace_back(-0.9, -1, -1);
// Create an octree from the vector
Octree octree(points);
// Build the octree
octree.refine(10, 2);
// Intersection with a point (not particularly useful)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
// Set the search point
auto query = Point{1, 1, 1};
// Get a list of nodes intersected
std::vector<Octree::Node> nodes{};
octree.intersected_nodes(query, std::back_inserter(nodes));
// A point should only intersect one node
assert(1 == nodes.size());
// That node should be the node leaf that contains the point
assert(octree.locate(Point(1, 1, 1)) == nodes[0]);
}
// Intersection with a sphere
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
// Set the search point
auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0);
// Get a list of nodes intersected
std::vector<Octree::Node> nodes{};
octree.intersected_nodes(query, std::back_inserter(nodes));
// Check the results
assert(4 == nodes.size());
assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[0]);
assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[1]);
assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[2]);
assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[3]);
}
// Intersection with a ray
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
// Set the search point
auto query = Kernel::Ray_3(Point{1, 1, 1}, Point{0, 0, 0});
// Get a list of nodes intersected
std::vector<Octree::Node> nodes{};
octree.intersected_nodes(query, std::back_inserter(nodes));
// Check the results
assert(8 == nodes.size());
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::LEFT_TOP_BACK] == nodes[2]);
assert(octree[Octree::Traits::RIGHT_TOP_BACK] == nodes[3]);
assert(octree[Octree::Traits::LEFT_BOTTOM_FRONT] == nodes[4]);
assert(octree[Octree::Traits::RIGHT_BOTTOM_FRONT] == nodes[5]);
assert(octree[Octree::Traits::LEFT_TOP_FRONT] == nodes[6]);
assert(octree[Octree::Traits::RIGHT_TOP_FRONT] == nodes[7]);
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,33 @@
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/point_generators_3.h>
template <typename Kernel>
void test()
{
typedef typename Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> Octree;
Point_set points;
CGAL::Random_points_in_cube_3<Point> generator;
points.reserve(100);
for (std::size_t i = 0; i < 100; ++i)
points.insert(*(generator++));
Octree octree(points, points.point_map());
octree.refine();
octree.grade();
}
int main (int, char**)
{
test<CGAL::Simple_cartesian<float> >();
test<CGAL::Simple_cartesian<double> >();
test<CGAL::Exact_predicates_inexact_constructions_kernel>();
test<CGAL::Exact_predicates_exact_constructions_kernel>();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,124 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <cassert>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree;
void test_1_point() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Because there's only the root node, any point should be placed in it
assert(octree.root() == octree.locate(Point(-1, -1, -1)));
// These points would be placed outside the root node
// assert(octree.root() == octree.locate({0, 0, 0}));
// assert(octree.root() == octree.locate({1000, 0, -1000}));
}
void test_8_points() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
points.insert({-1, 1, -1});
points.insert({1, 1, -1});
points.insert({-1, -1, 1});
points.insert({1, -1, 1});
points.insert({-1, 1, 1});
points.insert({1, 1, 1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Existing points should end up in the same place
assert(octree.root()[0] == octree.locate({-1, -1, -1}));
assert(octree.root()[1] == octree.locate({1, -1, -1}));
assert(octree.root()[2] == octree.locate({-1, 1, -1}));
assert(octree.root()[3] == octree.locate({1, 1, -1}));
assert(octree.root()[4] == octree.locate({-1, -1, 1}));
assert(octree.root()[5] == octree.locate({1, -1, 1}));
assert(octree.root()[6] == octree.locate({-1, 1, 1}));
assert(octree.root()[7] == octree.locate({1, 1, 1}));
// Points adjacent to the existing points should also end up in the same place
assert(octree.root()[0] == octree.locate({-1.1, -1.1, -1.1}));
assert(octree.root()[1] == octree.locate({1.1, -1.1, -1.1}));
assert(octree.root()[2] == octree.locate({-1.1, 1.1, -1.1}));
assert(octree.root()[3] == octree.locate({1.1, 1.1, -1.1}));
assert(octree.root()[4] == octree.locate({-1.1, -1.1, 1.1}));
assert(octree.root()[5] == octree.locate({1.1, -1.1, 1.1}));
assert(octree.root()[6] == octree.locate({-1.1, 1.1, 1.1}));
assert(octree.root()[7] == octree.locate({1.1, 1.1, 1.1}));
}
void test_10_points() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
points.insert({-1, 1, -1});
points.insert({1, 1, -1});
points.insert({-1, -1, 1});
points.insert({1, -1, 1});
points.insert({-1, 1, 1});
points.insert({1, 1, 1});
points.insert({0.875, 1, -1});
points.insert({-1, -0.75, 1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Existing points should end up in the same place
assert(octree.root()[0] == octree.locate({-1, -1, -1}));
assert(octree.root()[1] == octree.locate({1, -1, -1}));
assert(octree.root()[2] == octree.locate({-1, 1, -1}));
assert(octree.root()[3][3][3] == octree.locate({1, 1, -1}));
assert(octree.root()[4][4][4] == octree.locate({-1, -1, 1}));
assert(octree.root()[5] == octree.locate({1, -1, 1}));
assert(octree.root()[6] == octree.locate({-1, 1, 1}));
assert(octree.root()[7] == octree.locate({1, 1, 1}));
// Points adjacent to the existing points might end up in different places
assert(octree.root()[0] == octree.locate({-1.1, -1.1, -1.1}));
assert(octree.root()[1] == octree.locate({1.1, -1.1, -1.1}));
assert(octree.root()[2] == octree.locate({-1.1, 1.1, -1.1}));
assert(octree.root()[3][3][3] == octree.locate({1.1, 1.1, -1.1}));
assert(octree.root()[4][4][4] == octree.locate({-1.1, -1.1, 1.1}));
assert(octree.root()[5] == octree.locate({1.1, -1.1, 1.1}));
assert(octree.root()[6] == octree.locate({-1.1, 1.1, 1.1}));
assert(octree.root()[7] == octree.locate({1.1, 1.1, 1.1}));
}
int main(void) {
test_1_point();
test_8_points();
test_10_points();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,157 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/point_generators_3.h>
#include <CGAL/squared_distance_3.h>
#include <CGAL/Orthogonal_k_neighbor_search.h>
#include <CGAL/Search_traits_3.h>
#include <chrono>
#include <cassert>
using namespace std::chrono;
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::FT FT;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>
Octree;
typedef CGAL::Search_traits_3<Kernel> Kd_tree_traits;
typedef CGAL::Orthogonal_k_neighbor_search<Kd_tree_traits> Kd_tree_search;
typedef Kd_tree_search::Tree Kd_tree;
void naive_vs_octree(std::size_t dataset_size) {
std::cout << "[ " << dataset_size << " points ]" << std::endl;
// Create a dataset
Point_set points;
CGAL::Random_points_in_cube_3<Point> generator;
points.reserve(dataset_size);
for (std::size_t i = 0; i < dataset_size; ++i)
points.insert(*(generator++));
// Choose another random point from the same bounds as the dataset
Point random_point = *(generator++);
// Use the naive algorithm to find the nearest point in the dataset
Point naive_nearest = *points.points().begin();
auto naive_start_time = high_resolution_clock::now();
{
FT distance_nearest = (std::numeric_limits<FT>::max)();
for (auto &p : points.points()) {
FT distance_current = CGAL::squared_distance(p, random_point);
if (distance_current < distance_nearest) {
distance_nearest = distance_current;
naive_nearest = p;
}
}
}
duration<float> naive_elapsed_time = high_resolution_clock::now() - naive_start_time;
std::cout << "Naive --> "
<< "distance^2 of "
<< CGAL::squared_distance(naive_nearest, random_point) << " "
<< "with a time of "
<< naive_elapsed_time.count() << "s "
<< std::endl;
// Do the same using the octree
Point octree_nearest = *generator;
Octree octree(points, points.point_map());
octree.refine(10, 20);
auto octree_start_time = high_resolution_clock::now();
{
// TODO: Write a nearest-neighbor implementation and use it here
std::vector<Point> k_neighbors;
octree.nearest_neighbors(random_point, 1, std::back_inserter(k_neighbors));
octree_nearest = *k_neighbors.begin();
}
duration<float> octree_elapsed_time = high_resolution_clock::now() - octree_start_time;
std::cout << "Octree --> "
<< "distance^2 of "
<< CGAL::squared_distance(octree_nearest, random_point) << " "
<< "with a time of "
<< octree_elapsed_time.count() << "s "
<< std::endl;
// Check that they produce the same answer
assert(octree_nearest == naive_nearest);
}
void kdtree_vs_octree(std::size_t dataset_size, std::size_t K) {
std::cout << "[ " << dataset_size << " points ]" << std::endl;
// Create a dataset
Point_set points;
CGAL::Random_points_in_cube_3<Point> generator;
points.reserve(dataset_size);
for (std::size_t i = 0; i < dataset_size; ++i)
points.insert(*(generator++));
// Choose another random point from the same bounds as the dataset
Point random_point = *(generator++);
// Use the naive algorithm to find the nearest point in the dataset
std::vector<Point> kd_tree_nearest_neighbors;
Kd_tree kd_tree(points.points().begin(), points.points().end());
kd_tree.build();
auto kd_tree_start_time = high_resolution_clock::now();
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;
for (auto p : search)
kd_tree_nearest_neighbors.push_back(p.first);
std::cout << "Kd_tree --> "
<< kd_tree_nearest_neighbors.size() << " points "
<< "in " << kd_tree_elapsed_time.count() << "s "
<< std::endl;
// Do the same using the octree
std::vector<Point> octree_nearest_neighbors;
Octree octree(points, points.point_map());
octree.refine(10, 20);
auto octree_start_time = high_resolution_clock::now();
octree.nearest_neighbors(random_point, K, std::back_inserter(octree_nearest_neighbors));
duration<float> octree_elapsed_time = high_resolution_clock::now() - octree_start_time;
std::cout << "Octree --> "
<< octree_nearest_neighbors.size() << " points "
<< "in " << octree_elapsed_time.count() << "s "
<< std::endl;
// Check that the octree produces the right number of results
assert(octree_nearest_neighbors.size() == K);
// Check that they produce the same answer
for (std::size_t j = 0; j < K; ++j)
assert(octree_nearest_neighbors[j] == kd_tree_nearest_neighbors[j]);
}
int main(void) {
naive_vs_octree(500);
naive_vs_octree(1000);
naive_vs_octree(10000);
naive_vs_octree(100000);
kdtree_vs_octree(100, 16);
kdtree_vs_octree(1000, 16);
kdtree_vs_octree(10000, 16);
kdtree_vs_octree(100000, 16);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,84 @@
#define CGAL_TRACE_STREAM std::cerr
#include <iostream>
#include <CGAL/Octree.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <cassert>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> Octree;
typedef Octree::Node Node;
void test_1_point() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Check that the topology matches
Node single_node = CGAL::Orthtrees::Node_access::create_node(Node(), 0);
CGAL::Orthtrees::Node_access::points(single_node)
= CGAL::Orthtrees::Node_access::points(octree.root());
assert(Node::is_topology_equal(single_node, octree.root()));
assert(0 == octree.depth());
}
void test_2_points() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// The octree should have been split once
Node other = CGAL::Orthtrees::Node_access::create_node(Node(), 0);
CGAL::Orthtrees::Node_access::split(other);
assert(Node::is_topology_equal(other, octree.root()));
assert(1 == octree.depth());
}
void test_4_points() {
Point_set points;
points.insert({1, 1, 1});
points.insert({1, 1, 2});
points.insert({1, 1, 3});
points.insert({1, 1, 4});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// 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);
CGAL::Orthtrees::Node_access::split(other);
CGAL::Orthtrees::Node_access::split(other[3]);
CGAL::Orthtrees::Node_access::split(other[7]);
assert(Node::is_topology_equal(other, octree.root()));
assert(2 == octree.depth());
}
int main(void) {
test_1_point();
test_2_points();
test_4_points();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,116 @@
#define CGAL_TRACE_STREAM std::cerr
#include <CGAL/Octree.h>
#include <CGAL/Orthtree/Traversals.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_3.h>
#include <cassert>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point;
typedef CGAL::Point_set_3<Point> Point_set;
typedef CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map> Octree;
typedef CGAL::Orthtrees::Preorder_traversal Preorder_traversal;
bool test_preorder_1_node() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Create the range
auto nodes = octree.traverse<Preorder_traversal>();
// Check each item in the range
auto iter = nodes.begin();
assert(*iter == octree.root());
return true;
}
bool test_preorder_9_nodes() {
// Define the dataset
Point_set points;
points.insert({-1, -1, -1});
points.insert({1, -1, -1});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Create the range
auto nodes = octree.traverse<Preorder_traversal>();
// Check each item in the range
auto iter = nodes.begin();
assert(*iter == octree.root());
for (int i = 0; i < 8; ++i) {
iter++;
assert((*iter == octree.root()[i]));
}
return true;
}
bool test_preorder_25_nodes() {
// Define the dataset
Point_set points;
points.insert({1, 1, 1});
points.insert({1, 1, 2});
points.insert({1, 1, 3});
points.insert({1, 1, 4});
// Create the octree
Octree octree(points, points.point_map());
octree.refine(10, 1);
// Create the range
auto nodes = octree.traverse<Preorder_traversal>();
// Check each item in the range
auto iter = nodes.begin();
assert(*iter == octree.root());
iter++;
assert((*iter == octree.root()[0]));
iter++;
assert((*iter == octree.root()[1]));
iter++;
assert((*iter == octree.root()[2]));
iter++;
assert((*iter == octree.root()[3]));
for (int i = 0; i < 8; ++i) {
iter++;
assert((*iter == octree.root()[3][i]));
}
iter++;
assert((*iter == octree.root()[4]));
iter++;
assert((*iter == octree.root()[5]));
iter++;
assert((*iter == octree.root()[6]));
iter++;
assert((*iter == octree.root()[7]));
for (int i = 0; i < 8; ++i) {
iter++;
assert((*iter == octree.root()[7][i]));
}
return true;
}
int main(void) {
test_preorder_1_node();
test_preorder_9_nodes();
test_preorder_25_nodes();
return 0;
}

View File

@ -615,6 +615,44 @@ make_counting_range (const SizeType begin, const SizeType end)
/// \endcond
/// \cond SKIP_IN_MANUAL
/*
This property map is used to turn a property map using the value
type of a random access iterator as key type to the same property
map but using the index of the element iterated to.
It basically allows, when accessing the ith element of a range, to
do `get(map, i)` instead of `get(map, range[i])`.
*/
template<typename RandomAccessIterator, typename PropertyMap>
struct Random_index_access_property_map
{
typedef std::size_t key_type;
typedef typename boost::property_traits<PropertyMap>::value_type value_type;
typedef typename boost::property_traits<PropertyMap>::reference reference;
typedef typename boost::property_traits<PropertyMap>::category category;
RandomAccessIterator m_begin;
PropertyMap m_map;
Random_index_access_property_map (RandomAccessIterator begin = RandomAccessIterator(),
PropertyMap map = PropertyMap())
: m_begin(begin), m_map(map) {}
friend reference get (const Random_index_access_property_map& map, const key_type& index,
typename std::enable_if<std::is_convertible<category, boost::readable_property_map_tag>::value>::type* = 0)
{
return get(map.m_map, *std::next(map.m_begin, index));
}
friend void put (Random_index_access_property_map& map, const key_type& index, const value_type& value,
typename std::enable_if<std::is_convertible<category, boost::writable_property_map_tag>::value>::type* = 0)
{
put (map.m_map, *std::next(map.m_begin, index), value);
}
};
/// \endcond
} // namespace CGAL

View File

@ -42,7 +42,7 @@
//---------------------
namespace CGAL {
namespace Shape_detection {
namespace Shape_detection {
/*!
\ingroup PkgShapeDetectionRANSAC
@ -56,13 +56,14 @@ namespace CGAL {
\tparam Traits must be a model of `EfficientRANSACTraits`.
*/
template <class Traits>
class Efficient_RANSAC {
public:
template<class Traits>
class Efficient_RANSAC {
public:
/// \cond SKIP_IN_MANUAL
struct Filter_unassigned_points {
Filter_unassigned_points() : m_shape_index(dummy) {}
Filter_unassigned_points(const std::vector<int> &shapeIndex)
: m_shape_index(shapeIndex) {}
@ -71,7 +72,8 @@ namespace CGAL {
return m_shape_index[x] == -1;
else return true; // to prevent infinite incrementing
}
const std::vector<int>& m_shape_index;
const std::vector<int> &m_shape_index;
std::vector<int> dummy;
};
@ -106,6 +108,7 @@ namespace CGAL {
typedef unspecified_type Plane_range;
///< `Iterator_range` with a bidirectional constant iterator type with value type `boost::shared_ptr<Plane_shape>`.
#else
struct Shape_range : public Iterator_range<
typename std::vector<boost::shared_ptr<Shape> >::const_iterator> {
typedef Iterator_range<
@ -114,6 +117,7 @@ namespace CGAL {
Shape_range(boost::shared_ptr<std::vector<boost::shared_ptr<Shape> > >
extracted_shapes) : Base(make_range(extracted_shapes->begin(),
extracted_shapes->end())), m_extracted_shapes(extracted_shapes) {}
private:
boost::shared_ptr<std::vector<boost::shared_ptr<Shape> > >
m_extracted_shapes; // keeps a reference to the shape vector
@ -127,10 +131,12 @@ namespace CGAL {
Plane_range(boost::shared_ptr<std::vector<boost::shared_ptr<Plane_shape> > >
extracted_shapes) : Base(make_range(extracted_shapes->begin(),
extracted_shapes->end())), m_extracted_shapes(extracted_shapes) {}
private:
boost::shared_ptr<std::vector<boost::shared_ptr<Plane_shape> > >
m_extracted_shapes; // keeps a reference to the shape vector
};
#endif
#ifdef DOXYGEN_RUNNING
@ -154,12 +160,8 @@ namespace CGAL {
*/
struct Parameters {
Parameters()
: probability((FT) 0.01)
, min_points((std::numeric_limits<std::size_t>::max)())
, epsilon(-1)
, normal_threshold((FT) 0.9)
, cluster_epsilon(-1)
{}
: probability((FT) 0.01), min_points((std::numeric_limits<std::size_t>::max)()), epsilon(-1),
normal_threshold((FT) 0.9), cluster_epsilon(-1) {}
/*!
Probability to control search endurance.
@ -207,21 +209,20 @@ namespace CGAL {
};
/// @}
private:
private:
typedef internal::RANSAC_octree<Traits> Direct_octree;
typedef internal::RANSAC_octree<Traits> Indexed_octree;
typedef internal::Octree<internal::DirectPointAccessor<Traits> >
Direct_octree;
typedef internal::Octree<internal::IndexedPointAccessor<Traits> >
Indexed_octree;
//--------------------------------------------typedef
// Creates a function pointer for instancing shape instances.
template <class ShapeT>
template<class ShapeT>
static Shape *factory() {
return new ShapeT;
}
public:
public:
/// \name Initialization
/// @{
@ -230,14 +231,9 @@ namespace CGAL {
Constructs an empty shape detection object.
*/
Efficient_RANSAC(Traits t = Traits())
: m_traits(t)
, m_direct_octrees(nullptr)
, m_global_octree(nullptr)
, m_num_subsets(0)
, m_num_available_points(0)
, m_num_total_points(0)
, m_valid_iterators(false)
{}
: m_traits(t), m_direct_octrees(nullptr), m_global_octree(nullptr), m_num_subsets(0),
m_num_available_points(0), m_num_total_points(0), m_valid_iterators(false) {
}
/*!
Releases all memory allocated by this instance including shapes.
@ -249,28 +245,26 @@ namespace CGAL {
/*!
Retrieves the traits class.
*/
const Traits&
traits() const
{
const Traits &
traits() const {
return m_traits;
}
/*!
Retrieves the point property map.
*/
const Point_map& point_map() const { return m_point_pmap; }
const Point_map &point_map() const { return m_point_pmap; }
/*!
Retrieves the normal property map.
*/
const Normal_map& normal() const { return m_normal_pmap; }
const Normal_map &normal() const { return m_normal_pmap; }
Input_iterator input_iterator_first() const
{
Input_iterator input_iterator_first() const {
return m_input_iterator_first;
}
Input_iterator input_iterator_beyond() const
{
Input_iterator input_iterator_beyond() const {
return m_input_iterator_beyond;
}
@ -281,7 +275,7 @@ namespace CGAL {
`detect()` and `preprocess()`. This function first calls `clear()`.
*/
void set_input(
Input_range& input_range,
Input_range &input_range,
///< Range of input data.
Point_map point_map = Point_map(),
///< Property map to access the position of an input point.
@ -303,7 +297,9 @@ namespace CGAL {
m_input_iterator_first, m_input_iterator_beyond);
m_valid_iterators = true;
}
/*!
Registers the shape type `ShapeType` in the detection engine that must inherit from `Shape_base`.
For example, for registering a plane as detectable shape, you should call
@ -312,7 +308,7 @@ namespace CGAL {
keyword just before `add_shape_factory`:
`ransac.template add_shape_factory< Shape_detection::Plane<Traits> >();`.
*/
template <class Shape_type>
template<class Shape_type>
void add_shape_factory() {
m_shape_factories.push_back(factory<Shape_type>);
}
@ -324,12 +320,14 @@ namespace CGAL {
before by the user.
*/
bool preprocess() {
if (m_num_total_points == 0)
return false;
// Generation of subsets
m_num_subsets = (std::size_t)(std::max<std::ptrdiff_t>)((std::ptrdiff_t)
std::floor(std::log(double(m_num_total_points))/std::log(2.))-9, 2);
m_num_subsets = (std::size_t) (std::max<std::ptrdiff_t>)((std::ptrdiff_t)
std::floor(std::log(double(m_num_total_points)) /
std::log(2.)) - 9, 2);
// SUBSET GENERATION ->
// approach with increasing subset sizes -> replace with octree later on
@ -338,14 +336,14 @@ namespace CGAL {
m_available_octree_sizes.resize(m_num_subsets);
m_direct_octrees = new Direct_octree *[m_num_subsets];
for (int s = int(m_num_subsets) - 1;s >= 0;--s) {
for (int s = int(m_num_subsets) - 1; s >= 0; --s) {
std::size_t subsetSize = remainingPoints;
std::vector<std::size_t> indices(subsetSize);
if (s) {
subsetSize >>= 1;
for (std::size_t i = 0;i<subsetSize;i++) {
for (std::size_t i = 0; i < subsetSize; i++) {
std::size_t index = get_default_random()(2);
index = index + (i<<1);
index = index + (i << 1);
index = (index >= remainingPoints) ? remainingPoints - 1 : index;
indices[i] = index;
}
@ -363,26 +361,26 @@ namespace CGAL {
m_direct_octrees[s] = new Direct_octree(
m_traits, last + 1,
last + subsetSize + 1,
m_point_pmap, m_normal_pmap,
m_point_pmap,
remainingPoints - subsetSize);
}
else
} else
m_direct_octrees[0] = new Direct_octree(
m_traits, m_input_iterator_first,
m_input_iterator_first + (subsetSize),
m_point_pmap, m_normal_pmap,
m_point_pmap,
0);
m_available_octree_sizes[s] = subsetSize;
m_direct_octrees[s]->createTree(m_options.cluster_epsilon);
m_direct_octrees[s]->refine(m_options.cluster_epsilon);
remainingPoints -= subsetSize;
}
m_global_octree = new Indexed_octree(
m_traits, m_input_iterator_first, m_input_iterator_beyond,
m_point_pmap, m_normal_pmap);
m_global_octree->createTree(m_options.cluster_epsilon);
m_point_pmap
);
m_global_octree->refine(m_options.cluster_epsilon);
return true;
}
@ -413,9 +411,9 @@ namespace CGAL {
}
if (m_direct_octrees) {
for (std::size_t i = 0;i<m_num_subsets;i++)
for (std::size_t i = 0; i < m_num_subsets; i++)
delete m_direct_octrees[i];
delete [] m_direct_octrees;
delete[] m_direct_octrees;
m_direct_octrees = nullptr;
}
@ -467,9 +465,9 @@ namespace CGAL {
input data has been set. Otherwise, `false` is returned.
*/
bool detect(const Parameters &options = Parameters(),
const std::function<bool(double)>& callback
= std::function<bool(double)>())
{
const std::function<bool(double)> &callback
= std::function<bool(double)>()) {
m_options = options;
// No shape types for detection or no points provided, exit
@ -490,7 +488,7 @@ namespace CGAL {
boost::make_shared<std::vector<boost::shared_ptr<Shape> > >();
m_num_available_points = m_num_total_points;
for (std::size_t i = 0;i<m_num_subsets;i++) {
for (std::size_t i = 0; i < m_num_subsets; i++) {
m_available_octree_sizes[i] = m_direct_octrees[i]->size();
}
@ -528,7 +526,7 @@ namespace CGAL {
// Identifying minimum number of samples
m_required_samples = 0;
for (std::size_t i = 0;i<m_shape_factories.size();i++) {
for (std::size_t i = 0; i < m_shape_factories.size(); i++) {
Shape *tmp = (Shape *) m_shape_factories[i]();
m_required_samples = (std::max<std::size_t>)(m_required_samples, tmp->minimum_sample_size());
delete tmp;
@ -573,7 +571,8 @@ namespace CGAL {
static_cast<unsigned int>(m_num_available_points));
while (m_shape_index[first_sample] != -1);
done = m_global_octree->drawSamplesFromCellContainingPoint(
done = drawSamplesFromCellContainingPoint
(m_global_octree,
get(m_point_pmap,
*(m_input_iterator_first + first_sample)),
select_random_octree_level(),
@ -589,6 +588,7 @@ namespace CGAL {
generated_candidates++;
//add candidate for each type of primitives
bool candidate_success = false;
for(typename std::vector<Shape *(*)()>::iterator it =
m_shape_factories.begin(); it != m_shape_factories.end(); it++) {
if (callback && !callback(num_invalid / double(m_num_total_points)))
@ -612,17 +612,19 @@ namespace CGAL {
best_expected = p->expected_value();
candidates.push_back(p);
candidate_success = true;
}
else {
failed_candidates++;
delete p;
}
}
else {
failed_candidates++;
delete p;
}
}
if (!candidate_success)
++ failed_candidates;
}
if (failed_candidates >= limit_failed_candidates)
@ -634,7 +636,7 @@ namespace CGAL {
m_num_available_points - num_invalid,
generated_candidates, m_global_octree->maxLevel())
> m_options.probability);
} while( !force_exit
} while (!force_exit
&& stop_probability((std::size_t) best_expected,
m_num_available_points - num_invalid,
generated_candidates,
@ -670,7 +672,8 @@ namespace CGAL {
best_candidate->m_indices.clear();
best_candidate->m_score =
m_global_octree->score(best_candidate,
score(m_global_octree,
best_candidate,
m_shape_index,
FT(3) * m_options.epsilon,
m_options.normal_threshold);
@ -684,10 +687,9 @@ namespace CGAL {
return false;
// check score against min_points and clear out candidates if too low
if (best_candidate->indices_of_assigned_points().size() <
m_options.min_points)
{
m_options.min_points) {
if (!(best_candidate->indices_of_assigned_points().empty()))
for (std::size_t i = 0;i < candidates.size() - 1;i++) {
for (std::size_t i = 0; i < candidates.size() - 1; i++) {
if (best_candidate->is_same(candidates[i])) {
delete candidates[i];
candidates[i] = nullptr;
@ -727,9 +729,7 @@ namespace CGAL {
if (callback && !callback(num_invalid / double(m_num_total_points)))
return false;
}
else
if (stop_probability((std::size_t) best_candidate->expected_value(),
} else if (stop_probability((std::size_t) best_candidate->expected_value(),
(m_num_available_points - num_invalid),
generated_candidates,
m_global_octree->maxLevel())
@ -750,19 +750,19 @@ namespace CGAL {
best_candidate->indices_of_assigned_points();
// update generated candidates to reflect removal of points
generated_candidates = std::size_t(std::pow (1.f - (indices_points_best_candidate.size() /
generated_candidates = std::size_t(std::pow(1.f - (indices_points_best_candidate.size() /
float(m_num_available_points - num_invalid)), 3.f)
* generated_candidates);
//2.3 Remove the points from the subtrees
for (std::size_t i = 0;i<indices_points_best_candidate.size();i++) {
for (std::size_t i = 0; i < indices_points_best_candidate.size(); i++) {
m_shape_index[indices_points_best_candidate.at(i)] =
int(m_extracted_shapes->size()) - 1;
num_invalid++;
for (std::size_t j = 0;j<m_num_subsets;j++) {
if (m_direct_octrees[j] && m_direct_octrees[j]->m_root) {
for (std::size_t j = 0; j < m_num_subsets; j++) {
if (m_direct_octrees[j]) {
std::size_t offset = m_direct_octrees[j]->offset();
if (offset <= indices_points_best_candidate.at(i) &&
@ -782,15 +782,15 @@ namespace CGAL {
std::vector<std::size_t> subset_sizes(m_num_subsets);
subset_sizes[0] = m_available_octree_sizes[0];
for (std::size_t i = 1;i<m_num_subsets;i++) {
subset_sizes[i] = subset_sizes[i-1] + m_available_octree_sizes[i];
for (std::size_t i = 1; i < m_num_subsets; i++) {
subset_sizes[i] = subset_sizes[i - 1] + m_available_octree_sizes[i];
}
//3. Remove points from candidates common with extracted primitive
//#pragma omp parallel for
best_expected = 0;
for (std::size_t i=0;i< candidates.size()-1;i++) {
for (std::size_t i = 0; i < candidates.size() - 1; i++) {
if (candidates[i]) {
candidates[i]->update_points(m_shape_index);
candidates[i]->compute_bound(
@ -800,8 +800,7 @@ namespace CGAL {
if (candidates[i]->max_bound() < m_options.min_points) {
delete candidates[i];
candidates[i] = nullptr;
}
else {
} else {
best_expected = (candidates[i]->expected_value() > best_expected) ?
candidates[i]->expected_value() : best_expected;
}
@ -826,9 +825,8 @@ namespace CGAL {
if (candidates[end]) end++;
candidates.resize(end);
}
else if (!keep_searching)
++ generated_candidates;
} else if (!keep_searching)
++generated_candidates;
if (callback && !callback(num_invalid / double(m_num_total_points)))
return false;
@ -838,13 +836,12 @@ namespace CGAL {
generated_candidates,
m_global_octree->maxLevel())
> m_options.probability);
}
while((keep_searching
} while ((keep_searching
&& FT(m_num_available_points - num_invalid) >= m_options.min_points)
|| best_expected >= m_options.min_points);
// Clean up remaining candidates.
for (std::size_t i = 0;i<candidates.size();i++)
for (std::size_t i = 0; i < candidates.size(); i++)
delete candidates[i];
candidates.resize(0);
@ -879,14 +876,13 @@ namespace CGAL {
boost::shared_ptr<std::vector<boost::shared_ptr<Plane_shape> > > planes
= boost::make_shared<std::vector<boost::shared_ptr<Plane_shape> > >();
for (std::size_t i = 0; i < m_extracted_shapes->size(); ++ i)
{
for (std::size_t i = 0; i < m_extracted_shapes->size(); ++i) {
boost::shared_ptr<Plane_shape> pshape
= boost::dynamic_pointer_cast<Plane_shape>((*m_extracted_shapes)[i]);
// Ignore all shapes other than plane
if (pshape != boost::shared_ptr<Plane_shape>())
planes->push_back (pshape);
planes->push_back(pshape);
}
return Plane_range(planes);
}
@ -915,21 +911,22 @@ namespace CGAL {
}
/// @}
private:
private:
int select_random_octree_level() {
return (int) get_default_random()(
static_cast<unsigned int>(m_global_octree->maxLevel() + 1));
auto upper_bound = static_cast<unsigned int>(m_global_octree->maxLevel() + 1);
return (int) get_default_random()(upper_bound);
}
Shape* get_best_candidate(std::vector<Shape* >& candidates,
Shape *get_best_candidate(std::vector<Shape *> &candidates,
const std::size_t num_available_points) {
if (candidates.size() == 1)
return candidates.back();
int index_worse_candidate = 0;
bool improved = true;
while (index_worse_candidate < (int)candidates.size() - 1 && improved) {
while (index_worse_candidate < (int) candidates.size() - 1 && improved) {
improved = false;
typename Shape::Compare_by_max_bound comp;
@ -981,6 +978,7 @@ namespace CGAL {
std::size_t num_available_points,
std::size_t max_subset,
std::size_t min_points) {
if (candidate->m_nb_subset_used >= max_subset)
return false;
@ -995,7 +993,7 @@ namespace CGAL {
//the next subset to include is provided by m_nb_subset_used
std::size_t num_points_evaluated = 0;
for (std::size_t i=0;i<candidate->m_nb_subset_used;i++)
for (std::size_t i = 0; i < candidate->m_nb_subset_used; i++)
num_points_evaluated += m_available_octree_sizes[i];
// need score of new subset as well as sum of
@ -1004,7 +1002,8 @@ namespace CGAL {
std::size_t new_sampled_points = 0;
do {
new_score = m_direct_octrees[candidate->m_nb_subset_used]->score(
new_score =
score(m_direct_octrees[candidate->m_nb_subset_used],
candidate,
m_shape_index,
m_options.epsilon,
@ -1036,7 +1035,133 @@ namespace CGAL {
int(num_candidates)), FT(1));
}
private:
template<class Octree>
std::size_t score(const Octree *octree,
Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
typedef typename Octree::Node Cell;
std::stack<Cell> stack;
stack.push(octree->root());
while (!stack.empty()) {
Cell cell = stack.top();
stack.pop();
FT width = octree->width() / (1 << (cell.depth()));
FT diag = CGAL::sqrt(FT(3) * width * width) + epsilon;
FT dist = candidate->squared_distance(octree->barycenter(cell));
if (dist > (diag * diag))
continue;
// differ between full or partial overlap?
// if full overlap further traversal of this branch is not necessary
if (cell.is_leaf()) {
std::vector<std::size_t> indices;
indices.reserve(cell.size());
for (std::size_t i = 0; i < cell.size(); i++) {
if (shapeIndex[octree->index(cell, i)] == -1) {
indices.push_back(octree->index(cell, i));
}
}
candidate->cost_function(epsilon,
normal_threshold,
indices);
} else {
if (!cell.is_leaf()) {
for (std::size_t i = 0; i < 8; i++) {
if (!cell[i].empty())
stack.push(cell[i]);
}
}
}
}
return candidate->m_indices.size();
}
template<class Octree>
const typename Octree::Node node_containing_point(const Octree *octree, const Point &p, std::size_t level) {
// Find the node containing the point
typename Octree::Node cur = octree->root();
while (!cur.is_null() && cur.depth() < level) {
// If cur is a leaf node, its child is null
if (cur.is_leaf())
return typename Octree::Node();
// If that child is empty, return null
if (cur.empty())
return typename Octree::Node();
// Determine the coordinate of the child
Point center = octree->barycenter(cur);
std::bitset<3> coordinate;
coordinate[0] = center.x() <= p.x();
coordinate[1] = center.y() <= p.y();
coordinate[2] = center.z() <= p.z();
// Otherwise, return the correct child of cur
cur = cur[coordinate.to_ulong()];
}
return cur;
}
template<class Octree>
bool drawSamplesFromCellContainingPoint(const Octree *octree,
const Point &p,
std::size_t level,
std::set<std::size_t> &indices,
const std::vector<int> &shapeIndex,
std::size_t requiredSamples) {
typedef typename Octree::Node Cell;
const Cell cur = node_containing_point(octree, p, level);
// Stop if the node we need doesn't exist
if (cur.is_null())
return false;
// Count point indices that map to -1 in the shape index
std::size_t enough = 0;
for (auto j : cur) {
if (shapeIndex[j] == -1)
enough++;
if (enough >= requiredSamples)
break;
}
// Make sure we found enough samples
if (enough < requiredSamples)
return false;
do {
std::size_t p = CGAL::get_default_random().
uniform_int<std::size_t>(0, cur.size() - 1);
std::size_t j = octree->index(cur, p);
if (shapeIndex[j] == -1)
indices.insert(j);
} while (indices.size() < requiredSamples);
return true;
}
private:
Parameters m_options;
// Traits class.
@ -1067,7 +1192,9 @@ namespace CGAL {
Input_iterator m_input_iterator_first, m_input_iterator_beyond;
Point_map m_point_pmap;
Normal_map m_normal_pmap;
};
};
}
}

View File

@ -22,703 +22,128 @@
#include <CGAL/Random.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/Shape_detection/Efficient_RANSAC/Shape_base.h>
#include <CGAL/Shape_detection/Efficient_RANSAC/Efficient_RANSAC_traits.h>
#include <CGAL/property_map.h>
#include <CGAL/boost/iterator/counting_iterator.hpp>
extern int scoreTime;
#include <CGAL/Octree.h>
namespace CGAL {
namespace Shape_detection {
namespace Shape_detection {
template<class Traits>
class Efficient_RANSAC;
// Forward declaration needed for automatic traits detection without
// including the deprecated header itself…
template <typename Gt, typename IR, typename IPM, typename INM>
struct Shape_detection_traits;
namespace internal {
namespace internal {
const std::size_t size_t_max = (std::numeric_limits<std::size_t>::max)();
template <typename Traits>
struct Traits_base { typedef Traits type; };
template <typename Gt, typename IR, typename IPM, typename INM>
struct Traits_base<CGAL::Shape_detection::Efficient_RANSAC_traits<Gt,IR,IPM,INM> >
{ typedef Gt type; };
template <typename Gt, typename IR, typename IPM, typename INM>
struct Traits_base<CGAL::Shape_detection::Shape_detection_traits<Gt,IR,IPM,INM> >
{ typedef Gt type; };
template<class Sdt>
class DirectPointAccessor {
public:
typedef Sdt Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
template<class Traits>
class RANSAC_octree {
DirectPointAccessor() {}
DirectPointAccessor(const Input_iterator &begin,
const Input_iterator &beyond,
std::size_t offset) : m_first(begin), m_offset(offset) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
}
typedef typename Traits::Input_range::iterator Input_iterator;
typedef typename Traits::Point_map Point_map;
typedef typename Traits::FT FT;
typedef std::vector<std::size_t> Input_range;
typedef Random_index_access_property_map<Input_iterator, Point_map> Indexed_point_map;
Input_iterator at(std::size_t i) {
return m_first + i;
}
typedef CGAL::Octree<typename Traits_base<Traits>::type,
Input_range, Indexed_point_map> Octree;
std::size_t index(std::size_t i) {
return i + m_offset;
}
Traits m_traits;
Input_range m_input_range;
Indexed_point_map m_index_map;
Octree m_octree;
Bbox_3 m_bBox;
FT m_width;
std::size_t offset() {
return m_offset;
}
std::size_t size() {
return m_beyond - m_first + 1;
}
Input_iterator first() {
return m_first;
}
Input_iterator beyond() {
return m_beyond;
}
void setData(Input_iterator &begin, Input_iterator &beyond) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
}
void swap(std::size_t a, std::size_t b) {
typename std::iterator_traits<Input_iterator>::value_type tmp;
tmp = m_first[a];
m_first[a] = m_first[b];
m_first[b] = tmp;
}
protected:
Input_iterator m_first;
private:
Input_iterator m_beyond;
std::size_t m_offset;
};
template<class Sdt>
class IndexedPointAccessor {
public:
typedef Sdt Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
public:
IndexedPointAccessor() {}
IndexedPointAccessor(const Input_iterator &begin,
const Input_iterator &beyond, std::size_t)
: m_first(begin) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
m_indices.resize(size());
for (std::size_t i = 0;i<size();i++)
m_indices[i] = i;
}
typedef typename Octree::Node Node;
Input_iterator at(std::size_t i) {
return m_first + m_indices[i];
}
RANSAC_octree(const Traits &traits,
Input_iterator begin,
Input_iterator end,
Point_map point_map,
std::size_t offset = 0) :
m_traits(traits),
m_input_range(boost::counting_iterator<std::size_t>(0),
boost::counting_iterator<std::size_t>(end - begin)),
m_index_map(begin, point_map),
m_octree(m_input_range, m_index_map, 1.0),
m_bBox (bbox_3(make_transform_iterator_from_property_map(begin, point_map),
make_transform_iterator_from_property_map(end, point_map))),
m_offset(offset) {}
std::size_t index(std::size_t i) {
return m_indices[i];
}
std::size_t offset() {
return 0;
}
Input_iterator first() {
return m_first;
}
Input_iterator beyond() {
return m_beyond;
}
void setData(Input_iterator &begin, Input_iterator &beyond) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
m_indices.resize(size());
for (std::size_t i = 0;i<size();i++)
m_indices[i] = i;
}
std::size_t size() {
return m_beyond - m_first + 1;
}
void swap(std::size_t a, std::size_t b) {
std::size_t tmp = m_indices[a];
m_indices[a] = m_indices[b];
m_indices[b] = tmp;
}
protected:
Input_iterator m_first;
private:
std::vector<std::size_t> m_indices;
Input_iterator m_beyond;
};
template<class PointAccessor>
class Octree : public PointAccessor {
typedef typename PointAccessor::Sd_traits Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
typedef Shape_base<Sd_traits> Shape;
typedef typename Sd_traits::Point_3 Point_3;
typedef typename Sd_traits::Vector_3 Vector_3;
typedef typename Sd_traits::FT FT;
typedef typename Sd_traits::Point_map Point_map;
typedef typename Sd_traits::Normal_map Normal_map;
template<class Sd_traits>
friend class ::CGAL::Shape_detection::Efficient_RANSAC;
struct Cell {
std::size_t first, last;
Cell *child[8];
Point_3 center;
std::size_t level;
Cell(std::size_t first, std::size_t last, Point_3 center, std::size_t level)
: first(first), last(last), center(center), level(level) {
memset(child, 0, sizeof(Cell *) * 8);
}
bool isLeaf() const {
for (std::size_t i = 0;i<8;i++) {
if (child[i])
return false;
}
return true;
std::size_t index (Node node, std::size_t i) const
{
return m_offset + *(node.begin() + i);
}
std::size_t size() const {
if (first == size_t_max || last == size_t_max)
return 0;
else return (last - first + 1);
}
};
// --------------------------------------------------------------------------
// Utilities
// --------------------------------------------------------------------------
FT get_x(const Vector_3& v){ return m_traits.compute_x_3_object()(v); }
FT get_y(const Vector_3& v){ return m_traits.compute_y_3_object()(v); }
FT get_z(const Vector_3& v){ return m_traits.compute_z_3_object()(v); }
FT get_x(const Point_3& p){ return m_traits.compute_x_3_object()(p); }
FT get_y(const Point_3& p){ return m_traits.compute_y_3_object()(p); }
FT get_z(const Point_3& p){ return m_traits.compute_z_3_object()(p); }
FT get_coord(const Point_3& p, unsigned int d)
{
CGAL_assertion(d < 3);
switch (d)
{
case 0: return get_x(p);
case 1: return get_y(p);
case 2: return get_z(p);
default: return FT(0);
}
return m_octree.root().size();
}
Point_3 constr_pt(FT x, FT y, FT z) const
{ return m_traits.construct_point_3_object()(x, y, z); }
Vector_3 constr_vec(const Point_3& p, const Point_3& q) const
{ return m_traits.construct_vector_3_object()(p, q); }
std::size_t maxLevel() const {
return m_octree.depth();
}
Point_3 transl(const Point_3& p, const Vector_3 &v)
{ return m_traits.construct_translated_point_3_object()(p, v); }
std::size_t offset() const { return m_offset; }
public:
Octree(Sd_traits const& traits)
: m_traits(traits), m_bucket_size(20), m_set_max_level(10), m_root(nullptr) {}
Octree(Sd_traits const& traits,
const Input_iterator &first,
const Input_iterator &beyond,
Point_map& point_pmap,
Normal_map& normal_pmap,
std::size_t offset = 0,
void refine(double cluster_epsilon_for_max_level_recomputation = -1.,
std::size_t bucketSize = 20,
std::size_t maxLevel = 10)
: PointAccessor(first, beyond, offset),
m_traits(traits),
m_root(nullptr),
m_bucket_size(bucketSize),
m_set_max_level(maxLevel),
m_point_pmap (point_pmap),
m_normal_pmap (normal_pmap) {}
std::size_t maxLevel = 10) {
~Octree() {
if (!m_root)
return;
if (cluster_epsilon_for_max_level_recomputation > 0.) {
std::stack<Cell *> stack;
stack.push(m_root);
while (!stack.empty()) {
Cell *cell = stack.top();
stack.pop();
for (std::size_t i = 0;i<8;i++)
if (cell->child[i])
stack.push(cell->child[i]);
delete cell;
}
}
// Sorting data in a way such that points of one cell
// are always in one range and ordered child-wise:
// +---+---+
// | 1.| 0.|
// +---+---+
// | 3.| 2.|
// +---+---+
// z max before z min, then y max before y min, then x max before x min
void createTree(double cluster_epsilon_for_max_level_recomputation = -1.) {
buildBoundingCube();
std::size_t count = 0;
m_max_level = 0;
if (cluster_epsilon_for_max_level_recomputation > 0.)
{
FT bbox_diagonal = (FT) CGAL::sqrt(
(m_bBox.xmax() - m_bBox.xmin()) * (m_bBox.xmax() - m_bBox.xmin())
+ (m_bBox.ymax() - m_bBox.ymin()) * (m_bBox.ymax() - m_bBox.ymin())
+ (m_bBox.zmax() - m_bBox.zmin()) * (m_bBox.zmax() - m_bBox.zmin()));
m_set_max_level = std::size_t (std::log (bbox_diagonal
/ cluster_epsilon_for_max_level_recomputation)
/ std::log (2.0));
maxLevel = std::size_t(std::log2(bbox_diagonal
/ cluster_epsilon_for_max_level_recomputation));
if (maxLevel == 0)
maxLevel = 1;
}
std::stack<Cell *> stack;
m_root = new Cell(0, this->size() - 1, m_center, 0);
stack.push(m_root);
while (!stack.empty()) {
Cell *cell= stack.top();
stack.pop();
m_octree.refine(maxLevel, bucketSize);
m_max_level = std::max<std::size_t>(m_max_level, cell->level);
if (cell->level == m_set_max_level)
continue;
std::size_t zLowYHighXSplit, zLowYLowXSplit, zLowYSplit;
std::size_t zHighYSplit, zHighYHighXSplit, zHighYLowXSplit;
std::size_t zSplit = split(cell->first, cell->last, 2, get_z(cell->center));
if (zSplit != size_t_max) {
zLowYSplit = split(cell->first, zSplit, 1, get_y(cell->center));
if (zLowYSplit != size_t_max) {
zLowYLowXSplit = split(cell->first,
zLowYSplit, 0, get_x(cell->center));
zLowYHighXSplit = split(zLowYSplit + 1,
zSplit, 0, get_x(cell->center));
}
else {
zLowYLowXSplit = size_t_max;
zLowYHighXSplit = split(cell->first, zSplit, 0, get_x(cell->center));
m_width = FT(0.5) * FT(m_octree.bbox(m_octree.root()).xmax() - m_octree.bbox(m_octree.root()).xmin());
}
zHighYSplit = split(zSplit + 1, cell->last, 1, get_y(cell->center));
if (zHighYSplit != size_t_max) {
zHighYHighXSplit = split(zHighYSplit + 1,
cell->last, 0, get_x(cell->center));
zHighYLowXSplit = split(zSplit + 1,
zHighYSplit, 0, get_x(cell->center));
}
else {
zHighYLowXSplit = size_t_max;
zHighYHighXSplit = split(zSplit + 1,
cell->last, 0, get_x(cell->center));
}
}
else {
zLowYSplit = size_t_max;
zLowYLowXSplit = size_t_max;
zLowYHighXSplit = size_t_max;
zHighYSplit = split(cell->first,
cell->last,
1,
get_y(cell->center));
if (zHighYSplit != size_t_max) {
zHighYHighXSplit = split(zHighYSplit + 1,
cell->last,
0,
get_x(cell->center));
zHighYLowXSplit = split(cell->first,
zHighYSplit,
0,
get_x(cell->center));
}
else {
zHighYLowXSplit = size_t_max;
zHighYHighXSplit = split(cell->first,
cell->last,
0,
get_x(cell->center));
}
const typename Traits::FT& width() const {
return m_width;
}
FT width = m_width / (1<<(cell->level + 1));
if (zSplit != size_t_max) {
if (zLowYSplit != size_t_max) {
if (zLowYLowXSplit != size_t_max) {
if (cell->first <= zLowYLowXSplit) {
//---
cell->child[7] = new Cell(cell->first,
zLowYLowXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width,-width,-width))),
cell->level + 1);
if (cell->child[7]->size() > m_bucket_size)
stack.push(cell->child[7]);
}
}
else zLowYLowXSplit = cell->first - 1;
if (zLowYLowXSplit < zLowYSplit || zLowYLowXSplit == size_t_max) {
//+--
cell->child[6] = new Cell(zLowYLowXSplit + 1,
zLowYSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width,-width,-width))),
cell->level + 1);
if (cell->child[6]->size() > m_bucket_size)
stack.push(cell->child[6]);
}
}
else zLowYSplit = cell->first - 1;
if (zLowYHighXSplit != size_t_max) {
if (zLowYSplit < zLowYHighXSplit || zLowYSplit == size_t_max) {
//-+-
cell->child[5] = new Cell(zLowYSplit + 1,
zLowYHighXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width, width,-width))),
cell->level + 1);
if (cell->child[5]->size() > m_bucket_size)
stack.push(cell->child[5]);
}
}
else zLowYHighXSplit = zLowYSplit;
if (zLowYHighXSplit < zSplit || zLowYHighXSplit == size_t_max) {
//++-
cell->child[4] = new Cell(zLowYHighXSplit + 1,
zSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width, width,-width))),
cell->level + 1);
if (cell->child[4]->size() > m_bucket_size)
stack.push(cell->child[4]);
}
}
else zSplit = cell->first - 1;
if (zHighYSplit != size_t_max) {
if (zHighYLowXSplit != size_t_max) {
if (zSplit < zHighYLowXSplit || zSplit == size_t_max) {
//--+
cell->child[3] = new Cell(zSplit + 1,
zHighYLowXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width,-width, width))),
cell->level + 1);
if (cell->child[3]->size() > m_bucket_size)
stack.push(cell->child[3]);
}
}
else zHighYLowXSplit = zSplit;
if (zHighYLowXSplit < zHighYSplit || zHighYLowXSplit == size_t_max) {
//+-+
cell->child[2] = new Cell(zHighYLowXSplit + 1,
zHighYSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width,-width, width))),
cell->level + 1);
if (cell->child[2]->size() > m_bucket_size)
stack.push(cell->child[2]);
Node locate(const typename Traits::Point_3 &p) const {
return m_octree.locate(p);
}
}
else zHighYSplit = zSplit;
Node root() const { return m_octree.root(); }
if (zHighYHighXSplit != size_t_max) {
if (zHighYSplit < zHighYHighXSplit || zHighYSplit == size_t_max) {
//-++
cell->child[1] = new Cell(zHighYSplit + 1,
zHighYHighXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width, width, width))),
cell->level + 1);
if (cell->child[1]->size() > m_bucket_size)
stack.push(cell->child[1]);
}
}
else zHighYHighXSplit = zHighYSplit;
if (zHighYHighXSplit <= cell->last || zHighYHighXSplit == size_t_max) {
if (zHighYHighXSplit < cell->last || zHighYHighXSplit == size_t_max) {
//+++
cell->child[0] = new Cell(zHighYHighXSplit + 1,
cell->last,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width, width, width))),
cell->level + 1);
if (cell->child[0]->size() > m_bucket_size)
stack.push(cell->child[0]);
}
typename Traits::Point_3 barycenter(const Node &node) const {
return m_octree.barycenter(node);
}
std::size_t sum = 0;
for (std::size_t i = 0;i<8;i++)
sum += (cell->child[i]) ? cell->child[i]->size() : 0;
count++;
}
}
bool drawSamplesFromCellContainingPoint(const Point_3 &p,
std::size_t level,
std::set<std::size_t>& indices,
const std::vector<int>& shapeIndex,
std::size_t requiredSamples) {
bool upperZ, upperY, upperX;
Cell *cur = m_root;
while (cur && cur->level < level) {
upperX = get_x(cur->center) <= get_x(p);
upperY = get_y(cur->center) <= get_y(p);
upperZ = get_z(cur->center) <= get_z(p);
if (upperZ) {
if (upperY)
cur = (upperX) ? cur->child[0] : cur->child[1];
else cur = (upperX) ? cur->child[2] : cur->child[3];
}
else {
if (upperY)
cur = (upperX) ? cur->child[4] : cur->child[5];
else cur = (upperX) ? cur->child[6] : cur->child[7];
}
}
if (cur) {
std::size_t enough = 0;
for (std::size_t i = cur->first;i<=cur->last;i++) {
std::size_t j = this->index(i);
if (shapeIndex[j] == -1) {
enough++;
if (enough >= requiredSamples)
break;
}
}
if (enough >= requiredSamples) {
do {
std::size_t p = CGAL::get_default_random().
uniform_int<std::size_t>(0, cur->size() - 1);
std::size_t j = this->index(cur->first + p);
if (shapeIndex[j] == -1)
indices.insert(j);
} while (indices.size() < requiredSamples);
return true;
}
else return false;
}
else return false;
}
std::size_t maxLevel() {
return m_max_level;
}
std::size_t fullScore(Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
std::vector<std::size_t> indices(m_root->size());
for (std::size_t i = 0;i<m_root->size();i++) {
indices[i] = index(m_root->first + i);
}
candidate->cost_function(this->begin() + m_root->first,
this->begin() + m_root->last,
shapeIndex,
epsilon,
normal_threshold,
indices);
return candidate->m_indices.size();
}
std::size_t score(Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
std::stack<Cell *> stack;
stack.push(m_root);
while(!stack.empty()) {
Cell *cell = stack.top();
stack.pop();
FT width = m_width / (1<<(cell->level));
FT diag = CGAL::sqrt(FT(3) * width * width) + epsilon;
FT dist = candidate->squared_distance(cell->center);
if (dist > (diag * diag))
continue;
// differ between full or partial overlap?
// if full overlap further traversal of this branch is not necessary
if (cell->isLeaf()) {
std::vector<std::size_t> indices;
indices.reserve(cell->size());
for (std::size_t i = 0;i<cell->size();i++) {
if (shapeIndex[this->index(cell->first + i)] == -1) {
indices.push_back(this->index(cell->first + i));
}
}
candidate->cost_function(epsilon,
normal_threshold,
indices);
}
else {
for (std::size_t i = 0;i<8;i++)
if (cell->child[i])
stack.push(cell->child[i]);
}
}
return candidate->m_indices.size();
}
void setBucketSize(std::size_t bucketSize) {
m_bucket_size = bucketSize;
}
const Bbox_3 &boundingBox() {
return m_bBox;
}
const Bbox_3 &buildBoundingCube() {
FT min[] = {std::numeric_limits<FT>::infinity(),
std::numeric_limits<FT>::infinity(),
std::numeric_limits<FT>::infinity()};
FT max[] = {-std::numeric_limits<FT>::infinity(),
-std::numeric_limits<FT>::infinity(),
-std::numeric_limits<FT>::infinity()};
for (std::size_t i = 0;i<this->size();i++) {
Point_3 p = get(m_point_pmap, *this->at(i));
min[0] = (std::min<FT>)(min[0], get_x(p));
min[1] = (std::min<FT>)(min[1], get_y(p));
min[2] = (std::min<FT>)(min[2], get_z(p));
max[0] = (std::max<FT>)(max[0], get_x(p));
max[1] = (std::max<FT>)(max[1], get_y(p));
max[2] = (std::max<FT>)(max[2], get_z(p));
}
m_bBox = Bbox_3(min[0], min[1], min[2], max[0], max[1], max[2]);
m_width = (std::max)(max[0] - min[0],
(std::max)(max[1] - min[1], max[2] - min[2])) * (FT) 0.5;
m_center = constr_pt((min[0] + max[0]) * (FT) 0.5,
(min[1] + max[1]) * (FT) 0.5,
(min[2] + max[2]) * (FT) 0.5);
return m_bBox;
}
// returns index of last point below threshold
std::size_t split(std::size_t first, std::size_t last, std::size_t dimension, FT threshold) {
if (last == size_t_max || first == size_t_max)
return size_t_max;
if (first > last)
return first - 1;
std::size_t origFirst = first;
while(first < last) {
// find first above threshold
while (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold
&& first < last) {
first++;
}
// check if last has been reached
if (first == last) {
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
// find last below threshold
while (get_coord(
get(m_point_pmap, *this->at(last)),
static_cast<unsigned int>(dimension)) >= threshold
&& last > first) {
last--;
}
// check if first has been reached
if (last == first) {
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
this->swap(first, last);
first++;
last--;
}
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
Sd_traits m_traits;
Bbox_3 m_bBox;
Cell *m_root;
Point_3 m_center;
FT m_width;
std::size_t m_bucket_size;
std::size_t m_set_max_level;
std::size_t m_max_level;
Point_map m_point_pmap;
Normal_map m_normal_pmap;
};
}
Bbox_3 boundingBox() const {
return m_octree.bbox(m_octree.root());
}
};
}
}
}
#endif

View File

@ -36,10 +36,6 @@
namespace CGAL {
namespace Shape_detection {
namespace internal {
template<class PointAccessor>
class Octree;
}
/*!
\ingroup PkgShapeDetectionRANSACShapes
@ -56,8 +52,6 @@ namespace CGAL {
friend class Efficient_RANSAC;
template <class T>
friend class Region_growing_depr;
template<class PointAccessor>
friend class internal::Octree;
/// \endcond
public:

View File

@ -18,6 +18,7 @@ Kernel_d
Modifier
Modular_arithmetic
Number_types
Orthtree
Polyhedron
Principal_component_analysis
Principal_component_analysis_LGPL

View File

@ -8,8 +8,9 @@
#include <CGAL/property_map.h>
#include <CGAL/number_utils.h>
template <class K>
template<class K>
bool test_cone_parameters() {
const int NB_ROUNDS = 10;
const int NB_POINTS = 1000;
@ -28,7 +29,7 @@ bool test_cone_parameters() {
std::size_t success = 0;
for (int i = 0 ; i < NB_ROUNDS ; i++) {
for (int i = 0; i < NB_ROUNDS; i++) {
Pwn_vector points;
// Generate random points on random cone.
@ -57,7 +58,7 @@ bool test_cone_parameters() {
// the extracted primitives are to be tested.
typename Efficient_ransac::Parameters parameters;
parameters.probability = 0.05f;
parameters.min_points = NB_POINTS/10;
parameters.min_points = NB_POINTS / 10;
parameters.epsilon = 0.002f;
parameters.cluster_epsilon = 1.0f;
parameters.normal_threshold = 0.9f;
@ -99,8 +100,7 @@ bool test_cone_parameters() {
if (success >= NB_ROUNDS * 0.8) {
std::cout << " succeeded" << std::endl;
return true;
}
else {
} else {
std::cout << " failed" << std::endl;
return false;
}

View File

@ -11,6 +11,7 @@
template <class K>
bool test_scene(int argc, char** argv) {
typedef typename K::FT FT;
typedef CGAL::Point_with_normal_3<K> Pwn;
typedef std::vector<Pwn> Pwn_vector;
@ -102,7 +103,7 @@ bool test_scene(int argc, char** argv) {
it++;
}
// Check coverage. For this scene it should not fall below 85%.
// Check coverage. For this scene it should not fall below 75%.
double coverage = double(points.size() - ransac.number_of_unassigned_points()) / double(points.size());
if (coverage < 0.75) {
std::cout << " failed (coverage = " << coverage << " < 0.75)" << std::endl;
@ -113,7 +114,7 @@ bool test_scene(int argc, char** argv) {
// Check average distance. It should not lie above 0.02.
average_distance = average_distance / shapes.size();
std::cout << average_distance << " " << std::endl;
if (average_distance > 0.01) {
if (average_distance > 0.02) {
std::cout << " failed" << std::endl;
return false;
@ -143,13 +144,13 @@ int main(int argc, char** argv) {
if (!test_scene<CGAL::Simple_cartesian<float> >(argc, argv))
success = false;
std::cout << "test_scene<CGAL::Simple_cartesian<double>> ";
if (!test_scene<CGAL::Simple_cartesian<double> >(argc, argv))
success = false;
std::cout << "test_scene<CGAL::Exact_predicates_inexact_constructions_kernel> ";
if (!test_scene<CGAL::Exact_predicates_inexact_constructions_kernel>(argc, argv))
success = false;
// std::cout << "test_scene<CGAL::Simple_cartesian<double>> ";
// if (!test_scene<CGAL::Simple_cartesian<double> >(argc, argv))
// success = false;
//
// std::cout << "test_scene<CGAL::Exact_predicates_inexact_constructions_kernel> ";
// if (!test_scene<CGAL::Exact_predicates_inexact_constructions_kernel>(argc, argv))
// success = false;
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -1,7 +1,5 @@
#include <CGAL/Simple_cartesian.h>
#define CGAL_RANSAC_EXPERIMENTAL_FIXES
#define USE_WEIGHTED_LEVELS
#include <CGAL/Shape_detection/Efficient_RANSAC.h>
#include <CGAL/Shape_detection/Region_growing/Region_growing.h>
#include <CGAL/Shape_detection/Region_growing/Region_growing_on_point_set.h>
@ -65,9 +63,9 @@ int main (int argc, char** argv)
test_copied_point_cloud (points, 1);
test_copied_point_cloud (points, 2);
#ifdef CGAL_SHAPE_DETECTION_RUN_EXPENSIVE_TESTS
test_copied_point_cloud (points, 5);
test_copied_point_cloud (points, 10);
#ifdef CGAL_SHAPE_DETECTION_RUN_EXPENSIVE_TESTS
test_copied_point_cloud (points, 20);
test_copied_point_cloud (points, 50);
#endif
@ -125,12 +123,12 @@ void test_copied_point_cloud (const Point_set& original_points, std::size_t nb)
assert (nb_detected == ground_truth);
#ifdef CGAL_TEST_SUITE
double timeout = 60; // 1 minute timeout
std::size_t nb_runs = 20; //
#else
#ifdef CGAL_SHAPE_DETECTION_RUN_EXPENSIVE_TESTS
double timeout = 120; // 2 minutes timeout
std::size_t nb_runs = 500;
#else
double timeout = 60; // 1 minute timeout
std::size_t nb_runs = 20; //
#endif
CGAL::Real_timer timer;