Add v2 geometry normalization and boundary-layer seed points
This commit is contained in:
@@ -21,6 +21,71 @@ from src.shared.arc_utils import inset_arc, typed_segments_to_polyline, typed_se
|
||||
from .density_field import evaluate_density, density_to_spacing
|
||||
|
||||
|
||||
def _boundary_layer_offset_for_segment(mid_pt, geometry, params):
|
||||
"""Choose inward offset for boundary seed row."""
|
||||
explicit = params.get('boundary_layer_offset', None)
|
||||
if explicit is not None:
|
||||
return max(float(explicit), 0.0)
|
||||
eta = evaluate_density(mid_pt[0], mid_pt[1], geometry, params)
|
||||
return max(float(density_to_spacing(eta, params)), 1e-3)
|
||||
|
||||
|
||||
def _add_boundary_layer_seed_points(points, geometry, params, plate_poly, keepout_union):
|
||||
"""Add a structured point row offset inward from each straight outer edge."""
|
||||
boundary_pts = []
|
||||
ring = LinearRing(geometry['outer_boundary'])
|
||||
is_ccw = bool(ring.is_ccw)
|
||||
|
||||
# Prefer typed segments to avoid treating discretized arcs as straight edges
|
||||
typed = geometry.get('outer_boundary_typed')
|
||||
if typed:
|
||||
segments = [seg for seg in typed if seg.get('type', 'line') == 'line']
|
||||
edge_pairs = [(_np(seg['start']), _np(seg['end'])) for seg in segments]
|
||||
else:
|
||||
coords = np.asarray(geometry['outer_boundary'], dtype=float)
|
||||
if len(coords) >= 2 and np.allclose(coords[0], coords[-1]):
|
||||
coords = coords[:-1]
|
||||
edge_pairs = []
|
||||
for i in range(len(coords)):
|
||||
edge_pairs.append((coords[i], coords[(i + 1) % len(coords)]))
|
||||
|
||||
for a, b in edge_pairs:
|
||||
dx, dy = b[0] - a[0], b[1] - a[1]
|
||||
edge_len = float(np.hypot(dx, dy))
|
||||
if edge_len < 1e-9:
|
||||
continue
|
||||
|
||||
mid = np.array([(a[0] + b[0]) * 0.5, (a[1] + b[1]) * 0.5], dtype=float)
|
||||
spacing = float(density_to_spacing(evaluate_density(mid[0], mid[1], geometry, params), params))
|
||||
spacing = max(spacing, 1e-3)
|
||||
offset = _boundary_layer_offset_for_segment(mid, geometry, params)
|
||||
|
||||
nx_l, ny_l = (-dy / edge_len), (dx / edge_len)
|
||||
nx, ny = (nx_l, ny_l) if is_ccw else (-nx_l, -ny_l)
|
||||
|
||||
n_pts = max(int(np.floor(edge_len / spacing)), 1)
|
||||
for k in range(1, n_pts + 1):
|
||||
t = k / (n_pts + 1)
|
||||
bx = a[0] + t * dx
|
||||
by = a[1] + t * dy
|
||||
px = bx + offset * nx
|
||||
py = by + offset * ny
|
||||
p = Point(px, py)
|
||||
if not plate_poly.buffer(1e-6).contains(p):
|
||||
continue
|
||||
if not keepout_union.is_empty and keepout_union.contains(p):
|
||||
continue
|
||||
boundary_pts.append([px, py])
|
||||
|
||||
if boundary_pts:
|
||||
return np.vstack([points, np.asarray(boundary_pts, dtype=np.float64)])
|
||||
return points
|
||||
|
||||
|
||||
def _np(pt):
|
||||
return np.asarray([float(pt[0]), float(pt[1])], dtype=float)
|
||||
|
||||
|
||||
def _generate_hex_grid(bbox, base_spacing):
|
||||
"""
|
||||
Generate a regular hexagonal-packed point grid.
|
||||
@@ -267,6 +332,10 @@ def generate_triangulation(geometry, params, max_refinement_passes=3):
|
||||
# Add boundary-conforming vertices and get inset plate polygon for clipping
|
||||
all_pts, inner_plate = _add_boundary_vertices(grid_pts, geometry, params, keepout_union)
|
||||
|
||||
# Add structured boundary-layer seed row along straight edges
|
||||
plate_poly = Polygon(geometry['outer_boundary'])
|
||||
all_pts = _add_boundary_layer_seed_points(all_pts, geometry, params, plate_poly, keepout_union)
|
||||
|
||||
# Deduplicate close points
|
||||
all_pts = np.unique(np.round(all_pts, 4), axis=0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user