Fix more bugs, refined round-trip tests pass now
This commit is contained in:
parent
cf4957aee4
commit
e4941dd5e3
6 changed files with 32 additions and 136 deletions
119
.gitignore
vendored
119
.gitignore
vendored
|
|
@ -1,115 +1,4 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
gerbonara_test_failures
|
||||
*.egg-info
|
||||
__pycache__
|
||||
.tox
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ class FileSettings:
|
|||
value = value.lstrip('+-')
|
||||
|
||||
if self.zeros == 'leading':
|
||||
value = '0'*decimal_digits + value # pad with zeros to ensure we have enough decimals
|
||||
return float(sign + value[:-decimal_digits] + '.' + value[-decimal_digits:])
|
||||
|
||||
else: # no or trailing zero suppression
|
||||
|
|
|
|||
|
|
@ -505,6 +505,7 @@ class GerberParser:
|
|||
y = self.file_settings.parse_gerber_value(match['y'])
|
||||
i = self.file_settings.parse_gerber_value(match['i'])
|
||||
j = self.file_settings.parse_gerber_value(match['j'])
|
||||
print(f'coord x={x} y={y} i={i} j={j}')
|
||||
|
||||
if not (op := match['operation']):
|
||||
if self.last_operation == 'D01':
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import numpy as np
|
|||
from PIL import Image
|
||||
|
||||
class ImageDifference:
|
||||
def __init__(self, value, ref_path, act_path):
|
||||
self.value, self.ref_path, self.act_path = value, ref_path, act_path
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __float__(self):
|
||||
return float(self.value)
|
||||
|
|
@ -47,34 +47,32 @@ def gbr_to_svg(in_gbr, out_svg, origin=(0, 0), size=(10, 10)):
|
|||
'-o', str(out_svg), str(in_gbr)]
|
||||
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
def gerber_difference(reference, actual):
|
||||
def gerber_difference(reference, actual, diff_out=None):
|
||||
with tempfile.NamedTemporaryFile(suffix='.svg') as act_svg,\
|
||||
tempfile.NamedTemporaryFile(suffix='.svg') as ref_svg:
|
||||
|
||||
gbr_to_svg(reference, ref_svg.name)
|
||||
gbr_to_svg(actual, act_svg.name)
|
||||
|
||||
diff = svg_difference(ref_svg.name, act_svg.name)
|
||||
diff.ref_path, diff.act_path = reference, actual
|
||||
return diff
|
||||
return svg_difference(ref_svg.name, act_svg.name, diff_out=diff_out)
|
||||
|
||||
def svg_difference(reference, actual):
|
||||
def svg_difference(reference, actual, diff_out=None):
|
||||
with tempfile.NamedTemporaryFile(suffix='.png') as ref_png,\
|
||||
tempfile.NamedTemporaryFile(suffix='.png') as act_png:
|
||||
|
||||
svg_to_png(reference, ref_png.name)
|
||||
svg_to_png(actual, act_png.name)
|
||||
|
||||
diff = image_difference(ref_png.name, act_png.name)
|
||||
diff.ref_path, diff.act_path = reference, actual
|
||||
return diff
|
||||
return image_difference(ref_png.name, act_png.name, diff_out=diff_out)
|
||||
|
||||
def image_difference(reference, actual):
|
||||
def image_difference(reference, actual, diff_out=None):
|
||||
ref = np.array(Image.open(reference)).astype(float)
|
||||
out = np.array(Image.open(actual)).astype(float)
|
||||
|
||||
ref, out = ref.mean(axis=2), out.mean(axis=2) # convert to grayscale
|
||||
delta = np.abs(out - ref).astype(float) / 255
|
||||
return ImageDifference(delta.mean(), reference, actual)
|
||||
if diff_out:
|
||||
Image.fromarray((delta*255).astype(np.uint8), mode='L').save(diff_out)
|
||||
return ImageDifference(delta.mean()), ImageDifference(delta.max())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ Y60000D01*
|
|||
X50000D01*
|
||||
Y50000Y50000D01*
|
||||
G37*
|
||||
M02*
|
||||
M02*
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ def clear_failure_dir(request):
|
|||
reference_path = lambda reference: Path(__file__).parent / 'resources' / reference
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_gbr(request):
|
||||
with tempfile.NamedTemporaryFile(suffix='.gbr') as tmp_out_gbr:
|
||||
def temp_files(request):
|
||||
with tempfile.NamedTemporaryFile(suffix='.gbr') as tmp_out_gbr,\
|
||||
tempfile.NamedTemporaryFile(suffix='.png') as tmp_out_png:
|
||||
|
||||
yield Path(tmp_out_gbr.name)
|
||||
yield Path(tmp_out_gbr.name), Path(tmp_out_png.name)
|
||||
|
||||
if request.node.rep_call.failed:
|
||||
module, _, test_name = request.node.nodeid.rpartition('::')
|
||||
|
|
@ -36,9 +37,12 @@ def tmp_gbr(request):
|
|||
test_name, _, _ext = test_name.partition('.')
|
||||
test_name = re.sub(r'[^\w\d]', '_', test_name)
|
||||
fail_dir.mkdir(exist_ok=True)
|
||||
perm_path = fail_dir / f'failure_{test_name}.gbr'
|
||||
shutil.copy(tmp_out_gbr.name, perm_path)
|
||||
print(f'Failing output saved to {perm_path}')
|
||||
perm_path_gbr = fail_dir / f'failure_{test_name}.gbr'
|
||||
perm_path_png = fail_dir / f'failure_{test_name}.png'
|
||||
shutil.copy(tmp_out_gbr.name, perm_path_gbr)
|
||||
shutil.copy(tmp_out_png.name, perm_path_png)
|
||||
print(f'Failing output saved to {perm_path_gbr}')
|
||||
print(f'Difference image saved to {perm_path_png}')
|
||||
print(f'Reference file is {reference_path(request.node.funcargs["reference"])}')
|
||||
|
||||
@pytest.mark.filterwarnings('ignore:Deprecated.*statement found.*:DeprecationWarning')
|
||||
|
|
@ -88,8 +92,11 @@ top_copper.GTL
|
|||
top_mask.GTS
|
||||
top_silk.GTO
|
||||
'''.splitlines() if l ])
|
||||
def test_round_trip(tmp_gbr, reference):
|
||||
def test_round_trip(temp_files, reference):
|
||||
tmp_gbr, tmp_png = temp_files
|
||||
ref = reference_path(reference)
|
||||
GerberFile.open(ref).save(tmp_gbr)
|
||||
assert gerber_difference(ref, tmp_gbr) < 1e-5
|
||||
mean, max = gerber_difference(ref, tmp_gbr, diff_out=tmp_png)
|
||||
assert mean < 1e-6
|
||||
assert max < 0.1
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue