From 009791f4f8700279a1ea222f78db29621fe3cf2e Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Tue, 13 Feb 2024 12:33:48 +0100 Subject: [PATCH] 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 --- Orthtree/include/CGAL/Orthtree.h | 70 ++++++++-- .../include/CGAL/Orthtree/Split_predicates.h | 4 +- .../CGAL/Orthtree_traits_without_data.h | 61 ++++++++ .../test_octree_copy_move_constructors.cpp | 52 ++++--- .../include/CGAL/Property_container.h | 130 +++++++++++++++++- 5 files changed, 281 insertions(+), 36 deletions(-) create mode 100644 Orthtree/include/CGAL/Orthtree_traits_without_data.h diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index fe5530eeecc..0f1d9db0be0 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -42,9 +42,16 @@ #include #include #include +#include + +#include namespace CGAL { +namespace internal { +BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) +} + /*! \ingroup PkgOrthtreeRef @@ -65,13 +72,26 @@ namespace CGAL { */ template class Orthtree { +private: + template + struct Node_data_selector {}; + + template + struct Node_data_selector> { + using type = typename A::Node_data; + }; + + template + struct Node_data_selector> { + using type = void; + }; public: - /// \name Template Types /// @{ using Traits = GeomTraits; ///< Geometry traits /// @} + using Has_data = boost::mpl::bool_::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::type; /// @} @@ -153,16 +173,16 @@ private: // data members : using Node_property_container = Properties::Experimental::Property_container; template - using Property_array = typename Properties::Experimental::Property_container::template Array; + using Property_array = typename Properties::Experimental::Property_array_handle; Traits m_traits; /* the tree traits */ Node_property_container m_node_properties; - Property_array& m_node_contents; - Property_array& m_node_depths; - Property_array& m_node_coordinates; - Property_array>& m_node_parents; - Property_array>& m_node_children; + Property_array m_node_contents; + Property_array m_node_depths; + Property_array m_node_coordinates; + Property_array> m_node_parents; + Property_array> m_node_children; using Bbox_dimensions = std::array; Bbox m_bbox; @@ -170,6 +190,18 @@ private: // data members : Cartesian_ranges cartesian_range; /* a helper to easily iterate over coordinates of points */ + template + void init_data(); + + template<> + void init_data>() { + data(root()) = m_traits.construct_root_node_contents_object()(); + } + + template<> + void init_data>() { + } + 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(); } /// @} @@ -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 + auto data(Node_index n) -> std::enable_if_t::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 + auto data(Node_index n) const -> std::enable_if_t::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(n, center); } /*! @@ -1138,6 +1173,17 @@ private: // functions : return output; } + template + void distribute_node_contents(Node_index n, const Point& center); + + template<> + void distribute_node_contents>(Node_index n, const Point& center) { + m_traits.distribute_node_contents_object()(n, *this, center); + } + + template<> + void distribute_node_contents>(Node_index n, const Point& center) {} + public: /// \cond SKIP_IN_MANUAL diff --git a/Orthtree/include/CGAL/Orthtree/Split_predicates.h b/Orthtree/include/CGAL/Orthtree/Split_predicates.h index bc04c4b815e..cf3ad522148 100644 --- a/Orthtree/include/CGAL/Orthtree/Split_predicates.h +++ b/Orthtree/include/CGAL/Orthtree/Split_predicates.h @@ -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 { diff --git a/Orthtree/include/CGAL/Orthtree_traits_without_data.h b/Orthtree/include/CGAL/Orthtree_traits_without_data.h new file mode 100644 index 00000000000..608c058407f --- /dev/null +++ b/Orthtree/include/CGAL/Orthtree_traits_without_data.h @@ -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 + +#include +#include +#include +#include + +#include + +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` +*/ +template +struct Orthtree_traits_without_data: public Orthtree_traits_base { +public: + using Base = Orthtree_traits_base; + using Self = Orthtree_traits_without_data; + using Tree = Orthtree; + + 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{-1.0, -1.0, -1.0}), + std::apply(Self::construct_point_d_object(), std::array{1.0, 1.0, 1.0})}; + }; + } +}; + +} + + +#endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_WITHOUT_DATA_H diff --git a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp index 8a76ac514bb..3716036ede2 100644 --- a/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp +++ b/Orthtree/test/Orthtree/test_octree_copy_move_constructors.cpp @@ -9,44 +9,56 @@ #include #include +#include +#include + using Kernel = CGAL::Simple_cartesian; using Point = Kernel::Point_3; using FT = Kernel::FT; using Point_set = CGAL::Point_set_3; using Octree = CGAL::Octree; +using Octree_without_data = CGAL::Orthtree>; -int main(void) +template +int test(Tree &tree) { - std::size_t nb_pts = 100; - Point_set points; - CGAL::Random_points_in_cube_3 generator; - points.reserve(nb_pts); - for (std::size_t i = 0; i < nb_pts; ++i) - points.insert(*(generator++)); + assert (tree.is_leaf(tree.root())); // tree is not refined yet - Octree base ({points, points.point_map()}); - assert (base.is_leaf(base.root())); // base is not refined yet - - Octree copy1 (base); + Tree copy1 (tree); 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(); - assert (!base.is_leaf(base.root())); // base is now refined + 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 (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 (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 - // 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 (!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 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); +} diff --git a/Property_map/include/CGAL/Property_container.h b/Property_map/include/CGAL/Property_container.h index 739f652b318..16a44ff1dc2 100644 --- a/Property_map/include/CGAL/Property_container.h +++ b/Property_map/include/CGAL/Property_container.h @@ -191,6 +191,43 @@ public: }; +template +class Property_array : public Property_array_base { +public: + Property_array() {} + + virtual std::shared_ptr> empty_clone(const std::vector& active_indices) { + return std::make_shared>(); + } + + virtual std::shared_ptr> clone(const std::vector& active_indices) { + return std::make_shared>(); + } + + virtual void copy(const Property_array_base& other) {} + + // desactived as MSVC 2017 as an issue with that but it is not currently used. +#if 0 + virtual void move(Property_array_base&& other) = 0; +#endif + + virtual void append(const Property_array_base& 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& 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 +class Property_array_handle { + + std::reference_wrapper> m_array; + std::vector 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::iterator; + using const_iterator = typename std::vector::const_iterator; + + Property_array_handle(Property_array& array) : m_array(array) {} + + [[nodiscard]] std::size_t size() const { return 0; } + + [[nodiscard]] std::size_t capacity() const { return 0; } + + Property_array& 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& other) const { return true; } + + bool operator!=(const Property_array_handle& other) const { return false; } + + inline friend reference get(Property_array_handle p, const Index& i) {} + + //inline friend void put(Property_array_handle p, const Index& i, const T& v) {} +}; + template class Property_container { @@ -318,7 +405,46 @@ public: template std::pair>, 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* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return { {*typed_array_ptr}, false }; + } + + auto it = m_properties.emplace( + name, + std::make_shared>( + m_active_indices, + T() + ) + ); + + return { {*dynamic_cast*>(it->second.get())}, true }; + } + + template<> + std::pair>, 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* typed_array_ptr = dynamic_cast*>(it->second.get()); + if (typed_array_ptr != nullptr) + return { {*typed_array_ptr}, false }; + } + + auto it = m_properties.emplace( + name, + std::make_shared>() + ); + + return { {*dynamic_cast*>(it->second.get())}, true }; + } + + template + std::pair>, 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* typed_array_ptr = dynamic_cast*>(it->second.get()); @@ -334,7 +460,7 @@ public: ) ); - return {{*dynamic_cast*>(it->second.get())}, true}; + return { {*dynamic_cast*>(it->second.get())}, true }; } template