// 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) : Jackson Campolattaro #ifndef ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H #define ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H #include #include #include #include #include namespace CGAL { /*! \ingroup PkgOrthtreeTraits The class `Orthtree_traits_point_3` can be used as a template parameter of the `Orthtree` class. \tparam GeomTraits model of `Kernel`. \tparam Point_set must be a model of range whose value type is the key type of `Point_map` \tparam Point_map must be a model of `ReadablePropertyMap` whose value type is `GeomTraits::Traits::Point_d` \cgalModels `OrthtreeTraits` \sa `CGAL::Octree` \sa `CGAL::Orthtree_traits_2` \sa `CGAL::Orthtree_traits_d` */ template < typename GeomTraits, typename Point_set, typename Point_map = Identity_property_map > struct Orthtree_traits_point_3 { public: /// \name Types /// @{ using Self = Orthtree_traits_point_3; using Dimension = Dimension_tag<3>; using Bbox_d = Bbox_3; using FT = typename GeomTraits::FT; using Point_d = typename GeomTraits::Point_3; using Sphere_d = typename GeomTraits::Sphere_3; using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_3; using Array = std::array; // todo: This should have a more descriptive name // todo: looking for better names using Node_data = boost::iterator_range; using Node_data_element = typename std::iterator_traits::value_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 /// @} Orthtree_traits_point_3( Point_set& point_set, Point_map point_map = Point_map() ) : m_point_set(point_set), m_point_map(point_map) {} /// \name Operations /// The user shouldn't need to define these themselves /// @{ /*! 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(); } std::pair root_node_bbox() const { Array bbox_min; Array bbox_max; Orthtrees::internal::Cartesian_ranges cartesian_range; // init bbox with first values found { const Point_d& point = get(m_point_map, *(m_point_set.begin())); std::size_t i = 0; for (const FT& x: cartesian_range(point)) { bbox_min[i] = x; bbox_max[i] = x; ++i; } } // Expand bbox to contain all points for (const auto& p: m_point_set) { const Point_d& point = get(m_point_map, p); std::size_t i = 0; for (const FT& x: cartesian_range(point)) { bbox_min[i] = (std::min)(x, bbox_min[i]); bbox_max[i] = (std::max)(x, bbox_max[i]); ++i; } } return {bbox_min, bbox_max}; } Node_data root_node_contents() const { return {m_point_set.begin(), m_point_set.end()}; } template void distribute_node_contents(Node_index n, Tree& tree, const Point_d& center) { CGAL_precondition(!tree.is_leaf(n)); reassign_points(n, tree, center, tree.points(n)); } Point_d get_element(const Node_data_element& index) const { return get(m_point_map, index); } /// @} private: Point_set& m_point_set; Point_map m_point_map; template void reassign_points(Node_index n, Tree& tree, const Point_d& center, Node_data points, std::bitset coord = {}, std::size_t dimension = 0) { // Root case: reached the last dimension if (dimension == Dimension::value) { tree.points(tree.child(n, coord.to_ulong())) = points; return; } // Split the point collection around the center point on this dimension auto split_point = std::partition( points.begin(), points.end(), [&](const auto& p) -> bool { // This should be done with cartesian iterator, // but it seems complicated to do efficiently return (get(m_point_map, p)[int(dimension)] < center[int(dimension)]); } ); // Further subdivide the first side of the split std::bitset coord_left = coord; coord_left[dimension] = false; reassign_points(n, tree, center, {points.begin(), split_point}, coord_left, dimension + 1); // Further subdivide the second side of the split std::bitset coord_right = coord; coord_right[dimension] = true; reassign_points(n, tree, center, {split_point, points.end()}, coord_right, dimension + 1); } }; } #endif //ORTHTREE_TESTS_ORTHTREE_TRAITS_POINT_3_H