Fix unilateral boundary detection for notched elements

When an L-shaped element wraps around a smaller coplanar element,
the shared face is interior to the larger element's bounding box.
The previous seg_on_boundary_of check required the shared face to
lie on the larger element's own bbox edge (outer abutment only),
so it missed these cases entirely.

The new logic checks whether the segment lies on the *smaller*
element's own bbox edge and whether the other element extends past
that edge on the adjacent side — correctly handling both simple
side-by-side abutment and notch/wrap configurations.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Ryan Schultz
2026-04-05 22:05:15 -05:00
parent a818693a15
commit e6235d0cb6
@@ -1776,29 +1776,45 @@ class CreateDrawing(bpy.types.Operator):
ys = [c for _, (p0, p1) in segs for c in (p0[1], p1[1])]
return (min(xs), max(xs), min(ys), max(ys))
def seg_on_boundary_of(seg, bbox):
"""True if seg is axis-aligned, lies on a bbox face, and meaningfully overlaps it.
def seg_on_boundary_of(seg, bbox_self, bbox_other):
"""True if seg (from 'other' element) lies on the shared interface with 'self'.
Used for unilateral matching: when one element's shared edge is not
explicitly drawn (it's implicit or on the interior), the other element's
segments that lie on that missing edge can still be detected and removed.
A segment from 'other' is on the shared interface when it lies on 'other's
OWN bbox edge AND 'self' extends past that edge on the adjacent side.
This handles both simple abutment (elements side-by-side, bbox edges touching)
and notch/wrap cases (one L-shaped element wraps around a smaller one, so the
shared face is interior to the larger element's bbox).
External edges shared by both elements (e.g. both having the same min_y) are
correctly rejected: 'self' would have to extend PAST the edge, not merely
reach the same boundary.
"""
(x0, y0), (x1, y1) = seg
min_x, max_x, min_y, max_y = bbox
min_xs, max_xs, min_ys, max_ys = bbox_self
min_xo, max_xo, min_yo, max_yo = bbox_other
is_v = abs(x0 - x1) < TOL
is_h = abs(y0 - y1) < TOL
if is_v:
x_mid = (x0 + x1) / 2
if abs(x_mid - min_x) < TOL or abs(x_mid - max_x) < TOL:
# Seg on other's RIGHT edge; self must extend further right
if abs(x_mid - max_xo) < TOL and max_xs > max_xo + TOL and min_xs < max_xo + TOL:
y_lo, y_hi = min(y0, y1), max(y0, y1)
overlap = min(y_hi, max_y) - max(y_lo, min_y)
return overlap > TOL
return min(y_hi, max_ys) - max(y_lo, min_ys) > TOL
# Seg on other's LEFT edge; self must extend further left
if abs(x_mid - min_xo) < TOL and min_xs < min_xo - TOL and max_xs > min_xo - TOL:
y_lo, y_hi = min(y0, y1), max(y0, y1)
return min(y_hi, max_ys) - max(y_lo, min_ys) > TOL
if is_h:
y_mid = (y0 + y1) / 2
if abs(y_mid - min_y) < TOL or abs(y_mid - max_y) < TOL:
# Seg on other's BOTTOM edge; self must extend further down
if abs(y_mid - max_yo) < TOL and max_ys > max_yo + TOL and min_ys < max_yo + TOL:
x_lo, x_hi = min(x0, x1), max(x0, x1)
overlap = min(x_hi, max_x) - max(x_lo, min_x)
return overlap > TOL
return min(x_hi, max_xs) - max(x_lo, min_xs) > TOL
# Seg on other's TOP edge; self must extend further up
if abs(y_mid - min_yo) < TOL and min_ys < min_yo - TOL and max_ys > min_yo - TOL:
x_lo, x_hi = min(x0, x1), max(x0, x1)
return min(x_hi, max_xs) - max(x_lo, min_xs) > TOL
return False
def lines_match(a, b):
@@ -1960,11 +1976,11 @@ class CreateDrawing(bpy.types.Operator):
bbox_j = segs_bbox(segs_j)
if bbox_i and bbox_j:
for path_j, line_j in segs_j:
if seg_on_boundary_of(line_j, bbox_i):
if seg_on_boundary_of(line_j, bbox_i, bbox_j):
to_remove.add(id(path_j))
matched += 1
for path_i, line_i in segs_i:
if seg_on_boundary_of(line_i, bbox_j):
if seg_on_boundary_of(line_i, bbox_j, bbox_i):
to_remove.add(id(path_i))
matched += 1