"""Tests for the exact ShapeSearchTree spatial index.""" from __future__ import annotations from freeroute.board import ShapeSearchTree, TreeShape from freeroute.geometry import IntBox def shape(owner, net, layer, box, routed=False): return TreeShape(owner, net, frozenset({layer}), box, routed=routed) def test_insert_and_overlapping_exact(): tree = ShapeSearchTree(bucket_size=100) tree.insert(shape(0, 1, 0, IntBox(0, 0, 50, 50))) tree.insert(shape(1, 2, 0, IntBox(200, 200, 250, 250))) # a query box overlapping only the first shape hit = tree.overlapping(IntBox(40, 40, 60, 60)) assert [s.owner for s in hit] == [0] # a query far from everything assert tree.overlapping(IntBox(1000, 1000, 1010, 1010)) == [] def test_touching_boxes_do_not_overlap_but_do_intersect(): tree = ShapeSearchTree(bucket_size=100) tree.insert(shape(0, 1, 0, IntBox(0, 0, 10, 10))) # shares the edge x=10 -> intersection is 1-D (found by broad phase) assert len(tree.overlapping(IntBox(10, 0, 20, 10))) == 1 def test_remove_owner(): tree = ShapeSearchTree(bucket_size=100) tree.insert(shape(0, 1, 0, IntBox(0, 0, 50, 50))) tree.insert(shape(0, 1, 0, IntBox(60, 0, 90, 50))) # same owner, 2 tiles tree.insert(shape(1, 2, 0, IntBox(0, 0, 50, 50))) tree.remove_owner(0) assert all(s.owner == 1 for s in tree.all_shapes()) assert len(tree.all_shapes()) == 1 def test_clearance_conflict_respects_the_clearance_gap(): tree = ShapeSearchTree(bucket_size=1000) tree.insert(shape(0, 1, 0, IntBox(0, 0, 100, 100), routed=True)) # a net-2 box exactly `clearance` (20) to the right -> allowed (touching) ok = IntBox(120, 0, 200, 100) assert not tree.clearance_conflict(ok, net_no=2, layer=0, clearance=20) # one unit closer -> conflict bad = IntBox(119, 0, 200, 100) assert tree.clearance_conflict(bad, net_no=2, layer=0, clearance=20) # same net never conflicts assert not tree.clearance_conflict(bad, net_no=1, layer=0, clearance=20) # different layer never conflicts assert not tree.clearance_conflict(bad, net_no=2, layer=1, clearance=20) def test_has_violation_ignores_static_pad_pairs(): tree = ShapeSearchTree(bucket_size=1000) # two different-net *static* pads closer than clearance -> not a routing DRC tree.insert(shape(0, 1, 0, IntBox(0, 0, 10, 10), routed=False)) tree.insert(shape(1, 2, 0, IntBox(11, 0, 20, 10), routed=False)) assert tree.has_violation(clearance=20) is None # a routed trace of net 3 within clearance of pad net 1 -> violation tree.insert(shape(2, 3, 0, IntBox(11, 0, 20, 10), routed=True)) assert tree.has_violation(clearance=20) is not None