adaptation of Orthtree interface to allow OrthtreeTraits without data

adding Orthtree_traits_without_data
template specializations for Property_array, Property_array_handle and Property_container to allow for void as data type
extending test_octree_copy_move_constructors to include traits without data
This commit is contained in:
Sven Oesau 2024-02-13 12:33:48 +01:00
parent 8f106bf8bb
commit 009791f4f8
5 changed files with 281 additions and 36 deletions

View File

@ -42,9 +42,16 @@
#include <queue> #include <queue>
#include <vector> #include <vector>
#include <math.h> #include <math.h>
#include <utility>
#include <boost/mpl/has_xxx.hpp>
namespace CGAL { namespace CGAL {
namespace internal {
BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data)
}
/*! /*!
\ingroup PkgOrthtreeRef \ingroup PkgOrthtreeRef
@ -65,13 +72,26 @@ namespace CGAL {
*/ */
template <typename GeomTraits> template <typename GeomTraits>
class Orthtree { class Orthtree {
private:
template<class A, class T>
struct Node_data_selector {};
template<class A>
struct Node_data_selector<A, boost::mpl::bool_<true>> {
using type = typename A::Node_data;
};
template<class A>
struct Node_data_selector<A, boost::mpl::bool_<false>> {
using type = void;
};
public: public:
/// \name Template Types /// \name Template Types
/// @{ /// @{
using Traits = GeomTraits; ///< Geometry traits using Traits = GeomTraits; ///< Geometry traits
/// @} /// @}
using Has_data = boost::mpl::bool_<internal::has_Node_data<GeomTraits>::value>;
/// \name Traits Types /// \name Traits Types
/// @{ /// @{
@ -84,7 +104,7 @@ public:
using Adjacency = typename Traits::Adjacency; ///< Adjacency type. using Adjacency = typename Traits::Adjacency; ///< Adjacency type.
using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0. using Node_index = typename Traits::Node_index; ///< Index of a given node in the tree; the root always has index 0.
using Node_data = typename Traits::Node_data; using Node_data = typename Node_data_selector<Traits, Has_data>::type;
/// @} /// @}
@ -153,16 +173,16 @@ private: // data members :
using Node_property_container = Properties::Experimental::Property_container<Node_index>; using Node_property_container = Properties::Experimental::Property_container<Node_index>;
template <typename T> template <typename T>
using Property_array = typename Properties::Experimental::Property_container<Node_index>::template Array<T>; using Property_array = typename Properties::Experimental::Property_array_handle<Node_index, T>;
Traits m_traits; /* the tree traits */ Traits m_traits; /* the tree traits */
Node_property_container m_node_properties; Node_property_container m_node_properties;
Property_array<Node_data>& m_node_contents; Property_array<Node_data> m_node_contents;
Property_array<std::uint8_t>& m_node_depths; Property_array<std::uint8_t> m_node_depths;
Property_array<Global_coordinates>& m_node_coordinates; Property_array<Global_coordinates> m_node_coordinates;
Property_array<std::optional<Node_index>>& m_node_parents; Property_array<std::optional<Node_index>> m_node_parents;
Property_array<std::optional<Node_index>>& m_node_children; Property_array<std::optional<Node_index>> m_node_children;
using Bbox_dimensions = std::array<FT, dimension>; using Bbox_dimensions = std::array<FT, dimension>;
Bbox m_bbox; Bbox m_bbox;
@ -170,6 +190,18 @@ private: // data members :
Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */
template<class T>
void init_data();
template<>
void init_data<boost::mpl::bool_<true>>() {
data(root()) = m_traits.construct_root_node_contents_object()();
}
template<>
void init_data<boost::mpl::bool_<false>>() {
}
public: public:
/// \name Constructor /// \name Constructor
@ -211,7 +243,8 @@ public:
} }
// save orthtree attributes // save orthtree attributes
m_side_per_depth.push_back(size); m_side_per_depth.push_back(size);
data(root()) = m_traits.construct_root_node_contents_object()();
init_data<Has_data>();
} }
/// @} /// @}
@ -695,14 +728,16 @@ public:
/*! /*!
\brief retrieves a reference to the Node_data associated with the node specified by `n`. \brief retrieves a reference to the Node_data associated with the node specified by `n`.
*/ */
Node_data& data(Node_index n) { template<typename Dummy = Node_data>
auto data(Node_index n) -> std::enable_if_t<internal::has_Node_data<GeomTraits>::value, Dummy&>{
return m_node_contents[n]; return m_node_contents[n];
} }
/*! /*!
\brief retrieves a const reference to the Node_data associated with the node specified by `n`. \brief retrieves a const reference to the Node_data associated with the node specified by `n`.
*/ */
const Node_data& data(Node_index n) const { template<typename Dummy = Node_data>
auto data(Node_index n) const -> std::enable_if_t<internal::has_Node_data<GeomTraits>::value, const Dummy&> {
return m_node_contents[n]; return m_node_contents[n];
} }
@ -930,7 +965,7 @@ public:
Point center = barycenter(n); Point center = barycenter(n);
// Add the node's contents to its children // Add the node's contents to its children
m_traits.distribute_node_contents_object()(n, *this, center); distribute_node_contents<Has_data>(n, center);
} }
/*! /*!
@ -1138,6 +1173,17 @@ private: // functions :
return output; return output;
} }
template<class T>
void distribute_node_contents(Node_index n, const Point& center);
template<>
void distribute_node_contents<boost::mpl::bool_<true>>(Node_index n, const Point& center) {
m_traits.distribute_node_contents_object()(n, *this, center);
}
template<>
void distribute_node_contents<boost::mpl::bool_<false>>(Node_index n, const Point& center) {}
public: public:
/// \cond SKIP_IN_MANUAL /// \cond SKIP_IN_MANUAL

View File

@ -31,7 +31,7 @@ namespace Orthtrees {
split if it contains more than a certain number of items. split if it contains more than a certain number of items.
\warning This split predicate is only appropriate for trees with traits classes where \warning This split predicate is only appropriate for trees with traits classes where
`Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. `Node_data` is present and a model of `Range`. `RandomAccessRange` is suggested for performance.
*/ */
class Maximum_number_of_inliers { class Maximum_number_of_inliers {
@ -96,7 +96,7 @@ public:
than `bucket_size`, it is not split. than `bucket_size`, it is not split.
\warning This split predicate is only appropriate for trees with traits classes where \warning This split predicate is only appropriate for trees with traits classes where
`Node_data` is a model of `Range`. `RandomAccessRange` is suggested for performance. `Node_data` is present and a model of `Range`. `RandomAccessRange` is suggested for performance.
*/ */
class Maximum_depth_and_maximum_number_of_inliers { class Maximum_depth_and_maximum_number_of_inliers {

View File

@ -0,0 +1,61 @@
// Copyright (c) 2023 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) : Sven Oesau
#ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H
#define ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H
#include <CGAL/license/Orthtree.h>
#include <CGAL/Dimension.h>
#include <CGAL/Bbox_2.h>
#include <CGAL/Point_set_2.h>
#include <CGAL/Orthtree/Cartesian_ranges.h>
#include <CGAL/Orthtree_traits_base.h>
namespace CGAL {
/*!
\ingroup PkgOrthtreeTraits
Traits class for defining an orthtree using the class `CGAL::Orthtree` without storing data in the nodes.
\tparam GeomTraits model of `Kernel`.
\tparam dimension the dimension of the ambient Euclidean space.
\cgalModels{OrthtreeTraits}
\sa `CGAL::Octree`
\sa `CGAL::Quadtree`
\sa `CGAL::Orthtree_traits_base<GeomTraits, DimensionTag>`
*/
template <typename GeomTraits, int dimension>
struct Orthtree_traits_without_data: public Orthtree_traits_base<GeomTraits, dimension> {
public:
using Base = Orthtree_traits_base<GeomTraits, dimension>;
using Self = Orthtree_traits_without_data<GeomTraits, dimension>;
using Tree = Orthtree<Self>;
using Node_index = typename Base::Node_index;
Orthtree_traits_without_data() {}
auto construct_root_node_bbox_object() const {
return [&]() -> typename Self::Bbox_d {
return {std::apply(Self::construct_point_d_object(), std::array<typename Self::FT, Self::dimension>{-1.0, -1.0, -1.0}),
std::apply(Self::construct_point_d_object(), std::array<typename Self::FT, Self::dimension>{1.0, 1.0, 1.0})};
};
}
};
}
#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H

View File

@ -9,44 +9,56 @@
#include <CGAL/point_generators_3.h> #include <CGAL/point_generators_3.h>
#include <CGAL/Simple_cartesian.h> #include <CGAL/Simple_cartesian.h>
#include <CGAL/Orthtree_traits_without_data.h>
#include <CGAL/Orthtree/Split_predicates.h>
using Kernel = CGAL::Simple_cartesian<double>; using Kernel = CGAL::Simple_cartesian<double>;
using Point = Kernel::Point_3; using Point = Kernel::Point_3;
using FT = Kernel::FT; using FT = Kernel::FT;
using Point_set = CGAL::Point_set_3<Point>; using Point_set = CGAL::Point_set_3<Point>;
using Octree = CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>; using Octree = CGAL::Octree<Kernel, Point_set, typename Point_set::Point_map>;
using Octree_without_data = CGAL::Orthtree<CGAL::Orthtree_traits_without_data<Kernel, 3>>;
int main(void) template<typename Tree>
int test(Tree &tree)
{ {
std::size_t nb_pts = 100; assert (tree.is_leaf(tree.root())); // tree is not refined yet
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()}); Tree copy1 (tree);
assert (base.is_leaf(base.root())); // base is not refined yet
Octree copy1 (base);
assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either
assert (base == copy1); // base should be equal to copy1 assert (tree == copy1); // tree should be equal to copy1
base.refine(); tree.refine(CGAL::Orthtrees::Maximum_depth(5));
assert (!base.is_leaf(base.root())); // base is now refined assert (!tree.is_leaf(tree.root())); // tree is now refined
assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined
assert (base != copy1); // base should be different from copy1 assert (tree != copy1); // tree should be different from copy1
Octree copy2 (base); Tree copy2 (tree);
assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined
assert (base == copy2); // base should be equal to copy2 assert (tree == copy2); // tree should be equal to copy2
Octree move (std::move(base)); Tree move (std::move(tree));
assert (!move.is_leaf(move.root())); // move should be refined assert (!move.is_leaf(move.root())); // move should be refined
// fixme: my linter isn't happy about use-after-move
assert (base.is_leaf(base.root())); // base should be back to init state (unrefined) assert (tree.is_leaf(tree.root())); // tree should be back to init state (unrefined)
assert (copy1.is_leaf(copy1.root())); // copy1 still unaffected and still unrefined assert (copy1.is_leaf(copy1.root())); // copy1 still unaffected and still unrefined
assert (!copy2.is_leaf(copy2.root())); // copy2 unaffected by move and still refined assert (!copy2.is_leaf(copy2.root())); // copy2 unaffected by move and still refined
assert (move == copy2); // move should be equal to copy2 assert (move == copy2); // move should be equal to copy2
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
void main() {
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() });
test(base);
Octree_without_data base2({});
test(base2);
}

View File

@ -191,6 +191,43 @@ public:
}; };
template <typename Index>
class Property_array<Index, void> : public Property_array_base<Index> {
public:
Property_array() {}
virtual std::shared_ptr<Property_array_base<Index>> empty_clone(const std::vector<bool>& active_indices) {
return std::make_shared<Property_array<Index, void>>();
}
virtual std::shared_ptr<Property_array_base<Index>> clone(const std::vector<bool>& active_indices) {
return std::make_shared<Property_array<Index, void>>();
}
virtual void copy(const Property_array_base<Index>& other) {}
// desactived as MSVC 2017 as an issue with that but it is not currently used.
#if 0
virtual void move(Property_array_base<Index>&& other) = 0;
#endif
virtual void append(const Property_array_base<Index>& other) {}
virtual void reserve(std::size_t n) {}
virtual void shrink_to_fit() {}
virtual void swap(Index a, Index b) {}
virtual void reset(Index i) {}
virtual const std::type_info& type() const {
return typeid(void);
}
virtual void transfer_from(const Property_array_base<Index>& other_base, Index other_index, Index this_index) {}
};
// todo: property maps/array handles should go in their own file // todo: property maps/array handles should go in their own file
// todo: add const/read-only handle // todo: add const/read-only handle
@ -243,6 +280,56 @@ public:
}; };
// void specialization
template <typename Index>
class Property_array_handle<Index, void> {
std::reference_wrapper<Property_array<Index, void>> m_array;
std::vector<int> m_dummy;
public:
// Necessary for use as a boost::property_type
using key_type = Index;
using value_type = void;
using reference = void;
using const_reference = void;
using category = boost::lvalue_property_map_tag;
using iterator = typename std::vector<int>::iterator;
using const_iterator = typename std::vector<int>::const_iterator;
Property_array_handle(Property_array<Index, void>& array) : m_array(array) {}
[[nodiscard]] std::size_t size() const { return 0; }
[[nodiscard]] std::size_t capacity() const { return 0; }
Property_array<Index, void>& array() const { return m_array.get(); }
// todo: This might not be needed, if the other operator[] is made const
const_reference operator[](Index i) const { }
reference operator[](Index i) { }
// todo: maybe these can be const, in an lvalue property map?
iterator begin() { return m_dummy.begin(); }
iterator end() { return m_dummy.end(); }
const_iterator begin() const { return m_dummy.begin(); }
const_iterator end() const { return m_dummy.end(); }
bool operator==(const Property_array_handle<Index, void>& other) const { return true; }
bool operator!=(const Property_array_handle<Index, void>& other) const { return false; }
inline friend reference get(Property_array_handle<Index, void> p, const Index& i) {}
//inline friend void put(Property_array_handle<Index, void> p, const Index& i, const T& v) {}
};
template <typename Index = std::size_t> template <typename Index = std::size_t>
class Property_container { class Property_container {
@ -318,7 +405,46 @@ public:
template <typename T> template <typename T>
std::pair<std::reference_wrapper<Property_array<Index, T>>, bool> std::pair<std::reference_wrapper<Property_array<Index, T>>, bool>
get_or_add_property(const std::string& name, const T default_value = T()) { get_or_add_property(const std::string& name) {
auto range = m_properties.equal_range(name);
for (auto it = range.first; it != range.second; it++) {
Property_array<Index, T>* typed_array_ptr = dynamic_cast<Property_array<Index, T>*>(it->second.get());
if (typed_array_ptr != nullptr)
return { {*typed_array_ptr}, false };
}
auto it = m_properties.emplace(
name,
std::make_shared<Property_array<Index, T>>(
m_active_indices,
T()
)
);
return { {*dynamic_cast<Property_array<Index, T>*>(it->second.get())}, true };
}
template<>
std::pair<std::reference_wrapper<Property_array<Index, void>>, bool>
get_or_add_property(const std::string& name) {
auto range = m_properties.equal_range(name);
for (auto it = range.first; it != range.second; it++) {
Property_array<Index, void>* typed_array_ptr = dynamic_cast<Property_array<Index, void>*>(it->second.get());
if (typed_array_ptr != nullptr)
return { {*typed_array_ptr}, false };
}
auto it = m_properties.emplace(
name,
std::make_shared<Property_array<Index, void>>()
);
return { {*dynamic_cast<Property_array<Index, void>*>(it->second.get())}, true };
}
template <typename T>
std::pair<std::reference_wrapper<Property_array<Index, T>>, bool>
get_or_add_property(const std::string& name, const T default_value) {
auto range = m_properties.equal_range(name); auto range = m_properties.equal_range(name);
for (auto it = range.first; it != range.second; it++) { for (auto it = range.first; it != range.second; it++) {
Property_array<Index, T>* typed_array_ptr = dynamic_cast<Property_array<Index, T>*>(it->second.get()); Property_array<Index, T>* typed_array_ptr = dynamic_cast<Property_array<Index, T>*>(it->second.get());
@ -334,7 +460,7 @@ public:
) )
); );
return {{*dynamic_cast<Property_array<Index, T>*>(it->second.get())}, true}; return { {*dynamic_cast<Property_array<Index, T>*>(it->second.get())}, true };
} }
template <typename T> template <typename T>