feat: enforce Delaunay vertices at inset boundary corners + update geometry to v2.0 with arcs
- Add explicit corner vertices of the inset boundary (w_frame offset) to Delaunay point set - This guarantees no triangle can cross a boundary corner - Updated test_data geometry files to v2.0 format with typed segments - Sandbox 2 now has proper arc curves (4 arc segments) from extract_sandbox - Preserved holes from v1.0 geometry - Boundary vertices also enforced on keepout boundaries
This commit is contained in:
@@ -154,29 +154,55 @@ def _add_boundary_vertices(points, geometry, params, keepout_union):
|
||||
|
||||
This ensures triangles conform to boundaries rather than just being
|
||||
clipped. Points are spaced at approximately s_min along boundaries.
|
||||
|
||||
KEY: Enforce explicit vertices at every corner of the inset boundary.
|
||||
This guarantees no triangle can cross a corner — the Delaunay triangulation
|
||||
is forced to use these corner points as vertices.
|
||||
"""
|
||||
s_min = params['s_min']
|
||||
w_frame = params.get('w_frame', 8.0)
|
||||
|
||||
new_pts = list(points)
|
||||
|
||||
# Add points along inset outer boundary (frame inner edge)
|
||||
plate_poly = Polygon(geometry['outer_boundary'])
|
||||
inner_frame = plate_poly.buffer(-w_frame)
|
||||
if not inner_frame.is_empty and inner_frame.geom_type == 'Polygon':
|
||||
ring = inner_frame.exterior
|
||||
length = ring.length
|
||||
n_pts = max(int(length / s_min), 4)
|
||||
for i in range(n_pts):
|
||||
frac = i / n_pts
|
||||
pt = ring.interpolate(frac, normalized=True)
|
||||
new_pts.append([pt.x, pt.y])
|
||||
if not inner_frame.is_empty:
|
||||
# Handle MultiPolygon from buffer on complex shapes
|
||||
if inner_frame.geom_type == 'MultiPolygon':
|
||||
inner_polys = list(inner_frame.geoms)
|
||||
else:
|
||||
inner_polys = [inner_frame]
|
||||
|
||||
for inner_poly in inner_polys:
|
||||
ring = inner_poly.exterior
|
||||
|
||||
# 1) ENFORCE corner vertices: add every vertex of the inset boundary
|
||||
# These are the actual corner points — critical for preventing crossovers
|
||||
coords = list(ring.coords)[:-1] # skip closing duplicate
|
||||
for cx, cy in coords:
|
||||
new_pts.append([cx, cy])
|
||||
|
||||
# 2) Add evenly spaced points along edges for density
|
||||
length = ring.length
|
||||
n_pts = max(int(length / s_min), 4)
|
||||
for i in range(n_pts):
|
||||
frac = i / n_pts
|
||||
pt = ring.interpolate(frac, normalized=True)
|
||||
new_pts.append([pt.x, pt.y])
|
||||
|
||||
# Also add inner ring vertices (for any holes in the inset boundary)
|
||||
for interior in inner_poly.interiors:
|
||||
for cx, cy in list(interior.coords)[:-1]:
|
||||
new_pts.append([cx, cy])
|
||||
|
||||
# Add points along hole keepout boundaries
|
||||
if not keepout_union.is_empty:
|
||||
geoms = [keepout_union] if keepout_union.geom_type == 'Polygon' else list(keepout_union.geoms)
|
||||
for geom in geoms:
|
||||
ring = geom.exterior
|
||||
# Enforce corner vertices on keepout boundaries too
|
||||
for cx, cy in list(ring.coords)[:-1]:
|
||||
new_pts.append([cx, cy])
|
||||
length = ring.length
|
||||
n_pts = max(int(length / (s_min * 0.7)), 6)
|
||||
for i in range(n_pts):
|
||||
|
||||
Reference in New Issue
Block a user