From a9cd49c95c8f4d0017f01135e048e7dfd19ed49e Mon Sep 17 00:00:00 2001 From: Sven Oesau Date: Thu, 22 Jan 2026 13:03:23 +0100 Subject: [PATCH] added Has_on_bounded_side_2::operator() for Circle_2 and Iso_rectangle_2 including static filter added intersected_nodes for disk/ball to Orhtree --- .../Static_filters/Has_on_bounded_side_2.h | 129 ++++++++++++++++++ Kernel_23/doc/Kernel_23/CGAL/Circle_2.h | 10 +- .../Concepts/FunctionObjectConcepts.h | 7 + Kernel_23/include/CGAL/Circle_2.h | 7 + .../include/CGAL/Kernel/function_objects.h | 26 ++++ .../include/CGAL/_test_cls_circle_2.h | 2 + Orthtree/include/CGAL/Orthtree.h | 29 +++- Orthtree/include/CGAL/Orthtree_traits_base.h | 10 ++ .../Orthtree/test_octree_intersecting.cpp | 31 ++++- 9 files changed, 243 insertions(+), 8 deletions(-) diff --git a/Filtered_kernel/include/CGAL/Filtered_kernel/internal/Static_filters/Has_on_bounded_side_2.h b/Filtered_kernel/include/CGAL/Filtered_kernel/internal/Static_filters/Has_on_bounded_side_2.h index 0f82baba641..9d3f89005a0 100644 --- a/Filtered_kernel/include/CGAL/Filtered_kernel/internal/Static_filters/Has_on_bounded_side_2.h +++ b/Filtered_kernel/include/CGAL/Filtered_kernel/internal/Static_filters/Has_on_bounded_side_2.h @@ -34,6 +34,7 @@ class Has_on_bounded_side_2 typedef typename K_base::Point_2 Point_2; typedef typename K_base::Circle_2 Circle_2; typedef typename K_base::Segment_2 Segment_2; + typedef typename K_base::Iso_rectangle_2 Iso_rectangle_2; typedef typename K_base::Has_on_bounded_side_2 Base; public: @@ -159,6 +160,134 @@ public: this->operator()(c, s); } + Boolean + operator()(const Circle_2& c, const Iso_rectangle_2& r) const + { + CGAL_BRANCH_PROFILER_3(std::string("semi-static failures/attempts/calls to : ") + + std::string(CGAL_PRETTY_FUNCTION), tmp); + + Get_approx get_approx; // Identity functor for all points + + double cx, cy, csr; + double rxmin = r.xmin(), rymin = r.ymin(), + rxmax = r.xmax(), rymax = r.ymax(); + + if (fit_in_double(get_approx(c.center()).x(), cx) && + fit_in_double(get_approx(c.center()).y(), cy) && + fit_in_double(c.squared_radius(), csr)) + { + CGAL_BRANCH_PROFILER_BRANCH_1(tmp); + + if ((csr < 1.11261183279326254436e-293) || (csr > 2.80889552322236673473e+306)) { + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + return Base::operator()(c, r); + } + double distance = 0; + double max1 = 0; + double double_tmp_result = 0; + double eps = 0; + if (cx < rxmin) + { + double bxmax_scx = rxmax - cx; + max1 = bxmax_scx; + + distance = square(bxmax_scx); + double_tmp_result = (distance - csr); + + if ((max1 < 3.33558365626356687717e-147) || (max1 > 1.67597599124282407923e+153)) { + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + return Base::operator()(c, r); + } + + eps = 1.99986535548615598560e-15 * (std::max)(csr, square(max1)); + + if (double_tmp_result > eps) { + return false; + } + } + else if (cx > rxmax) + { + double scx_bxmix = cx - rxmin; + max1 = scx_bxmix; + + distance = square(scx_bxmix); + double_tmp_result = (distance - csr); + + if ((max1 < 3.33558365626356687717e-147) || (max1 > 1.67597599124282407923e+153)) { + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + return Base::operator()(c, r); + } + + eps = 1.99986535548615598560e-15 * (std::max)(csr, square(max1)); + + if (double_tmp_result > eps) { + return false; + } + } + + + if (cy < rymin) + { + double bymax_scy = rymax - cy; + if (max1 < bymax_scy) { + max1 = bymax_scy; + } + + distance += square(bymax_scy); + double_tmp_result = (distance - csr); + + if ((max1 < 3.33558365626356687717e-147) || ((max1 > 1.67597599124282407923e+153))) { + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + return Base::operator()(c, r); + } + + eps = 1.99986535548615598560e-15 * (std::max)(csr, square(max1)); + + if (double_tmp_result > eps) { + return false; + } + } + else if (cy > rymax) + { + double scy_bymin = cy - rymin; + if (max1 < scy_bymin) { + max1 = scy_bymin; + } + distance += square(scy_bymin); + double_tmp_result = (distance - csr); + + if (((max1 < 3.33558365626356687717e-147)) || ((max1 > 1.67597599124282407923e+153))) { + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + return Base::operator()(c, r); + } + + eps = 1.99986535548615598560e-15 * (std::max)(csr, square(max1)); + + if (double_tmp_result > eps) { + return false; + } + } + + // double_tmp_result and eps were growing all the time + // no need to test for > eps as done earlier in at least one case + if (double_tmp_result < -eps) { + return true; + } + else { + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + return Base::operator()(c, r); + } + + CGAL_BRANCH_PROFILER_BRANCH_2(tmp); + } + return Base::operator()(c, r); + } + + Boolean + operator()(const Iso_rectangle_2& r, const Circle_2& c) const + { + this->operator()(c, r); + } }; // end class Has_on_bounded_side_2 diff --git a/Kernel_23/doc/Kernel_23/CGAL/Circle_2.h b/Kernel_23/doc/Kernel_23/CGAL/Circle_2.h index ad760615bef..a6400b0cd9f 100644 --- a/Kernel_23/doc/Kernel_23/CGAL/Circle_2.h +++ b/Kernel_23/doc/Kernel_23/CGAL/Circle_2.h @@ -165,8 +165,14 @@ bool has_on_boundary(const Point_2 &p) const; bool has_on_bounded_side(const Point_2 &p) const; /*! -returns whether the segment `s` lies on the bounded side of the circle `c`. The endpoints of the segment -are allowed to be on the boundary of the circle. +returns whether the iso_rectangle `i` lies on the bounded side of the circle `c`. The corners of `i` +are allowed to be on the boundary of `c`. +*/ +bool has_on_bounded_side(const Iso_rectangle_2& i) const; + +/*! +returns whether the segment `s` lies on the bounded side of the circle `c`. The endpoints of `s` +are allowed to be on the boundary of `c`. */ bool has_on_bounded_side(const Segment_2 &s) const; diff --git a/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h b/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h index 443500fe291..d89737cdfff 100644 --- a/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h +++ b/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h @@ -8099,6 +8099,13 @@ public: bool operator()(const Kernel::Circle_2&c, const Kernel::Segment_2&s); + /*! + returns true iff `i` lies on the bounded side of `c`. + The corner points of `i` are allowed to be on the boundary of `c`. + */ + bool operator()(const Kernel::Circle_2&c, + const Kernel::IsoRectangle_2&i); + /*! returns true iff `p` lies on the bounded side of `i`. */ diff --git a/Kernel_23/include/CGAL/Circle_2.h b/Kernel_23/include/CGAL/Circle_2.h index d6401d9810a..91565095019 100644 --- a/Kernel_23/include/CGAL/Circle_2.h +++ b/Kernel_23/include/CGAL/Circle_2.h @@ -32,6 +32,7 @@ class Circle_2 : public R_::Kernel_base::Circle_2 typedef typename R_::FT FT; typedef typename R_::Point_2 Point_2; typedef typename R_::Segment_2 Segment_2; + typedef typename R_::Iso_rectangle_2 Iso_rectangle_2; typedef typename R_::Kernel_base::Circle_2 RCircle_2; typedef typename R_::Aff_transformation_2 Aff_transformation_2; @@ -136,6 +137,12 @@ public: return bounded_side(p) == ON_BOUNDED_SIDE; } + typename R::Boolean + has_on_bounded_side(const Iso_rectangle_2& r) const + { + return R().has_on_bounded_side_2_object()(*this, r); + } + typename R::Boolean has_on_bounded_side(const Segment_2& s) const { diff --git a/Kernel_23/include/CGAL/Kernel/function_objects.h b/Kernel_23/include/CGAL/Kernel/function_objects.h index 8f956a5cf53..55d08ca5603 100644 --- a/Kernel_23/include/CGAL/Kernel/function_objects.h +++ b/Kernel_23/include/CGAL/Kernel/function_objects.h @@ -3599,6 +3599,32 @@ namespace CommonKernelFunctors { operator()( const Circle_2& c, const Point_2& p) const { return c.has_on_bounded_side(p); } + Boolean + operator()(const Circle_2& c, const Iso_rectangle_2 & r) const + { + typedef typename K::FT FT; + FT d = FT(0); + FT distance = FT(0); + + // x + d = (std::max)(square(c.center().x() - r.xmin()), square(c.center().x() - r.xmax())); + + if (certainly(d > c.squared_radius())) + return false; + + distance = d; + + // y + d = (std::max)(square(c.center().y() - r.ymin()), square(c.center().y() - r.ymax())); + + if (certainly(d > c.squared_radius())) + return false; + + distance += d; + + return (distance <= c.squared_radius()); + } + Boolean operator()(const Circle_2& c, const Segment_2& s) const { diff --git a/Kernel_23/test/Kernel_23/include/CGAL/_test_cls_circle_2.h b/Kernel_23/test/Kernel_23/include/CGAL/_test_cls_circle_2.h index ffbc0d924f0..7dc1dd5a7f8 100644 --- a/Kernel_23/test/Kernel_23/include/CGAL/_test_cls_circle_2.h +++ b/Kernel_23/test/Kernel_23/include/CGAL/_test_cls_circle_2.h @@ -167,6 +167,8 @@ _test_cls_circle_2(const R& ) assert( cc.has_on_boundary( p3 - vx6) ); assert( c1.has_on_bounded_side(CGAL::Segment_2(p0, p1)) ); assert( c1.has_on_bounded_side(CGAL::Segment_2(p2, p1)) ); + assert( c1.has_on_bounded_side(CGAL::Iso_rectangle_2(CGAL::Point_2(3, 0), CGAL::Point_2(2, 2))) ); + assert(!c1.has_on_bounded_side(CGAL::Iso_rectangle_2(CGAL::Point_2(-3, 0), CGAL::Point_2(2, 2))) ); assert( c1.has_on_bounded_side(CGAL::Segment_2(p1, p1)) ); assert(!c1.has_on_bounded_side(CGAL::Segment_2(p1, p4)) ); diff --git a/Orthtree/include/CGAL/Orthtree.h b/Orthtree/include/CGAL/Orthtree.h index 4a18a3ae7a6..f2479d6f4b6 100644 --- a/Orthtree/include/CGAL/Orthtree.h +++ b/Orthtree/include/CGAL/Orthtree.h @@ -51,6 +51,7 @@ namespace Orthtree_impl { BOOST_MPL_HAS_XXX_TRAIT_DEF(Node_data) BOOST_MPL_HAS_XXX_TRAIT_DEF(Squared_distance_of_element) +BOOST_MPL_HAS_XXX_TRAIT_DEF(Has_on_bounded_side) template struct Node_data_wrapper; @@ -126,9 +127,11 @@ public: #ifndef DOXYGEN_RUNNING static inline constexpr bool has_data = Orthtree_impl::has_Node_data::value; static inline constexpr bool supports_neighbor_search = Orthtree_impl::has_Squared_distance_of_element::value; + static inline constexpr bool supports_ball_search = Orthtree_impl::has_Has_on_bounded_side::value; #else static inline constexpr bool has_data = bool_value; ///< `true` if `GeomTraits` is a model of `OrthtreeTraitsWithData` and `false` otherwise. static inline constexpr bool supports_neighbor_search = bool_value; ///< `true` if `GeomTraits` is a model of `CollectionPartitioningOrthtreeTraits` and `false` otherwise. + static inline constexpr bool supports_ball_search = bool_value; ///< `true` if `GeomTraits` provides a `Has_on_bounded_side` functor and `false` otherwise. #endif static constexpr int dimension = Traits::dimension; ///< Dimension of the tree using Kernel = typename Traits::Kernel; ///< Kernel type. @@ -772,7 +775,7 @@ public: This function finds all the intersecting leaf nodes and writes their indices to the output iterator. - \tparam Query the primitive class (e.g., sphere, ray) + \tparam Query the primitive class (e.g., plane, ray) \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types \param query the intersecting primitive. @@ -785,6 +788,30 @@ public: return intersected_nodes_recursive(query, root(), output, [](const Query& query, const typename Traits::Bbox_d &box) -> bool {return CGAL::do_intersect(query, box);}); } + /*! + \brief finds the leaf nodes that intersect with a ball. + + This function finds all the intersecting leaf nodes and writes their indices to the output iterator. + Requires the Traits class to provide the functor via `has_on_bounded_side_object()` with an operator: + + `bool operator()(Traits::Sphere_d, Traits::Bbox_d)` + + `Kernel::HasOnBoundedSide_2` and `Kernel::HasOnBoundedSide_3` are compatible concepts for dimenions 2 and 3. + + \tparam OutputIterator a model of `OutputIterator` that accepts `Node_index` types + + \param center the center of the ball + \param squared_radius the squared radius of the ball + \param output output iterator. + + \return the output iterator after writing + */ + template + auto intersected_nodes(const Point& center, const FT squared_radius, OutputIterator output) const -> std::enable_if_t { + return intersected_nodes_recursive(Sphere(center, squared_radius), root(), output, [&](const Sphere& query, const typename Traits::Bbox_d& box) -> bool + {return CGAL::do_intersect(query, box) || m_traits.has_on_bounded_side_object()(query, box); }); + } + /*! \brief finds the leaf nodes that intersect with any primitive. diff --git a/Orthtree/include/CGAL/Orthtree_traits_base.h b/Orthtree/include/CGAL/Orthtree_traits_base.h index dc31ba2dd2f..2b94de9f96e 100644 --- a/Orthtree/include/CGAL/Orthtree_traits_base.h +++ b/Orthtree/include/CGAL/Orthtree_traits_base.h @@ -112,6 +112,7 @@ struct Orthtree_traits_base { using Bbox_d = typename GeomTraits::Iso_rectangle_2; using Sphere_d = typename GeomTraits::Circle_2; using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_2; + using Has_on_bounded_side = typename GeomTraits::Has_on_bounded_side_2; enum Adjacency { LEFT, @@ -127,6 +128,10 @@ struct Orthtree_traits_base { return {x, y}; }; } + + Has_on_bounded_side has_on_bounded_side_object() const { + return GeomTraits().has_on_bounded_side_3_object(); + } }; template @@ -139,6 +144,7 @@ struct Orthtree_traits_base { using Bbox_d = typename GeomTraits::Iso_cuboid_3; using Sphere_d = typename GeomTraits::Sphere_3; using Cartesian_const_iterator_d = typename GeomTraits::Cartesian_const_iterator_3; + using Has_on_bounded_side = typename GeomTraits::Has_on_bounded_side_3; enum Adjacency { LEFT, @@ -167,6 +173,10 @@ struct Orthtree_traits_base { return {x, y, z}; }; } + + Has_on_bounded_side has_on_bounded_side_object() const { + return GeomTraits().has_on_bounded_side_3_object(); + } }; } diff --git a/Orthtree/test/Orthtree/test_octree_intersecting.cpp b/Orthtree/test/Orthtree/test_octree_intersecting.cpp index 77eb206f193..f04d58d6652 100644 --- a/Orthtree/test/Orthtree/test_octree_intersecting.cpp +++ b/Orthtree/test/Orthtree/test_octree_intersecting.cpp @@ -79,15 +79,12 @@ int main(void) { assert(octree.locate(Point(1, 1, 1)) == nodes[0]); } - // Intersection with a sphere + // Intersection with a ball // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { - // Set the search point - auto query = Kernel::Sphere_3(Point{1, 0.5, 1}, 1.0); - // Get a list of nodes intersected std::vector nodes{}; - octree.intersected_nodes(query, std::back_inserter(nodes)); + octree.intersected_nodes(Point(1, 0.5, 1), 1.0, std::back_inserter(nodes)); // Check the results assert(4 == nodes.size()); @@ -97,6 +94,30 @@ int main(void) { assert(octree.node(Octree::Traits::RIGHT_TOP_FRONT) == nodes[3]); } + // Intersection with a sphere (does not include the interior) + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { + // Get a list of nodes intersected, in this case the 8 leaf nodes in the corners + std::vector nodes{}; + octree.intersected_nodes(Kernel::Sphere_3(Point(0, 0, 0), 3), std::back_inserter(nodes)); + + // Check the results + assert(8 == nodes.size()); + + auto n = octree.node(Octree::Traits::RIGHT_BOTTOM_BACK); + while (!octree.is_leaf(n)) + n = octree.child(n, Octree::Traits::RIGHT_BOTTOM_BACK); + + assert(octree.node(Octree::Traits::LEFT_BOTTOM_BACK) == nodes[0]); + assert(n == nodes[1]); + assert(octree.node(Octree::Traits::LEFT_TOP_BACK) == nodes[2]); + assert(octree.node(Octree::Traits::RIGHT_TOP_BACK) == nodes[3]); + assert(octree.node(Octree::Traits::LEFT_BOTTOM_FRONT) == nodes[4]); + assert(octree.node(Octree::Traits::RIGHT_BOTTOM_FRONT) == nodes[5]); + assert(octree.node(Octree::Traits::LEFT_TOP_FRONT) == nodes[6]); + assert(octree.node(Octree::Traits::RIGHT_TOP_FRONT) == nodes[7]); + } + // Intersection with a ray // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {