"""Tests for Polyline / PolylineShape (the swept trace copper).""" from __future__ import annotations from freeroute.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