diff --git a/masque/builder/planner/planner.py b/masque/builder/planner/planner.py index 282a835..5efcaa1 100644 --- a/masque/builder/planner/planner.py +++ b/masque/builder/planner/planner.py @@ -32,7 +32,6 @@ from __future__ import annotations from collections.abc import Iterable, Mapping, Sequence from dataclasses import dataclass, replace from itertools import combinations -from math import cos, sin from typing import Any, Literal import numpy @@ -41,7 +40,7 @@ from numpy.typing import ArrayLike from ...error import BuildError, PortError from ...ports import Port -from ...utils import PTypeMatch, SupportsBool, ptype_match, ptypes_compatible +from ...utils import PTypeMatch, SupportsBool, ptype_match, ptypes_compatible, rotation_matrix_2d from ..tools import ( BendOffer, PrimitiveKind, @@ -579,10 +578,11 @@ class Solver: out_port = step.out_port if out_port.rotation is None: raise ToolContractError('Primitive endpoints must have rotation') - angle_cos = cos(angle) - angle_sin = sin(angle) - x += angle_cos * float(out_port.x) - angle_sin * float(out_port.y) - y += angle_sin * float(out_port.x) + angle_cos * float(out_port.y) + rotation = rotation_matrix_2d(angle) + out_x = float(out_port.x) + out_y = float(out_port.y) + x += rotation[0, 0] * out_x + rotation[0, 1] * out_y + y += rotation[1, 0] * out_x + rotation[1, 1] * out_y angle += out_port.rotation + pi ptype = out_port.ptype return Port((x, y), rotation=angle - pi, ptype=ptype) diff --git a/masque/ports.py b/masque/ports.py index bbd18b7..552f081 100644 --- a/masque/ports.py +++ b/masque/ports.py @@ -574,10 +574,10 @@ class PortList(metaclass=ABCMeta): raise PortError(msg) translations = a_offsets - b_offsets - if not numpy.allclose(translations, 0): + if not numpy.allclose(a_offsets, b_offsets): msg = 'Port translations do not match:\n' for nn, (kk, vv) in enumerate(connections.items()): - if not numpy.allclose(translations[nn], 0): + if not numpy.allclose(a_offsets[nn], b_offsets[nn]): msg += f'{kk} | {translations[nn]} | {vv}\n' raise PortError(msg) diff --git a/masque/test/test_pather_trace_into.py b/masque/test/test_pather_trace_into.py index 9e5e268..48ab125 100644 --- a/masque/test/test_pather_trace_into.py +++ b/masque/test/test_pather_trace_into.py @@ -113,6 +113,22 @@ def test_pather_trace_into_shapes() -> None: assert numpy.isclose(p.pattern.ports['I'].rotation, pi / 2) +def test_pather_trace_into_large_composed_manhattan_route_plugs() -> None: + p = Pather( + Library(), + tools=PathTool(layer='M1', width=1000, ptype='wire'), + render='deferred', + ) + p.ports['src'] = Port((123.25, 456.75), rotation=0, ptype='wire') + p.ports['dst'] = Port((-99_999_876.75, 20_000_456.75), rotation=0, ptype='wire') + + p.trace_into('src', 'dst') + + assert 'src' not in p.ports + assert 'dst' not in p.ports + assert [step.kind for step in p._paths['src']] == ['straight', 'bend', 'straight', 'bend', 'plug'] + + def test_pather_trace_into_refines_output_adapter_route_before_plug() -> None: class TransitionTool(Tool): def primitive_offers( diff --git a/masque/test/test_ports.py b/masque/test/test_ports.py index f17921f..a4c560a 100644 --- a/masque/test/test_ports.py +++ b/masque/test/test_ports.py @@ -169,6 +169,17 @@ def test_port_list_plugged() -> None: assert not pl.ports # Both should be removed +def test_port_list_plugged_uses_coordinate_relative_tolerance() -> None: + pattern = Pattern(ports={ + "A": Port((10, 10), 0), + "B": Port((10 + 5e-8, 10), pi), + }) + + pattern.plugged({"A": "B"}) + + assert not pattern.ports + + def test_port_list_plugged_ptype_compatibility_warnings(caplog: pytest.LogCaptureFixture) -> None: caplog.set_level("WARNING", logger="masque.ports")