Fix hole transparency in aperture macros

This commit is contained in:
jaseg 2021-06-13 22:15:05 +02:00
parent d0f836ecfa
commit 66da2d1654
5 changed files with 74 additions and 18 deletions

View file

@ -1064,9 +1064,6 @@ class RoundRectangle(Primitive):
class Obround(Primitive):
"""
"""
def __init__(self, position, width, height, hole_diameter=0,
hole_width=0,hole_height=0, **kwargs):
super(Obround, self).__init__(**kwargs)

View file

@ -513,9 +513,21 @@ class GerberCairoContext(GerberContext):
self.ctx.mask_surface(mask.surface, self.origin_in_pixels[0])
def _render_amgroup(self, amgroup, color):
mask_surface = cairo.SVGSurface(None, self.size_in_pixels[0], self.size_in_pixels[1])
mask_ctx = cairo.Context(mask_surface)
mask_ctx.set_matrix(self.ctx.get_matrix())
old_surface, self.surface = self.surface, mask_surface
old_ctx, self.ctx = self.ctx, mask_ctx
for primitive in amgroup.primitives:
self.render(primitive)
old_ctx.mask_surface(mask_surface, self.origin_in_pixels[0])
mask_surface.finish()
self.surface, self.ctx = old_surface, old_ctx
def _render_test_record(self, primitive, color):
position = [pos + origin for pos, origin in
zip(primitive.position, self.origin_in_inch)]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Before After
Before After

View file

@ -1,16 +1,16 @@
G04 Umaco example for exposure modifier and clearing area*
%FSLAX26Y26*%
%MOIN*%
%AMSQUAREWITHHOLE*
21,0.1,1,1,0,0,0*
1,0,0.5,0,0*%
%ADD10SQUAREWITHHOLE*%
%MOMM*%
%AMSquareWithHole*
21,1,10,10,0,0,0*
1,0,5,0,0*%
%ADD10SquareWithHole*%
%ADD11C,1*%
G01*
%LPD*%
D11*
X-1000000Y-250000D02*
X1000000Y250000D01*
X-08939393Y-2500000D02*
X08939393Y2500000D01*
D10*
X0Y0D03*
M02*
M02*

View file

@ -7,6 +7,7 @@ import shutil
import io
import tempfile
import uuid
import cv2
from pathlib import Path
import pytest
@ -201,12 +202,16 @@ def test_holes_dont_clear():
)
def _DISABLED_test_render_am_exposure_modifier():
def test_render_am_exposure_modifier():
"""Umaco example that an aperture macro with a hole does not clear the area"""
_test_render(
"resources/example_am_exposure_modifier.gbr",
"golden/example_am_exposure_modifier.png",
scale = 50,
autocrop_golden = True,
auto_contrast = True,
max_delta = 0.005 # Take artifacts due to differences in anti-aliasing and thresholding into account
)
@ -252,19 +257,59 @@ def test_fine_lines_x():
def _resolve_path(path):
return os.path.join(os.path.dirname(__file__), path)
def images_match(reference, output, max_delta):
def images_match(reference, output, max_delta, autocrop_golden=False, auto_contrast=False):
global output_dir
ref, out = Image.open(reference), Image.open(output)
if ref.mode == 'P': # palette mode
ref = ref.convert('RGB')
ref, out = np.array(ref), np.array(out)
# convert to grayscale
ref, out = ref.astype(float).mean(axis=2), out.astype(float).mean(axis=2)
if autocrop_golden:
rows = ref.sum(axis=1)
cols = ref.sum(axis=0)
x0 = np.argmax(cols > 0)
y0 = np.argmax(rows > 0)
x1 = len(cols) - np.argmax(cols[::-1] > 0)
y1 = len(rows) - np.argmax(rows[::-1] > 0)
print(f'{x0=} {y0=} {x1=} {y1=}')
ref = ref[y0:y1, x0:x1]
ref = cv2.resize(ref, dsize=out.shape[::-1], interpolation=cv2.INTER_LINEAR)
def print_stats(name, ref):
print(name, 'stats:', ref.min(), ref.mean(), ref.max(), 'std:', ref.std())
if auto_contrast:
print_stats('ref pre proc', ref)
print_stats('out pre proc', out)
ref -= ref.min()
ref /= ref.max()
ref *= 255
out -= out.min()
out /= out.max()
out *= 255
def write_refout():
nonlocal autocrop_golden, ref
if autocrop_golden:
global output_dir
with output_dir.create(suffix='.png') as ref_out:
cv2.imwrite(str(ref_out), ref)
print('Processed reference image:', ref_out)
if ref.shape != out.shape:
print(f'Rendering image size mismatch')
print(f'Rendering image size mismatch: {ref.shape} != {out.shape}')
print(f'Reference image: {Path(reference).absolute()}')
print(f'Actual output: {output}')
write_refout()
output_dir.keep()
return False
@ -275,11 +320,13 @@ def images_match(reference, output, max_delta):
print(f'Renderings mismatch: {delta.mean()=}, {max_delta=}')
print(f'Reference image: {Path(reference).absolute()}')
print(f'Actual output: {output}')
def print_stats(name, ref):
print(name, 'stats:', ref.min(), ref.mean(), ref.max(), 'std:', ref.std())
with output_dir.create(suffix='.png') as proc_out:
cv2.imwrite(str(proc_out), out)
print('Processed output image:', proc_out)
print_stats('reference', ref)
print_stats('actual', out)
write_refout()
output_dir.keep()
return False
@ -287,7 +334,7 @@ def images_match(reference, output, max_delta):
return True
def _test_render(gerber_path, png_expected_path, max_delta=1e-6, scale=300):
def _test_render(gerber_path, png_expected_path, max_delta=1e-6, scale=300, autocrop_golden=False, auto_contrast=False):
"""Render the gerber file and compare to the expected PNG output.
Parameters
@ -314,7 +361,7 @@ def _test_render(gerber_path, png_expected_path, max_delta=1e-6, scale=300):
with output_dir.create(suffix='.png') as outfile:
actual_bytes = ctx.dump(outfile)
assert images_match(png_expected_path, outfile, max_delta)
assert images_match(png_expected_path, outfile, max_delta, autocrop_golden, auto_contrast)
return gerber