The router began as a port of FreeRouting and has grown its own engine (negotiated congestion, tree nets, exact checks, axis escapes), and 'freeroute' was one letter from the project it no longer follows. The package, module and command are now rpmroute; the freeroute command stays as an alias, and the RPMROUTE / RPMROUTE_ORACLE_JAVA variables fall back to the old FREEROUTE names. Version 2026.10.07. Routing is unchanged: a PCBench board routes to a byte-identical session either way.
82 lines
2.7 KiB
Python
82 lines
2.7 KiB
Python
"""Tests for Polyline / PolylineShape (the swept trace copper)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from rpmroute.geometry import (
|
|
IntBox,
|
|
IntPoint,
|
|
Polyline,
|
|
PolylineShape,
|
|
segment_box,
|
|
shove_segment,
|
|
)
|
|
|
|
|
|
def test_polyline_segments():
|
|
pl = Polyline([IntPoint(0, 0), IntPoint(10, 0), IntPoint(10, 10)])
|
|
segs = list(pl.segments())
|
|
assert len(segs) == 2
|
|
assert pl.corner_count() == 3
|
|
assert not pl.is_empty()
|
|
assert Polyline([IntPoint(0, 0)]).is_empty()
|
|
|
|
|
|
def test_segment_box_horizontal_is_flush_in_x():
|
|
box = segment_box(IntPoint(0, 0), IntPoint(100, 0), 10)
|
|
# flush in the travel direction (x), offset by half-width in y
|
|
assert box == IntBox(0, -10, 100, 10)
|
|
|
|
|
|
def test_segment_box_vertical_is_flush_in_y():
|
|
box = segment_box(IntPoint(0, 0), IntPoint(0, 100), 10)
|
|
assert box == IntBox(-10, 0, 10, 100)
|
|
|
|
|
|
def test_polyline_shape_tiles_include_corner_squares():
|
|
pl = Polyline([IntPoint(0, 0), IntPoint(100, 0), IntPoint(100, 100)])
|
|
tiles = PolylineShape(pl, 10).tiles()
|
|
# 2 segment boxes + 1 interior-corner square
|
|
assert len(tiles) == 3
|
|
# the corner square is centred on the turn at (100, 0)
|
|
assert IntBox(90, -10, 110, 10) in tiles
|
|
|
|
|
|
def test_polyline_shape_bounding_box():
|
|
pl = Polyline([IntPoint(0, 0), IntPoint(100, 0)])
|
|
assert PolylineShape(pl, 5).bounding_box() == IntBox(-5, -5, 105, 5)
|
|
|
|
|
|
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
|