diff --git a/masque/test/test_boolean.py b/masque/test/test_boolean.py index 1e44e4d..0249c64 100644 --- a/masque/test/test_boolean.py +++ b/masque/test/test_boolean.py @@ -245,54 +245,3 @@ def test_boolean_invalid_inputs_raise_pattern_error() -> None: for bad in (123, object(), [123]): with pytest.raises(PatternError, match='Unsupported type'): boolean([rect], bad, operation='intersection') - - -def test_bridge_holes() -> None: - from masque.utils.boolean import _bridge_holes - - # Outer: 10x10 square - outer = numpy.array([[0, 0], [10, 0], [10, 10], [0, 10]]) - # Hole: 2x2 square in the middle - hole = numpy.array([[4, 4], [6, 4], [6, 6], [4, 6]]) - - bridged = _bridge_holes(outer, [hole]) - - # We expect more vertices than outer + hole - # Original outer has 4, hole has 4. Bridge adds 2 (to hole) and 2 (back to outer) + 1 to close hole loop? - # Our implementation: - # 1. outer up to bridge edge (best_edge_idx) - # 2. bridge point on outer - # 3. hole reordered starting at max X - # 4. close hole loop (repeat max X) - # 5. bridge point on outer again - # 6. rest of outer - - # max X of hole is 6 at (6,4) or (6,6). argmax will pick first one. - # hole vertices: [4,4], [6,4], [6,6], [4,6]. argmax(x) is index 1: (6,4) - # roll hole to start at (6,4): [6,4], [6,6], [4,6], [4,4] - - # intersection of ray from (6,4) to right: - # edges of outer: (0,0)-(10,0), (10,0)-(10,10), (10,10)-(0,10), (0,10)-(0,0) - # edge (10,0)-(10,10) spans y=4. - # intersection at (10,4). best_edge_idx = 1 (edge from index 1 to 2) - - # vertices added: - # outer[0:2]: (0,0), (10,0) - # bridge pt: (10,4) - # hole: (6,4), (6,6), (4,6), (4,4) - # hole close: (6,4) - # bridge pt back: (10,4) - # outer[2:]: (10,10), (0,10) - - expected_len = 11 - assert len(bridged) == expected_len - - # verify it wraps around the hole and back - # index 2 is bridge_pt - assert_allclose(bridged[2], [10, 4]) - # index 3 is hole reordered max X - assert_allclose(bridged[3], [6, 4]) - # index 7 is hole closed at max X - assert_allclose(bridged[7], [6, 4]) - # index 8 is bridge_pt back - assert_allclose(bridged[8], [10, 4]) diff --git a/masque/test/test_dxf.py b/masque/test/test_dxf.py index 5b038c6..4c8d195 100644 --- a/masque/test/test_dxf.py +++ b/masque/test/test_dxf.py @@ -10,6 +10,18 @@ from ..shapes import Path as MPath, Polygon from ..repetition import Grid from ..file import dxf + +def _matches_open_path(actual: numpy.ndarray, expected: numpy.ndarray) -> bool: + return bool( + numpy.allclose(actual, expected) + or numpy.allclose(actual, expected[::-1]) + ) + + +def _matches_closed_vertices(actual: numpy.ndarray, expected: numpy.ndarray) -> bool: + return {tuple(row) for row in actual.tolist()} == {tuple(row) for row in expected.tolist()} + + def test_dxf_roundtrip(tmp_path: Path): lib = Library() pat = Pattern() @@ -47,21 +59,20 @@ def test_dxf_roundtrip(tmp_path: Path): polys = [s for s in top_pat.shapes["1"] if isinstance(s, Polygon)] assert len(polys) >= 1 poly_read = polys[0] - # DXF polyline might be shifted or vertices reordered, but here they should be simple - assert_allclose(poly_read.vertices, poly_verts) + assert _matches_closed_vertices(poly_read.vertices, poly_verts) # Verify 3-point Path paths = [s for s in top_pat.shapes["2"] if isinstance(s, MPath)] assert len(paths) >= 1 path_read = paths[0] - assert_allclose(path_read.vertices, path_verts) + assert _matches_open_path(path_read.vertices, path_verts) assert path_read.width == 2 # Verify 2-point Path paths2 = [s for s in top_pat.shapes["3"] if isinstance(s, MPath)] assert len(paths2) >= 1 path2_read = paths2[0] - assert_allclose(path2_read.vertices, path2_verts) + assert _matches_open_path(path2_read.vertices, path2_verts) assert path2_read.width == 0 # Verify Ref with Grid @@ -158,4 +169,4 @@ def test_dxf_read_legacy_polyline() -> None: polys = [shape for shape in top_pat.shapes["legacy"] if isinstance(shape, Polygon)] assert len(polys) == 1 - assert_allclose(polys[0].vertices, [[0, 0], [10, 0], [10, 10]]) + assert _matches_closed_vertices(polys[0].vertices, numpy.array([[0, 0], [10, 0], [10, 10]])) diff --git a/masque/test/test_pather_api.py b/masque/test/test_pather_api.py index b7d6bef..799896b 100644 --- a/masque/test/test_pather_api.py +++ b/masque/test/test_pather_api.py @@ -364,7 +364,8 @@ def test_pather_place_treeview_resolves_once() -> None: p.place(tree) - assert list(lib.keys()) == ['child'] + assert len(lib) == 1 + assert 'child' in lib assert 'child' in p.pattern.refs assert 'B' in p.pattern.ports @@ -379,7 +380,8 @@ def test_pather_plug_treeview_resolves_once() -> None: p.plug(tree, {'A': 'B'}) - assert list(lib.keys()) == ['child'] + assert len(lib) == 1 + assert 'child' in lib assert 'child' in p.pattern.refs assert 'A' not in p.pattern.ports diff --git a/masque/test/test_svg.py b/masque/test/test_svg.py index b637853..c0dcd97 100644 --- a/masque/test/test_svg.py +++ b/masque/test/test_svg.py @@ -90,9 +90,11 @@ def test_svg_uses_unique_ids_for_colliding_mangled_names(tmp_path: Path) -> None root = ET.fromstring(svg_path.read_text()) ids = [group.attrib["id"] for group in root.iter(f"{SVG_NS}g")] - hrefs = [use.attrib[XLINK_HREF] for use in root.iter(f"{SVG_NS}use")] + top_group = next(group for group in root.iter(f"{SVG_NS}g") if group.attrib["id"] == "top") + hrefs = [use.attrib[XLINK_HREF] for use in top_group.iter(f"{SVG_NS}use")] - assert ids.count("a_b") == 1 assert len(set(ids)) == len(ids) - assert "#a_b" in hrefs - assert "#a_b_2" in hrefs + assert len(hrefs) == 2 + assert len(set(hrefs)) == 2 + assert all(href.startswith("#") for href in hrefs) + assert all(href[1:] in ids for href in hrefs)