[ILibraryView / Pattern] flatten() shouldn't drop ports-only patterns if flatten_ports=True

This commit is contained in:
Jan Petykiewicz 2026-03-30 21:11:00 -07:00
commit b843ffb4d3
4 changed files with 38 additions and 3 deletions

View file

@ -303,7 +303,8 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
target_pat = flattened[target] target_pat = flattened[target]
if target_pat is None: if target_pat is None:
raise PatternError(f'Circular reference in {name} to {target}') raise PatternError(f'Circular reference in {name} to {target}')
if target_pat.is_empty(): # avoid some extra allocations ports_only = flatten_ports and bool(target_pat.ports)
if target_pat.is_empty() and not ports_only: # avoid some extra allocations
continue continue
for ref in pat.refs[target]: for ref in pat.refs[target]:

View file

@ -1077,7 +1077,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
if target_pat is None: if target_pat is None:
raise PatternError(f'Circular reference in {name} to {target}') raise PatternError(f'Circular reference in {name} to {target}')
if target_pat.is_empty(): # avoid some extra allocations ports_only = flatten_ports and bool(target_pat.ports)
if target_pat.is_empty() and not ports_only: # avoid some extra allocations
continue continue
for ref in refs: for ref in refs:

View file

@ -2,7 +2,9 @@ import pytest
from typing import cast, TYPE_CHECKING from typing import cast, TYPE_CHECKING
from ..library import Library, LazyLibrary from ..library import Library, LazyLibrary
from ..pattern import Pattern from ..pattern import Pattern
from ..error import LibraryError from ..error import LibraryError, PatternError
from ..ports import Port
from ..repetition import Grid
if TYPE_CHECKING: if TYPE_CHECKING:
from ..shapes import Polygon from ..shapes import Polygon
@ -59,6 +61,22 @@ def test_library_flatten() -> None:
assert tuple(assert_vertices[0]) == (10.0, 10.0) assert tuple(assert_vertices[0]) == (10.0, 10.0)
def test_library_flatten_preserves_ports_only_child() -> None:
lib = Library()
child = Pattern(ports={"P1": Port((1, 2), 0)})
lib["child"] = child
parent = Pattern()
parent.ref("child", offset=(10, 10))
lib["parent"] = parent
flat_parent = lib.flatten("parent", flatten_ports=True)["parent"]
assert set(flat_parent.ports) == {"P1"}
assert cast("Port", flat_parent.ports["P1"]).rotation == 0
assert tuple(flat_parent.ports["P1"].offset) == (11.0, 12.0)
def test_lazy_library() -> None: def test_lazy_library() -> None:
lib = LazyLibrary() lib = LazyLibrary()
called = 0 called = 0

View file

@ -1,12 +1,15 @@
import pytest
from typing import cast from typing import cast
from numpy.testing import assert_equal, assert_allclose from numpy.testing import assert_equal, assert_allclose
from numpy import pi from numpy import pi
from ..error import PatternError
from ..pattern import Pattern from ..pattern import Pattern
from ..shapes import Polygon from ..shapes import Polygon
from ..ref import Ref from ..ref import Ref
from ..ports import Port from ..ports import Port
from ..label import Label from ..label import Label
from ..repetition import Grid
def test_pattern_init() -> None: def test_pattern_init() -> None:
@ -99,6 +102,18 @@ def test_pattern_get_bounds() -> None:
assert_equal(bounds, [[-5, -5], [10, 10]]) assert_equal(bounds, [[-5, -5], [10, 10]])
def test_pattern_flatten_preserves_ports_only_child() -> None:
child = Pattern(ports={"P1": Port((1, 2), 0)})
parent = Pattern()
parent.ref("child", offset=(10, 10))
parent.flatten({"child": child}, flatten_ports=True)
assert set(parent.ports) == {"P1"}
assert parent.ports["P1"].rotation == 0
assert tuple(parent.ports["P1"].offset) == (11.0, 12.0)
def test_pattern_interface() -> None: def test_pattern_interface() -> None:
source = Pattern() source = Pattern()
source.ports["A"] = Port((10, 20), 0, ptype="test") source.ports["A"] = Port((10, 20), 0, ptype="test")