mirror of https://github.com/CGAL/cgal
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:
parent
8f106bf8bb
commit
009791f4f8
|
|
@ -42,9 +42,16 @@
|
|||
#include <queue>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/mpl/has_xxx.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace internal {
|
||||
BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data)
|
||||
}
|
||||
|
||||
/*!
|
||||
\ingroup PkgOrthtreeRef
|
||||
|
||||
|
|
@ -65,13 +72,26 @@ namespace CGAL {
|
|||
*/
|
||||
template <typename GeomTraits>
|
||||
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:
|
||||
|
||||
/// \name Template Types
|
||||
/// @{
|
||||
using Traits = GeomTraits; ///< Geometry traits
|
||||
/// @}
|
||||
using Has_data = boost::mpl::bool_<internal::has_Node_data<GeomTraits>::value>;
|
||||
|
||||
/// \name Traits Types
|
||||
/// @{
|
||||
|
|
@ -84,7 +104,7 @@ public:
|
|||
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_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>;
|
||||
|
||||
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 */
|
||||
|
||||
Node_property_container m_node_properties;
|
||||
Property_array<Node_data>& m_node_contents;
|
||||
Property_array<std::uint8_t>& m_node_depths;
|
||||
Property_array<Global_coordinates>& m_node_coordinates;
|
||||
Property_array<std::optional<Node_index>>& m_node_parents;
|
||||
Property_array<std::optional<Node_index>>& m_node_children;
|
||||
Property_array<Node_data> m_node_contents;
|
||||
Property_array<std::uint8_t> m_node_depths;
|
||||
Property_array<Global_coordinates> m_node_coordinates;
|
||||
Property_array<std::optional<Node_index>> m_node_parents;
|
||||
Property_array<std::optional<Node_index>> m_node_children;
|
||||
|
||||
using Bbox_dimensions = std::array<FT, dimension>;
|
||||
Bbox m_bbox;
|
||||
|
|
@ -170,6 +190,18 @@ private: // data members :
|
|||
|
||||
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:
|
||||
|
||||
/// \name Constructor
|
||||
|
|
@ -211,7 +243,8 @@ public:
|
|||
}
|
||||
// save orthtree attributes
|
||||
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`.
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
/*!
|
||||
\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];
|
||||
}
|
||||
|
||||
|
|
@ -930,7 +965,7 @@ public:
|
|||
Point center = barycenter(n);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace Orthtrees {
|
|||
split if it contains more than a certain number of items.
|
||||
|
||||
\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 {
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ public:
|
|||
than `bucket_size`, it is not split.
|
||||
|
||||
\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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -9,14 +9,46 @@
|
|||
#include <CGAL/point_generators_3.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 Point = Kernel::Point_3;
|
||||
using FT = Kernel::FT;
|
||||
using Point_set = CGAL::Point_set_3<Point>;
|
||||
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)
|
||||
{
|
||||
assert (tree.is_leaf(tree.root())); // tree is not refined yet
|
||||
|
||||
Tree copy1 (tree);
|
||||
assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either
|
||||
assert (tree == copy1); // tree should be equal to copy1
|
||||
|
||||
tree.refine(CGAL::Orthtrees::Maximum_depth(5));
|
||||
assert (!tree.is_leaf(tree.root())); // tree is now refined
|
||||
assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined
|
||||
assert (tree != copy1); // tree should be different from copy1
|
||||
|
||||
Tree copy2 (tree);
|
||||
assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined
|
||||
assert (tree == copy2); // tree should be equal to copy2
|
||||
|
||||
Tree move (std::move(tree));
|
||||
assert (!move.is_leaf(move.root())); // move should be refined
|
||||
|
||||
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 (!copy2.is_leaf(copy2.root())); // copy2 unaffected by move and still refined
|
||||
assert (move == copy2); // move should be equal to copy2
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void main() {
|
||||
std::size_t nb_pts = 100;
|
||||
Point_set points;
|
||||
CGAL::Random_points_in_cube_3<Point> generator;
|
||||
|
|
@ -24,29 +56,9 @@ int main(void)
|
|||
for (std::size_t i = 0; i < nb_pts; ++i)
|
||||
points.insert(*(generator++));
|
||||
|
||||
Octree base ({points, points.point_map()});
|
||||
assert (base.is_leaf(base.root())); // base is not refined yet
|
||||
Octree base({ points, points.point_map() });
|
||||
test(base);
|
||||
|
||||
Octree copy1 (base);
|
||||
assert (copy1.is_leaf(copy1.root())); // copy1 is thus not refined either
|
||||
assert (base == copy1); // base should be equal to copy1
|
||||
|
||||
base.refine();
|
||||
assert (!base.is_leaf(base.root())); // base is now refined
|
||||
assert (copy1.is_leaf(copy1.root())); // copy1 should be unaffected and still unrefined
|
||||
assert (base != copy1); // base should be different from copy1
|
||||
|
||||
Octree copy2 (base);
|
||||
assert (!copy2.is_leaf(copy2.root())); // copy2 should be refined
|
||||
assert (base == copy2); // base should be equal to copy2
|
||||
|
||||
Octree move (std::move(base));
|
||||
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 (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 (move == copy2); // move should be equal to copy2
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
Octree_without_data base2({});
|
||||
test(base2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: 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>
|
||||
class Property_container {
|
||||
|
||||
|
|
@ -318,7 +405,46 @@ public:
|
|||
|
||||
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 = 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);
|
||||
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());
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue