[dxf] add roundtrip dxf test, enable refs and improve path handling
This commit is contained in:
parent
e261585894
commit
564ff10db3
2 changed files with 164 additions and 18 deletions
111
masque/test/test_dxf.py
Normal file
111
masque/test/test_dxf.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
|
||||
import numpy
|
||||
from numpy.testing import assert_allclose
|
||||
from pathlib import Path
|
||||
|
||||
from ..pattern import Pattern
|
||||
from ..library import Library
|
||||
from ..shapes import Path as MPath, Polygon
|
||||
from ..repetition import Grid
|
||||
from ..file import dxf
|
||||
|
||||
def test_dxf_roundtrip(tmp_path: Path):
|
||||
lib = Library()
|
||||
pat = Pattern()
|
||||
|
||||
# 1. Polygon (closed)
|
||||
poly_verts = numpy.array([[0, 0], [10, 0], [10, 10], [0, 10]])
|
||||
pat.polygon("1", vertices=poly_verts)
|
||||
|
||||
# 2. Path (open, 3 points)
|
||||
path_verts = numpy.array([[20, 0], [30, 0], [30, 10]])
|
||||
pat.path("2", vertices=path_verts, width=2)
|
||||
|
||||
# 3. Path (open, 2 points) - Testing the fix for 2-point polylines
|
||||
path2_verts = numpy.array([[40, 0], [50, 10]])
|
||||
pat.path("3", vertices=path2_verts, width=0) # width 0 to be sure it's not a polygonized path if we're not careful
|
||||
|
||||
# 4. Ref with Grid repetition (Manhattan)
|
||||
subpat = Pattern()
|
||||
subpat.polygon("sub", vertices=[[0, 0], [1, 0], [1, 1]])
|
||||
lib["sub"] = subpat
|
||||
|
||||
pat.ref("sub", offset=(100, 100), repetition=Grid(a_vector=(10, 0), a_count=2, b_vector=(0, 10), b_count=3))
|
||||
|
||||
lib["top"] = pat
|
||||
|
||||
dxf_file = tmp_path / "test.dxf"
|
||||
dxf.writefile(lib, "top", dxf_file)
|
||||
|
||||
read_lib, _ = dxf.readfile(dxf_file)
|
||||
|
||||
# In DXF read, the top level is usually called "Model"
|
||||
top_pat = read_lib.get("Model") or read_lib.get("top") or list(read_lib.values())[0]
|
||||
|
||||
# Verify Polygon
|
||||
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)
|
||||
|
||||
# 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 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 path2_read.width == 0
|
||||
|
||||
# Verify Ref with Grid
|
||||
# Finding the sub pattern name might be tricky because of how DXF stores blocks
|
||||
# but "sub" should be in read_lib
|
||||
assert "sub" in read_lib
|
||||
|
||||
# Check refs in the top pattern
|
||||
found_grid = False
|
||||
for target, reflist in top_pat.refs.items():
|
||||
# DXF names might be case-insensitive or modified, but ezdxf usually preserves them
|
||||
if target.upper() == "SUB":
|
||||
for ref in reflist:
|
||||
if isinstance(ref.repetition, Grid):
|
||||
assert ref.repetition.a_count == 2
|
||||
assert ref.repetition.b_count == 3
|
||||
assert_allclose(ref.repetition.a_vector, (10, 0))
|
||||
assert_allclose(ref.repetition.b_vector, (0, 10))
|
||||
found_grid = True
|
||||
assert found_grid, f"Manhattan Grid repetition should have been preserved. Targets: {list(top_pat.refs.keys())}"
|
||||
|
||||
def test_dxf_manhattan_precision(tmp_path: Path):
|
||||
# Test that float precision doesn't break Manhattan grid detection
|
||||
lib = Library()
|
||||
sub = Pattern()
|
||||
sub.polygon("1", vertices=[[0, 0], [1, 0], [1, 1]])
|
||||
lib["sub"] = sub
|
||||
|
||||
top = Pattern()
|
||||
# 90 degree rotation: in masque the grid is NOT rotated, so it stays [[10,0],[0,10]]
|
||||
# In DXF, an array with rotation 90 has basis vectors [[0,10],[-10,0]].
|
||||
# So a masque grid [[10,0],[0,10]] with ref rotation 90 matches a DXF array.
|
||||
angle = numpy.pi / 2 # 90 degrees
|
||||
top.ref("sub", offset=(0, 0), rotation=angle,
|
||||
repetition=Grid(a_vector=(10, 0), a_count=2, b_vector=(0, 10), b_count=2))
|
||||
|
||||
lib["top"] = top
|
||||
|
||||
dxf_file = tmp_path / "precision.dxf"
|
||||
dxf.writefile(lib, "top", dxf_file)
|
||||
|
||||
# If the isclose() fix works, this should still be a Grid when read back
|
||||
read_lib, _ = dxf.readfile(dxf_file)
|
||||
read_top = read_lib.get("Model") or read_lib.get("top") or list(read_lib.values())[0]
|
||||
|
||||
target_name = next(k for k in read_top.refs if k.upper() == "SUB")
|
||||
ref = read_top.refs[target_name][0]
|
||||
assert isinstance(ref.repetition, Grid), "Grid should be preserved for 90-degree rotation"
|
||||
Loading…
Add table
Add a link
Reference in a new issue