From 8be3d2972e07511f99b53fd613fc2685a4b5f2fb Mon Sep 17 00:00:00 2001 From: Yoann Le Montagner Date: Fri, 20 Jun 2025 17:25:43 +0200 Subject: [PATCH 1/3] feat(AABBTree): generic intersection search method --- include/geode/geometry/aabb.hpp | 23 ++++++++ include/geode/geometry/detail/aabb_impl.hpp | 61 +++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/include/geode/geometry/aabb.hpp b/include/geode/geometry/aabb.hpp index 6dd3d1d55..adc9f9377 100644 --- a/include/geode/geometry/aabb.hpp +++ b/include/geode/geometry/aabb.hpp @@ -195,6 +195,29 @@ namespace geode const InfiniteLine< dimension >& line, EvalIntersection& action ) const; + /*! + * @brief Computes the intersections between any object (for which + * intersection against an arbitrary axis-aligned bounding box can be + * detected efficiently) and all element boxes. + * @param[in] boxFilter The functor to run to determine whether a box is + * intersected by the searched object or not. The box may correspond + * either to an internal tree node or to a tree element. + * @param[in] action The functor to run when a tree element box is + * intersected by the search object. + * @tparam EvalBox this functor should have an operator() defined like + * this: bool operator( const BoundingBox & ) ; + * @tparam EvalIntersection this functor should have an operator() + * defined like this: + * bool operator()( index_t cur_element_box ) ; + * @note the operator define what to do with the box \p cur_element_box + * if it is intersected by the searched object. + * @note The returned boolean indicates if the search should stop or + * continue. Return true to stop the search, false to continue. + */ + template < class EvalBox, class EvalIntersection > + void compute_generic_element_bbox_intersections( + EvalBox& boxFilter, EvalIntersection& action ) const; + /*! * @brief Computes the intersections between a given Segment and * all element boxes. diff --git a/include/geode/geometry/detail/aabb_impl.hpp b/include/geode/geometry/detail/aabb_impl.hpp index 22501f27d..29bf8eb51 100644 --- a/include/geode/geometry/detail/aabb_impl.hpp +++ b/include/geode/geometry/detail/aabb_impl.hpp @@ -571,6 +571,54 @@ namespace geode return task.get(); } + template < typename BOX_FILTER, typename ACTION > + bool generic_intersect_recursive( BOX_FILTER& boxFilter, + index_t node_index, + index_t element_begin, + index_t element_end, + index_t depth, + ACTION& action ) const + { + OPENGEODE_ASSERT( + node_index < tree_.size(), "Node out of tree range" ); + OPENGEODE_ASSERT( element_begin != element_end, + "No iteration allowed start == end" ); + + // Prune sub-tree that does not have intersection + if( !boxFilter( node( node_index ) ) ) + { + return false; + } + + if( is_leaf( element_begin, element_end ) ) + { + return action( mapping_morton( element_begin ) ); + } + + const auto it = get_recursive_iterators( + node_index, element_begin, element_end ); + if( depth > async_depth_ ) + { + if( generic_intersect_recursive( boxFilter, it.child_left, + element_begin, it.element_middle, depth + 1, action ) ) + { + return true; + } + return generic_intersect_recursive( boxFilter, it.child_right, + it.element_middle, element_end, depth + 1, action ); + } + auto task = async::local_spawn( [&] { + return generic_intersect_recursive( boxFilter, it.child_left, + element_begin, it.element_middle, depth + 1, action ); + } ); + if( generic_intersect_recursive( boxFilter, it.child_right, + it.element_middle, element_end, depth + 1, action ) ) + { + return true; + } + return task.get(); + } + [[nodiscard]] index_t closest_element_box_hint( const Point< dimension >& query ) const { @@ -730,6 +778,19 @@ namespace geode line, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); } + template < index_t dimension > + template < class EvalBox, class EvalIntersection > + void AABBTree< dimension >::compute_generic_element_bbox_intersections( + EvalBox& boxFilter, EvalIntersection& action ) const + { + if( nb_bboxes() == 0 ) + { + return; + } + impl_->generic_intersect_recursive( + boxFilter, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + } + template < index_t dimension > template < class EvalIntersection > void AABBTree< dimension >::compute_triangle_element_bbox_intersections( From 3728b71b006befddf3f4e2086c4daebb77dbb841 Mon Sep 17 00:00:00 2001 From: Yoann Le Montagner Date: Wed, 9 Jul 2025 10:49:31 +0200 Subject: [PATCH 2/3] Stick to naming conv --- include/geode/geometry/aabb.hpp | 6 +++--- include/geode/geometry/detail/aabb_impl.hpp | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/geode/geometry/aabb.hpp b/include/geode/geometry/aabb.hpp index adc9f9377..b2e68f69a 100644 --- a/include/geode/geometry/aabb.hpp +++ b/include/geode/geometry/aabb.hpp @@ -199,8 +199,8 @@ namespace geode * @brief Computes the intersections between any object (for which * intersection against an arbitrary axis-aligned bounding box can be * detected efficiently) and all element boxes. - * @param[in] boxFilter The functor to run to determine whether a box is - * intersected by the searched object or not. The box may correspond + * @param[in] box_filter The functor to run to determine whether a box + * is intersected by the searched object or not. The box may correspond * either to an internal tree node or to a tree element. * @param[in] action The functor to run when a tree element box is * intersected by the search object. @@ -216,7 +216,7 @@ namespace geode */ template < class EvalBox, class EvalIntersection > void compute_generic_element_bbox_intersections( - EvalBox& boxFilter, EvalIntersection& action ) const; + EvalBox& box_filter, EvalIntersection& action ) const; /*! * @brief Computes the intersections between a given Segment and diff --git a/include/geode/geometry/detail/aabb_impl.hpp b/include/geode/geometry/detail/aabb_impl.hpp index 29bf8eb51..32add8974 100644 --- a/include/geode/geometry/detail/aabb_impl.hpp +++ b/include/geode/geometry/detail/aabb_impl.hpp @@ -572,7 +572,7 @@ namespace geode } template < typename BOX_FILTER, typename ACTION > - bool generic_intersect_recursive( BOX_FILTER& boxFilter, + bool generic_intersect_recursive( BOX_FILTER& box_filter, index_t node_index, index_t element_begin, index_t element_end, @@ -585,7 +585,7 @@ namespace geode "No iteration allowed start == end" ); // Prune sub-tree that does not have intersection - if( !boxFilter( node( node_index ) ) ) + if( !box_filter( node( node_index ) ) ) { return false; } @@ -599,19 +599,19 @@ namespace geode node_index, element_begin, element_end ); if( depth > async_depth_ ) { - if( generic_intersect_recursive( boxFilter, it.child_left, + if( generic_intersect_recursive( box_filter, it.child_left, element_begin, it.element_middle, depth + 1, action ) ) { return true; } - return generic_intersect_recursive( boxFilter, it.child_right, + return generic_intersect_recursive( box_filter, it.child_right, it.element_middle, element_end, depth + 1, action ); } auto task = async::local_spawn( [&] { - return generic_intersect_recursive( boxFilter, it.child_left, + return generic_intersect_recursive( box_filter, it.child_left, element_begin, it.element_middle, depth + 1, action ); } ); - if( generic_intersect_recursive( boxFilter, it.child_right, + if( generic_intersect_recursive( box_filter, it.child_right, it.element_middle, element_end, depth + 1, action ) ) { return true; @@ -781,14 +781,14 @@ namespace geode template < index_t dimension > template < class EvalBox, class EvalIntersection > void AABBTree< dimension >::compute_generic_element_bbox_intersections( - EvalBox& boxFilter, EvalIntersection& action ) const + EvalBox& box_filter, EvalIntersection& action ) const { if( nb_bboxes() == 0 ) { return; } impl_->generic_intersect_recursive( - boxFilter, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + box_filter, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); } template < index_t dimension > From 9a711617fea4512aeae09cabdca27a1c3cfe7783 Mon Sep 17 00:00:00 2001 From: Yoann Le Montagner Date: Wed, 9 Jul 2025 11:06:20 +0200 Subject: [PATCH 3/3] Re-impl existing queries with the generic one --- include/geode/geometry/detail/aabb_impl.hpp | 196 ++------------------ 1 file changed, 20 insertions(+), 176 deletions(-) diff --git a/include/geode/geometry/detail/aabb_impl.hpp b/include/geode/geometry/detail/aabb_impl.hpp index 32add8974..3878970f1 100644 --- a/include/geode/geometry/detail/aabb_impl.hpp +++ b/include/geode/geometry/detail/aabb_impl.hpp @@ -260,104 +260,6 @@ namespace geode } } - template < typename ACTION > - bool bbox_intersect_recursive( const BoundingBox< dimension >& box, - index_t node_index, - index_t element_begin, - index_t element_end, - index_t depth, - ACTION& action ) const - - { - OPENGEODE_ASSERT( - node_index < tree_.size(), "Node out of tree range" ); - OPENGEODE_ASSERT( element_begin != element_end, - "No iteration allowed start == end" ); - - // Prune sub-tree that does not have intersection - if( !box.intersects( node( node_index ) ) ) - { - return false; - } - - if( is_leaf( element_begin, element_end ) ) - { - return action( mapping_morton( element_begin ) ); - } - - const auto it = get_recursive_iterators( - node_index, element_begin, element_end ); - if( depth > async_depth_ ) - { - if( bbox_intersect_recursive( box, it.child_left, element_begin, - it.element_middle, depth + 1, action ) ) - { - return true; - } - return bbox_intersect_recursive( box, it.child_right, - it.element_middle, element_end, depth + 1, action ); - } - auto task = async::local_spawn( [&] { - return bbox_intersect_recursive( box, it.child_left, - element_begin, it.element_middle, depth + 1, action ); - } ); - if( bbox_intersect_recursive( box, it.child_right, - it.element_middle, element_end, depth + 1, action ) ) - { - return true; - } - return task.get(); - } - - template < typename ACTION > - bool triangle_intersect_recursive( - const Triangle< dimension >& triangle, - index_t node_index, - index_t element_begin, - index_t element_end, - index_t depth, - ACTION& action ) const - { - OPENGEODE_ASSERT( - node_index < tree_.size(), "Node out of tree range" ); - OPENGEODE_ASSERT( element_begin != element_end, - "No iteration allowed start == end" ); - - // Prune sub-tree that does not have intersection - if( !node( node_index ).intersects( triangle ) ) - { - return false; - } - - if( is_leaf( element_begin, element_end ) ) - { - return action( mapping_morton( element_begin ) ); - } - - const auto it = get_recursive_iterators( - node_index, element_begin, element_end ); - if( depth > async_depth_ ) - { - if( triangle_intersect_recursive( triangle, it.child_left, - element_begin, it.element_middle, depth + 1, action ) ) - { - return true; - } - return triangle_intersect_recursive( triangle, it.child_right, - it.element_middle, element_end, depth + 1, action ); - } - auto task = async::local_spawn( [&] { - return triangle_intersect_recursive( triangle, it.child_left, - element_begin, it.element_middle, depth + 1, action ); - } ); - if( triangle_intersect_recursive( triangle, it.child_right, - it.element_middle, element_end, depth + 1, action ) ) - { - return true; - } - return task.get(); - } - template < typename ACTION > bool self_intersect_recursive( index_t node_index1, index_t element_begin1, @@ -523,54 +425,6 @@ namespace geode return task.get(); } - template < typename Line, typename ACTION > - bool line_intersect_recursive( const Line& line, - index_t node_index, - index_t element_begin, - index_t element_end, - index_t depth, - ACTION& action ) const - { - OPENGEODE_ASSERT( - node_index < tree_.size(), "Node out of tree range" ); - OPENGEODE_ASSERT( element_begin != element_end, - "No iteration allowed start == end" ); - - // Prune sub-tree that does not have intersection - if( !node( node_index ).intersects( line ) ) - { - return false; - } - - if( is_leaf( element_begin, element_end ) ) - { - return action( mapping_morton( element_begin ) ); - } - - const auto it = get_recursive_iterators( - node_index, element_begin, element_end ); - if( depth > async_depth_ ) - { - if( line_intersect_recursive( line, it.child_left, - element_begin, it.element_middle, depth + 1, action ) ) - { - return true; - } - return line_intersect_recursive( line, it.child_right, - it.element_middle, element_end, depth + 1, action ); - } - auto task = async::local_spawn( [&] { - return line_intersect_recursive( line, it.child_left, - element_begin, it.element_middle, depth + 1, action ); - } ); - if( line_intersect_recursive( line, it.child_right, - it.element_middle, element_end, depth + 1, action ) ) - { - return true; - } - return task.get(); - } - template < typename BOX_FILTER, typename ACTION > bool generic_intersect_recursive( BOX_FILTER& box_filter, index_t node_index, @@ -717,12 +571,10 @@ namespace geode void AABBTree< dimension >::compute_bbox_element_bbox_intersections( const BoundingBox< dimension >& box, EvalIntersection& action ) const { - if( nb_bboxes() == 0 ) - { - return; - } - impl_->bbox_intersect_recursive( - box, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + auto box_filter = [&box]( const BoundingBox< dimension >& inner_box ) { + return inner_box.intersects( box ); + }; + compute_generic_element_bbox_intersections( box_filter, action ); } template < index_t dimension > @@ -757,12 +609,10 @@ namespace geode void AABBTree< dimension >::compute_ray_element_bbox_intersections( const Ray< dimension >& ray, EvalIntersection& action ) const { - if( nb_bboxes() == 0 ) - { - return; - } - impl_->line_intersect_recursive( - ray, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + auto box_filter = [&ray]( const BoundingBox< dimension >& box ) { + return box.intersects( ray ); + }; + compute_generic_element_bbox_intersections( box_filter, action ); } template < index_t dimension > @@ -770,12 +620,10 @@ namespace geode void AABBTree< dimension >::compute_line_element_bbox_intersections( const InfiniteLine< dimension >& line, EvalIntersection& action ) const { - if( nb_bboxes() == 0 ) - { - return; - } - impl_->line_intersect_recursive( - line, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + auto box_filter = [&line]( const BoundingBox< dimension >& box ) { + return box.intersects( line ); + }; + compute_generic_element_bbox_intersections( box_filter, action ); } template < index_t dimension > @@ -796,12 +644,10 @@ namespace geode void AABBTree< dimension >::compute_triangle_element_bbox_intersections( const Triangle< dimension >& triangle, EvalIntersection& action ) const { - if( nb_bboxes() == 0 ) - { - return; - } - impl_->triangle_intersect_recursive( - triangle, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + auto box_filter = [&triangle]( const BoundingBox< dimension >& box ) { + return box.intersects( triangle ); + }; + compute_generic_element_bbox_intersections( box_filter, action ); } template < index_t dimension > @@ -809,11 +655,9 @@ namespace geode void AABBTree< dimension >::compute_segment_element_bbox_intersections( const Segment< dimension >& segment, EvalIntersection& action ) const { - if( nb_bboxes() == 0 ) - { - return; - } - impl_->line_intersect_recursive( - segment, Impl::ROOT_INDEX, 0, nb_bboxes(), 0, action ); + auto box_filter = [&segment]( const BoundingBox< dimension >& box ) { + return box.intersects( segment ); + }; + compute_generic_element_bbox_intersections( box_filter, action ); } } // namespace geode