From 1b4c1d6a4de9361d8a525182b5c668516cf88f4d Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 18 Dec 2025 17:50:38 +0100 Subject: [PATCH 1/4] fix(Binding): Expose missing functions in BBox --- bindings/python/src/geometry/bounding_box.cpp | 70 ++++++++++++------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/bindings/python/src/geometry/bounding_box.cpp b/bindings/python/src/geometry/bounding_box.cpp index c85225d00..fc18550e6 100644 --- a/bindings/python/src/geometry/bounding_box.cpp +++ b/bindings/python/src/geometry/bounding_box.cpp @@ -24,35 +24,53 @@ #include "../common.hpp" #include +#include #include #include -#define PYTHON_BOUNDING_BOX( dimension ) \ - const auto name##dimension = \ - "BoundingBox" + std::to_string( dimension ) + "D"; \ - pybind11::class_< BoundingBox##dimension##D >( \ - module, name##dimension.c_str() ) \ - .def( pybind11::init<>() ) \ - .def( "add_box", &BoundingBox##dimension##D::add_box ) \ - .def( "add_point", &BoundingBox##dimension##D::add_point ) \ - .def( "contains_point", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const Point< dimension >& ) const >( \ - &BoundingBox##dimension##D::contains ) ) \ - .def( "contains_bbox", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const BoundingBox< dimension >& ) const >( \ - &BoundingBox##dimension##D::contains ) ) \ - .def( "intersects_bbox", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const BoundingBox< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - .def( "min", &BoundingBox##dimension##D::min ) \ - .def( "max", &BoundingBox##dimension##D::max ) \ - .def( "center", &BoundingBox##dimension##D::center ) \ - .def( "diagonal", &BoundingBox##dimension##D::diagonal ) \ - .def( "smallest_length", &BoundingBox##dimension##D::smallest_length ) \ - .def( "largest_length", &BoundingBox##dimension##D::largest_length ) +#define PYTHON_BOUNDING_BOX( dimension ) \ + const auto name##dimension = \ + "BoundingBox" + std::to_string( dimension ) + "D"; \ + pybind11::class_< BoundingBox##dimension##D >( \ + module, name##dimension.c_str() ) \ + .def( pybind11::init<>() ) \ + .def( "add_box", &BoundingBox##dimension##D::add_box ) \ + .def( "add_point", &BoundingBox##dimension##D::add_point ) \ + .def( "extends", &BoundingBox##dimension##D::extends ) \ + .def( "contains_point", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Point< dimension >& ) const >( \ + &BoundingBox##dimension##D::contains ) ) \ + .def( "contains_bbox", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const BoundingBox< dimension >& ) const >( \ + &BoundingBox##dimension##D::contains ) ) \ + .def( "intersects_bbox", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const BoundingBox< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "intersects_ray", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Ray< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "intersects_infiniteline", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const InfiniteLine< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + \ + .def( "intersects_segment", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Segment< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "signed_distance", &BoundingBox##dimension##D::signed_distance ) \ + .def( "min", &BoundingBox##dimension##D::min ) \ + .def( "max", &BoundingBox##dimension##D::max ) \ + .def( "center", &BoundingBox##dimension##D::center ) \ + .def( "diagonal", &BoundingBox##dimension##D::diagonal ) \ + .def( "smallest_length", &BoundingBox##dimension##D::smallest_length ) \ + .def( "largest_length", &BoundingBox##dimension##D::largest_length ) \ + .def( "n_volume", &BoundingBox##dimension##D::n_volume ) \ + .def( "string", &BoundingBox##dimension##D::string ) namespace geode { From 9365d5930fb2418bd8188b7c63d5ec9c39c1b507 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 18 Dec 2025 19:29:25 +0100 Subject: [PATCH 2/4] typos --- bindings/python/src/geometry/bounding_box.cpp | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/bindings/python/src/geometry/bounding_box.cpp b/bindings/python/src/geometry/bounding_box.cpp index fc18550e6..fa05db38f 100644 --- a/bindings/python/src/geometry/bounding_box.cpp +++ b/bindings/python/src/geometry/bounding_box.cpp @@ -28,48 +28,47 @@ #include #include -#define PYTHON_BOUNDING_BOX( dimension ) \ - const auto name##dimension = \ - "BoundingBox" + std::to_string( dimension ) + "D"; \ - pybind11::class_< BoundingBox##dimension##D >( \ - module, name##dimension.c_str() ) \ - .def( pybind11::init<>() ) \ - .def( "add_box", &BoundingBox##dimension##D::add_box ) \ - .def( "add_point", &BoundingBox##dimension##D::add_point ) \ - .def( "extends", &BoundingBox##dimension##D::extends ) \ - .def( "contains_point", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const Point< dimension >& ) const >( \ - &BoundingBox##dimension##D::contains ) ) \ - .def( "contains_bbox", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const BoundingBox< dimension >& ) const >( \ - &BoundingBox##dimension##D::contains ) ) \ - .def( "intersects_bbox", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const BoundingBox< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - .def( "intersects_ray", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const Ray< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - .def( "intersects_infiniteline", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const InfiniteLine< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - \ - .def( "intersects_segment", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const Segment< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - .def( "signed_distance", &BoundingBox##dimension##D::signed_distance ) \ - .def( "min", &BoundingBox##dimension##D::min ) \ - .def( "max", &BoundingBox##dimension##D::max ) \ - .def( "center", &BoundingBox##dimension##D::center ) \ - .def( "diagonal", &BoundingBox##dimension##D::diagonal ) \ - .def( "smallest_length", &BoundingBox##dimension##D::smallest_length ) \ - .def( "largest_length", &BoundingBox##dimension##D::largest_length ) \ - .def( "n_volume", &BoundingBox##dimension##D::n_volume ) \ +#define PYTHON_BOUNDING_BOX( dimension ) \ + const auto name##dimension = \ + "BoundingBox" + std::to_string( dimension ) + "D"; \ + pybind11::class_< BoundingBox##dimension##D >( \ + module, name##dimension.c_str() ) \ + .def( pybind11::init<>() ) \ + .def( "add_box", &BoundingBox##dimension##D::add_box ) \ + .def( "add_point", &BoundingBox##dimension##D::add_point ) \ + .def( "extends", &BoundingBox##dimension##D::extends ) \ + .def( "contains_point", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Point< dimension >& ) const >( \ + &BoundingBox##dimension##D::contains ) ) \ + .def( "contains_bbox", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const BoundingBox< dimension >& ) const >( \ + &BoundingBox##dimension##D::contains ) ) \ + .def( "intersects_bounding_box", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const BoundingBox< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "intersects_ray", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Ray< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "intersects_infinite_line", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const InfiniteLine< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "intersects_segment", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Segment< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) \ + .def( "signed_distance", &BoundingBox##dimension##D::signed_distance ) \ + .def( "min", &BoundingBox##dimension##D::min ) \ + .def( "max", &BoundingBox##dimension##D::max ) \ + .def( "center", &BoundingBox##dimension##D::center ) \ + .def( "diagonal", &BoundingBox##dimension##D::diagonal ) \ + .def( "smallest_length", &BoundingBox##dimension##D::smallest_length ) \ + .def( "largest_length", &BoundingBox##dimension##D::largest_length ) \ + .def( "n_volume", &BoundingBox##dimension##D::n_volume ) \ .def( "string", &BoundingBox##dimension##D::string ) namespace geode From 3a85c6400e3b6344c7515face8ebf097fe0d20e1 Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Thu, 18 Dec 2025 20:05:15 +0100 Subject: [PATCH 3/4] stubgen --- bindings/python/src/geometry/geometry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/src/geometry/geometry.cpp b/bindings/python/src/geometry/geometry.cpp index 42bc7fdd5..8b2083ba4 100644 --- a/bindings/python/src/geometry/geometry.cpp +++ b/bindings/python/src/geometry/geometry.cpp @@ -60,8 +60,8 @@ PYBIND11_MODULE( opengeode_py_geometry, module ) geode::define_vector( module ); geode::define_frame( module ); geode::define_angle( module ); - geode::define_bounding_box( module ); geode::define_basic_objects( module ); + geode::define_bounding_box( module ); geode::define_barycentric( module ); geode::define_distance( module ); geode::define_intersection( module ); From 009148b3ae4ce2ae73997179b80a425061af9c4e Mon Sep 17 00:00:00 2001 From: Francois Bonneau Date: Mon, 5 Jan 2026 15:39:08 +0100 Subject: [PATCH 4/4] split binding --- bindings/python/src/geometry/CMakeLists.txt | 2 +- .../python/src/geometry/basic_objects.cpp | 21 +++++++++++++ bindings/python/src/geometry/bounding_box.cpp | 12 ------- bindings/python/src/geometry/geometry.cpp | 2 +- .../tests/geometry/test-py-bounding-box.py | 31 +++++++++++++++++++ 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/bindings/python/src/geometry/CMakeLists.txt b/bindings/python/src/geometry/CMakeLists.txt index 2fe7b2010..0dfbc753a 100644 --- a/bindings/python/src/geometry/CMakeLists.txt +++ b/bindings/python/src/geometry/CMakeLists.txt @@ -24,8 +24,8 @@ add_geode_python_binding( "geometry.cpp" "angle.cpp" "barycentric_coordinates.cpp" - "basic_objects.cpp" "bounding_box.cpp" + "basic_objects.cpp" "coordinate_system.cpp" "distance.cpp" "frame.cpp" diff --git a/bindings/python/src/geometry/basic_objects.cpp b/bindings/python/src/geometry/basic_objects.cpp index 591f579af..b600f0497 100644 --- a/bindings/python/src/geometry/basic_objects.cpp +++ b/bindings/python/src/geometry/basic_objects.cpp @@ -97,6 +97,25 @@ .def( "origin", &Ray##dimension##D::origin ) \ .def( "direction", &Ray##dimension##D::direction ) +#define PYTHON_BOUNDING_BOX_AND_BASIC_OBJECTS( dimension ) \ + const auto bbox##dimension = \ + "BoundingBox" + std::to_string( dimension ) + "D"; \ + auto pybbox##dimension = \ + module.attr( bbox##dimension.c_str() ) \ + .cast< pybind11::class_< BoundingBox##dimension##D > >(); \ + pybbox##dimension.def( \ + "intersects_ray", static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Ray< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ); \ + pybbox##dimension.def( "intersects_infinite_line", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const InfiniteLine< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ); \ + pybbox##dimension.def( "intersects_segment", \ + static_cast< bool ( BoundingBox##dimension##D::* )( \ + const Segment< dimension >& ) const >( \ + &BoundingBox##dimension##D::intersects ) ) + namespace geode { void define_basic_objects( pybind11::module& module ) @@ -169,5 +188,7 @@ namespace geode .def( pybind11::init< Segment3D, double >() ) .def( "axis", &Cylinder::axis ) .def( "radius", &Cylinder::radius ); + PYTHON_BOUNDING_BOX_AND_BASIC_OBJECTS( 2 ); + PYTHON_BOUNDING_BOX_AND_BASIC_OBJECTS( 3 ); } } // namespace geode diff --git a/bindings/python/src/geometry/bounding_box.cpp b/bindings/python/src/geometry/bounding_box.cpp index fa05db38f..95b672277 100644 --- a/bindings/python/src/geometry/bounding_box.cpp +++ b/bindings/python/src/geometry/bounding_box.cpp @@ -49,18 +49,6 @@ static_cast< bool ( BoundingBox##dimension##D::* )( \ const BoundingBox< dimension >& ) const >( \ &BoundingBox##dimension##D::intersects ) ) \ - .def( "intersects_ray", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const Ray< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - .def( "intersects_infinite_line", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const InfiniteLine< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ - .def( "intersects_segment", \ - static_cast< bool ( BoundingBox##dimension##D::* )( \ - const Segment< dimension >& ) const >( \ - &BoundingBox##dimension##D::intersects ) ) \ .def( "signed_distance", &BoundingBox##dimension##D::signed_distance ) \ .def( "min", &BoundingBox##dimension##D::min ) \ .def( "max", &BoundingBox##dimension##D::max ) \ diff --git a/bindings/python/src/geometry/geometry.cpp b/bindings/python/src/geometry/geometry.cpp index 8b2083ba4..42bc7fdd5 100644 --- a/bindings/python/src/geometry/geometry.cpp +++ b/bindings/python/src/geometry/geometry.cpp @@ -60,8 +60,8 @@ PYBIND11_MODULE( opengeode_py_geometry, module ) geode::define_vector( module ); geode::define_frame( module ); geode::define_angle( module ); - geode::define_basic_objects( module ); geode::define_bounding_box( module ); + geode::define_basic_objects( module ); geode::define_barycentric( module ); geode::define_distance( module ); geode::define_intersection( module ); diff --git a/bindings/python/tests/geometry/test-py-bounding-box.py b/bindings/python/tests/geometry/test-py-bounding-box.py index 74e6325fc..fd92bb695 100644 --- a/bindings/python/tests/geometry/test-py-bounding-box.py +++ b/bindings/python/tests/geometry/test-py-bounding-box.py @@ -47,3 +47,34 @@ raise ValueError("[Test] BBox should contain this point") if box2.contains_point(geom.Point2D([10, 0])): raise ValueError("[Test] BBox should not contain this point") + + # --- 3D Bounding Box and Ray/Line intersections --- + bbox3 = geom.BoundingBox3D() + bbox3.add_point(geom.Point3D([-1, -1, -1])) + bbox3.add_point(geom.Point3D([1, 1, 1])) + + # Rays + ray_inside = geom.Ray3D( geom.Vector3D([0, 0, 1]),geom.Point3D([0, 0, 0])) + if not bbox3.intersects_ray(ray_inside): + raise ValueError("[Test] Wrong result with ray_inside") + + ray_up = geom.Ray3D( geom.Vector3D([0, 0, 1]),geom.Point3D([0, 0, 2])) + if bbox3.intersects_ray(ray_up): + raise ValueError("[Test] Wrong result with ray_up") + + ray_down = geom.Ray3D( geom.Vector3D([0, 0, 1]),geom.Point3D([0, 0, -2])) + if not bbox3.intersects_ray(ray_down): + raise ValueError("[Test] Wrong result with ray_down") + + # Infinite lines + line_inside = geom.InfiniteLine3D( geom.Vector3D([0, 0, 1]),geom.Point3D([0, 0, 0])) + if not bbox3.intersects_infinite_line(line_inside): + raise ValueError("[Test] Wrong result with line_inside") + + line_up = geom.InfiniteLine3D( geom.Vector3D([0, 0, 1]),geom.Point3D([0, 0, 2])) + if not bbox3.intersects_infinite_line(line_up): + raise ValueError("[Test] Wrong result with line_up") + + line_down = geom.InfiniteLine3D( geom.Vector3D([0, 0, 1]),geom.Point3D([0, 0, -2])) + if not bbox3.intersects_infinite_line(line_down): + raise ValueError("[Test] Wrong result with line_down") \ No newline at end of file