From 47844a82ac256e79381237090a8f610efe855613 Mon Sep 17 00:00:00 2001 From: Pierre Anquez Date: Wed, 21 Oct 2026 13:20:05 +0200 Subject: [PATCH 1/2] fix(RadialSort): return identity order for fewer than 3 points A cyclic order of 0, 1 or 2 elements is unique, so no geometric computation is needed. Previously the segment direction was always computed, so a zero-length segment threw in Segment::direction, e.g. in surface_radial_sort on a degenerated Line edge between two colocated unique vertices. Co-Authored-By: Claude Opus 5.5 --- src/geode/geometry/radial_sort.cpp | 6 ++++++ tests/geometry/test-radial-sort.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/geode/geometry/radial_sort.cpp b/src/geode/geometry/radial_sort.cpp index c8c99b62f..0cd941697 100644 --- a/src/geode/geometry/radial_sort.cpp +++ b/src/geode/geometry/radial_sort.cpp @@ -140,6 +140,12 @@ namespace geode absl::FixedArray< index_t > radial_sort( const Segment3D& segment, absl::Span< const Point3D > points ) { + if( points.size() < 3 ) + { + absl::FixedArray< index_t > identity( points.size() ); + absl::c_iota( identity, 0 ); + return identity; + } RadialSort radial_sort{ segment, points }; return radial_sort.sort(); } diff --git a/tests/geometry/test-radial-sort.cpp b/tests/geometry/test-radial-sort.cpp index ed7ca53ed..27d3db641 100644 --- a/tests/geometry/test-radial-sort.cpp +++ b/tests/geometry/test-radial-sort.cpp @@ -64,10 +64,23 @@ void test_2() sorted == answer, "Wrong radial sorting" ); } +void test_degenerated_segment() +{ + geode::Point3D s0{ { 0, 0, 1 } }; + geode::Point3D a{ { 0, 0.5, 1.5 } }; + geode::Point3D b{ { 0, 0, 0.5 } }; + + const auto sorted = geode::radial_sort( { s0, s0 }, { a, b } ); + const absl::FixedArray< geode::index_t > answer{ 0, 1 }; + geode::OpenGeodeGeometryException::test( + sorted == answer, "Wrong radial sorting on degenerated segment" ); +} + void test() { test_1(); test_2(); + test_degenerated_segment(); geode::Logger::info( "TEST SUCCESS" ); } From 5bb2b3d21008a2575e34f9fa7ec78785fb89cb53 Mon Sep 17 00:00:00 2001 From: Pierre Anquez Date: Wed, 21 Oct 2026 13:20:05 +0200 Subject: [PATCH 2/2] fix(IntersectionDetection): handle triangles collapsed to a point In coplanar segment/triangle detection, best_projection_axis indexed the triangle vertices with longest_edge_index(), which is NO_LID when all the triangle vertices are colocated. This out-of-bounds read made BRepInspector segfault on models with such triangles. The projection axis now only depends on the segment in that case. Co-Authored-By: Claude Opus 5.5 --- src/geode/geometry/intersection_detection.cpp | 11 ++++++++--- tests/geometry/test-intersection-detection.cpp | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/geode/geometry/intersection_detection.cpp b/src/geode/geometry/intersection_detection.cpp index 7ac67b704..f38a99ec8 100644 --- a/src/geode/geometry/intersection_detection.cpp +++ b/src/geode/geometry/intersection_detection.cpp @@ -106,14 +106,19 @@ namespace return max_abs_axis( normal.value() ); } const auto& vertices = triangle.vertices(); + const auto& segment_vertices = segment.vertices(); + const geode::Vector3D segment_direction{ segment_vertices[0], + segment_vertices[1] }; const auto longest_edge = triangle.longest_edge_index(); + if( longest_edge == geode::NO_LID ) + { + return coplanar_projection_axis( + segment_vertices[0], segment_direction, { vertices[0] } ); + } const geode::Point3D& origin = vertices[longest_edge]; const geode::Point3D& next = vertices[longest_edge == 2 ? 0 : longest_edge + 1]; const geode::Vector3D direction{ origin, next }; - const auto& segment_vertices = segment.vertices(); - const geode::Vector3D segment_direction{ segment_vertices[0], - segment_vertices[1] }; if( segment_direction.length2() > direction.length2() ) { return coplanar_projection_axis( segment_vertices[0], diff --git a/tests/geometry/test-intersection-detection.cpp b/tests/geometry/test-intersection-detection.cpp index bfdd2c019..c43a44707 100644 --- a/tests/geometry/test-intersection-detection.cpp +++ b/tests/geometry/test-intersection-detection.cpp @@ -224,6 +224,16 @@ void test_segment_degenerated_triangle_intersection_detection() geode::POSITION::parallel, geode::POSITION::parallel ), "Wrong result for segment_triangle_intersection_detection with " "segment crossing a cap triangle in its plane" ); + + const geode::Triangle3D point_triangle{ c, c, c }; + const auto far_from_point = geode::segment_triangle_intersection_detection( + geode::Segment3D{ d, e }, point_triangle ); + geode::OpenGeodeGeometryException::test( + far_from_point + == std::make_pair( + geode::POSITION::outside, geode::POSITION::outside ), + "Wrong result for segment_triangle_intersection_detection with " + "triangle collapsed to a point far from the segment" ); } void test_segment_line_intersection_detection()