@@ -2382,6 +2382,86 @@ bool ops::get_euclidean_distance(const geometry &lhs_geom, const geometry &rhs_g
23822382 return found;
23832383}
23842384
2385+ // Visit all non-collection geometries
2386+ template <class CALLBACK >
2387+ void visit_leaf_pairs (const geometry &lhs, const geometry &rhs, CALLBACK &&callback) {
2388+ const auto lhs_root = lhs.get_parent ();
2389+ auto lhs_part = &lhs;
2390+
2391+ while (true ) {
2392+ switch (lhs_part->get_type ()) {
2393+ case geometry_type::POINT:
2394+ case geometry_type::LINESTRING:
2395+ case geometry_type::POLYGON: {
2396+
2397+ const auto rhs_root = rhs.get_parent ();
2398+ auto rhs_part = &rhs;
2399+
2400+ while (true ) {
2401+ switch (rhs_part->get_type ()) {
2402+ case geometry_type::POINT:
2403+ case geometry_type::LINESTRING:
2404+ case geometry_type::POLYGON: {
2405+ // Found a pair of leaf geometries
2406+ // If the callback returns true, we stop visiting
2407+ if (callback (*lhs_part, *rhs_part)) {
2408+ return ;
2409+ }
2410+ } break ;
2411+ case geometry_type::MULTI_POINT:
2412+ case geometry_type::MULTI_LINESTRING:
2413+ case geometry_type::MULTI_POLYGON:
2414+ case geometry_type::GEOMETRY_COLLECTION:
2415+ if (rhs_part->is_empty ()) {
2416+ break ;
2417+ }
2418+ rhs_part = rhs_part->get_first_part ();
2419+ continue ;
2420+ default :
2421+ break ;
2422+ }
2423+
2424+ while (true ) {
2425+ const auto parent = rhs_part->get_parent ();
2426+ if (parent == rhs_root) {
2427+ break ;
2428+ }
2429+ if (rhs_part != parent->get_last_part ()) {
2430+ rhs_part = rhs_part->get_next ();
2431+ break ;
2432+ }
2433+ rhs_part = parent;
2434+ }
2435+ }
2436+
2437+ } break ;
2438+ case geometry_type::MULTI_POINT:
2439+ case geometry_type::MULTI_LINESTRING:
2440+ case geometry_type::MULTI_POLYGON:
2441+ case geometry_type::GEOMETRY_COLLECTION:
2442+ if (lhs_part->is_empty ()) {
2443+ break ;
2444+ }
2445+ lhs_part = lhs_part->get_first_part ();
2446+ continue ;
2447+ default :
2448+ break ;
2449+ }
2450+
2451+ while (true ) {
2452+ const auto parent = lhs_part->get_parent ();
2453+ if (parent == lhs_root) {
2454+ return ;
2455+ }
2456+ if (lhs_part != parent->get_last_part ()) {
2457+ lhs_part = lhs_part->get_next ();
2458+ break ;
2459+ }
2460+ lhs_part = parent;
2461+ }
2462+ }
2463+ }
2464+
23852465} // namespace sgl
23862466
23872467// ======================================================================================================================
@@ -3130,13 +3210,30 @@ static double point_segment_dist_sq(const vertex_xy &p, const vertex_xy &a, cons
31303210 return diff.norm_sq ();
31313211}
31323212
3213+ // Check if point P is on segment QR
31333214static bool point_on_segment (const vertex_xy &p, const vertex_xy &q, const vertex_xy &r) {
31343215 return q.x >= std::min (p.x , r.x ) && q.x <= std::max (p.x , r.x ) && q.y >= std::min (p.y , r.y ) &&
31353216 q.y <= std::max (p.y , r.y );
31363217}
31373218
31383219static bool segment_intersects (const vertex_xy &a1, const vertex_xy &a2, const vertex_xy &b1, const vertex_xy &b2) {
31393220 // Check if two segments intersect using the orientation method
3221+ // Handle degenerate cases where a segment is actually a single point
3222+ const bool a_is_point = (a1.x == a2.x && a1.y == a2.y );
3223+ const bool b_is_point = (b1.x == b2.x && b1.y == b2.y );
3224+
3225+ if (a_is_point && b_is_point) {
3226+ // Both are points: intersect only if identical
3227+ return (a1.x == b1.x && a1.y == b1.y );
3228+ }
3229+ if (a_is_point) {
3230+ // A is a point: check if A lies on segment B
3231+ return point_on_segment (a1, b1, b2);
3232+ }
3233+ if (b_is_point) {
3234+ // B is a point: check if B lies on segment A
3235+ return point_on_segment (b1, a1, a2);
3236+ }
31403237
31413238 const auto o1 = orient2d_fast (a1, a2, b1);
31423239 const auto o2 = orient2d_fast (a1, a2, b2);
@@ -3250,6 +3347,15 @@ static bool try_get_prepared_distance_lines(const prepared_geometry &lhs, const
32503347 for (uint32_t i = lhs_beg_idx + 1 ; i < lhs_end_idx; i++) {
32513348 memcpy (&lhs_next, lhs_vertex_array + i * lhs_vertex_width, sizeof (vertex_xy));
32523349
3350+ // If this is a zero-length segment, skip it
3351+ // LINESTRINGs must have at least two distinct vertices to be valid, so this is safe. Even if we skip
3352+ // this vertex now, we must eventually reach a non-zero-length segment that includes this vertex as
3353+ // its start point. It will therefore still contribute to the distance calculation once we process that
3354+ // segment.
3355+ if (lhs_prev.x == lhs_next.x && lhs_prev.y == lhs_next.y ) {
3356+ continue ;
3357+ }
3358+
32533359 // Quick check: If the distance between the segment and the box (all the segments)
32543360 // is greater than min_dist, we can skip the exact distance check
32553361
@@ -3268,6 +3374,13 @@ static bool try_get_prepared_distance_lines(const prepared_geometry &lhs, const
32683374 for (uint32_t j = rhs_beg_idx + 1 ; j < rhs_end_idx; j++) {
32693375 memcpy (&rhs_next, rhs_vertex_array + j * rhs_vertex_width, sizeof (vertex_xy));
32703376
3377+ // If this is a zero-length segment, skip it
3378+ // LINESTRINGs must have at least two distinct points to be valid, so this is safe.
3379+ // (see comment above)
3380+ if (rhs_prev.x == rhs_next.x && rhs_prev.y == rhs_next.y ) {
3381+ continue ;
3382+ }
3383+
32713384 // Quick check: If the distance between the segment bounds are greater than min_dist,
32723385 // we can skip the exact distance check
32733386 extent_xy rhs_seg;
0 commit comments