Add shove_segment geometric primitive
Displaces one segment of a trace polyline perpendicular by a given offset, keeping the trace orthogonal and its two pad endpoints fixed: a jog corner is inserted when the moved segment is first or last, and interior neighbours simply resize. Collinear/duplicate corners are dropped. This is the foundation for shoving a trace aside to make room for another. Unit-tested for single-segment, interior-segment, and orthogonality-preservation cases.
This commit is contained in:
@@ -2,7 +2,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from freeroute.geometry import IntBox, IntPoint, Polyline, PolylineShape, segment_box
|
||||
from freeroute.geometry import (
|
||||
IntBox,
|
||||
IntPoint,
|
||||
Polyline,
|
||||
PolylineShape,
|
||||
segment_box,
|
||||
shove_segment,
|
||||
)
|
||||
|
||||
|
||||
def test_polyline_segments():
|
||||
@@ -42,3 +49,33 @@ def test_polyline_shape_bounding_box():
|
||||
def test_diagonal_segment_box_is_conservative_bbox():
|
||||
box = segment_box(IntPoint(0, 0), IntPoint(50, 50), 10)
|
||||
assert box == IntBox(-10, -10, 60, 60)
|
||||
|
||||
|
||||
# --- shove primitive --------------------------------------------------------
|
||||
|
||||
|
||||
def test_shove_single_segment_inserts_jogs_and_keeps_pads():
|
||||
# a horizontal pad-to-pad trace shoved up by 30 keeps its endpoints
|
||||
corners = [IntPoint(0, 0), IntPoint(100, 0)]
|
||||
out = shove_segment(corners, 0, 0, 30)
|
||||
assert [(p.x, p.y) for p in out] == [(0, 0), (0, 30), (100, 30), (100, 0)]
|
||||
assert out[0] == corners[0] and out[-1] == corners[1] # pads fixed
|
||||
|
||||
|
||||
def test_shove_interior_segment_resizes_neighbours():
|
||||
# the middle horizontal run of a U shoved up 20; verticals resize, pads fixed
|
||||
corners = [IntPoint(0, 0), IntPoint(0, 50), IntPoint(100, 50), IntPoint(100, 0)]
|
||||
out = shove_segment(corners, 1, 0, 20)
|
||||
assert [(p.x, p.y) for p in out] == [(0, 0), (0, 70), (100, 70), (100, 0)]
|
||||
|
||||
|
||||
def test_shove_stays_orthogonal():
|
||||
corners = [IntPoint(0, 0), IntPoint(0, 40), IntPoint(80, 40), IntPoint(80, 0)]
|
||||
out = shove_segment(corners, 1, 0, 15)
|
||||
for a, b in zip(out, out[1:], strict=False):
|
||||
assert a.x == b.x or a.y == b.y # every segment axis-aligned
|
||||
|
||||
|
||||
def test_shove_out_of_range_is_noop():
|
||||
corners = [IntPoint(0, 0), IntPoint(100, 0)]
|
||||
assert shove_segment(corners, 5, 0, 30) == corners
|
||||
|
||||
Reference in New Issue
Block a user