More tests and bugfixes
This commit is contained in:
parent
360eddc3c4
commit
d98d23f8b5
9 changed files with 227 additions and 102 deletions
|
|
@ -25,15 +25,15 @@ if __name__ == '__main__':
|
|||
sys.exit(1)
|
||||
|
||||
ctx = GerberSvgContext()
|
||||
ctx.set_alpha(0.95)
|
||||
ctx.alpha = 0.95
|
||||
for filename in sys.argv[1:]:
|
||||
print "parsing %s" % filename
|
||||
if 'GTO' in filename or 'GBO' in filename:
|
||||
ctx.set_color((1, 1, 1))
|
||||
ctx.set_alpha(0.8)
|
||||
ctx.color = (1, 1, 1)
|
||||
ctx.alpha = 0.8
|
||||
elif 'GTS' in filename or 'GBS' in filename:
|
||||
ctx.set_color((0.2, 0.2, 0.75))
|
||||
ctx.set_alpha(0.8)
|
||||
ctx.color = (0.2, 0.2, 0.75)
|
||||
ctx.alpha = 0.8
|
||||
gerberfile = read(filename)
|
||||
gerberfile.render(ctx)
|
||||
|
||||
|
|
|
|||
|
|
@ -62,29 +62,24 @@ class FileSettings(object):
|
|||
if units not in ['inch', 'metric']:
|
||||
raise ValueError('Units must be either inch or metric')
|
||||
self.units = units
|
||||
|
||||
|
||||
|
||||
if zero_suppression is None and zeros is None:
|
||||
self.zero_suppression = 'trailing'
|
||||
|
||||
|
||||
elif zero_suppression == zeros:
|
||||
raise ValueError('Zeros and Zero Suppression must be different. \
|
||||
Best practice is to specify only one.')
|
||||
|
||||
|
||||
elif zero_suppression is not None:
|
||||
if zero_suppression not in ['leading', 'trailing']:
|
||||
raise ValueError('Zero suppression must be either leading or \
|
||||
trailling')
|
||||
self.zero_suppression = zero_suppression
|
||||
|
||||
|
||||
elif zeros is not None:
|
||||
if zeros not in ['leading', 'trailing']:
|
||||
raise ValueError('Zeros must be either leading or trailling')
|
||||
self.zeros = zeros
|
||||
else:
|
||||
self.zeros = 'leading'
|
||||
|
||||
|
||||
if len(format) != 2:
|
||||
raise ValueError('Format must be a tuple(n=2) of integers')
|
||||
|
|
@ -150,6 +145,9 @@ class FileSettings(object):
|
|||
raise ValueError('Format must be a tuple(n=2) of integers')
|
||||
self.format = value
|
||||
|
||||
else:
|
||||
raise KeyError('%s is not a valid key' % key)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.notation == other.notation and
|
||||
self.units == other.units and
|
||||
|
|
@ -230,7 +228,7 @@ class CamFile(object):
|
|||
def bounds(self):
|
||||
""" File baundaries
|
||||
"""
|
||||
pass
|
||||
raise NotImplementedError('bounds must be implemented in a subclass')
|
||||
|
||||
def render(self, ctx, filename=None):
|
||||
""" Generate image of layer.
|
||||
|
|
|
|||
|
|
@ -39,4 +39,5 @@ def read(filename):
|
|||
elif fmt == 'excellon':
|
||||
return excellon.read(filename)
|
||||
else:
|
||||
return None
|
||||
raise TypeError('Unable to detect file format')
|
||||
|
||||
|
|
|
|||
|
|
@ -345,21 +345,26 @@ class Obround(Primitive):
|
|||
self.position = position
|
||||
self.width = width
|
||||
self.height = height
|
||||
# Axis-aligned width and height
|
||||
self._abs_width = (math.cos(math.radians(self.rotation)) * self.width +
|
||||
math.sin(math.radians(self.rotation)) * self.height)
|
||||
self._abs_height = (math.cos(math.radians(self.rotation)) * self.height +
|
||||
math.sin(math.radians(self.rotation)) * self.width)
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return (self.position[0] - (self._abs_width / 2.),
|
||||
self.position[1] - (self._abs_height / 2.))
|
||||
|
||||
@property
|
||||
def upper_right(self):
|
||||
return (self.position[0] + (self._abs_width / 2.),
|
||||
self.position[1] + (self._abs_height / 2.))
|
||||
|
||||
@property
|
||||
def orientation(self):
|
||||
return 'vertical' if self.height > self.width else 'horizontal'
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return (self.position[0] - (self.width / 2.),
|
||||
self.position[1] - (self.height / 2.))
|
||||
|
||||
@property
|
||||
def upper_right(self):
|
||||
return (self.position[0] + (self.width / 2.),
|
||||
self.position[1] + (self.height / 2.))
|
||||
|
||||
@property
|
||||
def bounding_box(self):
|
||||
min_x = self.lower_left[0]
|
||||
|
|
@ -380,7 +385,7 @@ class Obround(Primitive):
|
|||
else:
|
||||
circle1 = Circle((self.position[0] - (self.height - self.width) / 2.,
|
||||
self.position[1]), self.height)
|
||||
circle2 = Circle((self.position[0] - (self.height - self.width) / 2.,
|
||||
circle2 = Circle((self.position[0] + (self.height - self.width) / 2.,
|
||||
self.position[1]), self.height)
|
||||
rect = Rectangle(self.position, (self.width - self.height),
|
||||
self.height)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class GerberContext(object):
|
|||
Attributes
|
||||
----------
|
||||
units : string
|
||||
Measurement units
|
||||
Measurement units. 'inch' or 'metric'
|
||||
|
||||
color : tuple (<float>, <float>, <float>)
|
||||
Color used for rendering as a tuple of normalized (red, green, blue) values.
|
||||
|
|
@ -57,74 +57,70 @@ class GerberContext(object):
|
|||
Rendering opacity. Between 0.0 (transparent) and 1.0 (opaque.)
|
||||
"""
|
||||
def __init__(self, units='inch'):
|
||||
self.units = units
|
||||
self.color = (0.7215, 0.451, 0.200)
|
||||
self.drill_color = (0.25, 0.25, 0.25)
|
||||
self.background_color = (0.0, 0.0, 0.0)
|
||||
self.alpha = 1.0
|
||||
self._units = units
|
||||
self._color = (0.7215, 0.451, 0.200)
|
||||
self._drill_color = (0.25, 0.25, 0.25)
|
||||
self._background_color = (0.0, 0.0, 0.0)
|
||||
self._alpha = 1.0
|
||||
|
||||
def set_units(self, units):
|
||||
""" Set context measurement units
|
||||
@property
|
||||
def units(self):
|
||||
return self._units
|
||||
|
||||
Parameters
|
||||
----------
|
||||
unit : string
|
||||
Measurement units. may be 'inch' or 'metric'
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `unit` is not 'inch' or 'metric'
|
||||
"""
|
||||
@units.setter
|
||||
def units(self, units):
|
||||
if units not in ('inch', 'metric'):
|
||||
raise ValueError('Units may be "inch" or "metric"')
|
||||
self.units = units
|
||||
self._units = units
|
||||
|
||||
def set_color(self, color):
|
||||
""" Set rendering color.
|
||||
@property
|
||||
def color(self):
|
||||
return self._color
|
||||
|
||||
Parameters
|
||||
----------
|
||||
color : tuple (<float>, <float>, <float>)
|
||||
Color as a tuple of (red, green, blue) values. Each channel is
|
||||
represented as a float value in (0, 1)
|
||||
"""
|
||||
self.color = color
|
||||
@color.setter
|
||||
def color(self, color):
|
||||
if len(color) != 3:
|
||||
raise TypeError('Color must be a tuple of R, G, and B values')
|
||||
for c in color:
|
||||
if c < 0 or c > 1:
|
||||
raise ValueError('Channel values must be between 0.0 and 1.0')
|
||||
self._color = color
|
||||
|
||||
def set_drill_color(self, color):
|
||||
""" Set color used for rendering drill hits.
|
||||
@property
|
||||
def drill_color(self):
|
||||
return self._drill_color
|
||||
|
||||
Parameters
|
||||
----------
|
||||
color : tuple (<float>, <float>, <float>)
|
||||
Color as a tuple of (red, green, blue) values. Each channel is
|
||||
represented as a float value in (0, 1)
|
||||
"""
|
||||
self.drill_color = color
|
||||
@drill_color.setter
|
||||
def drill_color(self, color):
|
||||
if len(color) != 3:
|
||||
raise TypeError('Drill color must be a tuple of R, G, and B values')
|
||||
for c in color:
|
||||
if c < 0 or c > 1:
|
||||
raise ValueError('Channel values must be between 0.0 and 1.0')
|
||||
self._drill_color = color
|
||||
|
||||
def set_background_color(self, color):
|
||||
""" Set rendering background color
|
||||
@property
|
||||
def background_color(self):
|
||||
return self._background_color
|
||||
|
||||
Parameters
|
||||
----------
|
||||
color : tuple (<float>, <float>, <float>)
|
||||
Color as a tuple of (red, green, blue) values. Each channel is
|
||||
represented as a float value in (0, 1)
|
||||
"""
|
||||
self.background_color = color
|
||||
@background_color.setter
|
||||
def background_color(self, color):
|
||||
if len(color) != 3:
|
||||
raise TypeError('Background color must be a tuple of R, G, and B values')
|
||||
for c in color:
|
||||
if c < 0 or c > 1:
|
||||
raise ValueError('Channel values must be between 0.0 and 1.0')
|
||||
self._background_color = color
|
||||
|
||||
def set_alpha(self, alpha):
|
||||
""" Set layer rendering opacity
|
||||
@property
|
||||
def alpha(self):
|
||||
return self._alpha
|
||||
|
||||
.. note::
|
||||
Not all backends/rendering devices support this parameter.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
alpha : float
|
||||
Rendering opacity. must be between 0.0 (transparent) and 1.0 (opaque)
|
||||
"""
|
||||
self.alpha = alpha
|
||||
@alpha.setter
|
||||
def alpha(self, alpha):
|
||||
if alpha < 0 or alpha > 1:
|
||||
raise ValueError('Alpha must be between 0.0 and 1.0')
|
||||
self._alpha = alpha
|
||||
|
||||
def render(self, primitive):
|
||||
color = (self.color if primitive.level_polarity == 'dark'
|
||||
|
|
|
|||
|
|
@ -65,8 +65,14 @@ def test_camfile_settings():
|
|||
cf = CamFile()
|
||||
assert_equal(cf.settings, FileSettings())
|
||||
|
||||
def test_zeros():
|
||||
#def test_bounds_override():
|
||||
# cf = CamFile()
|
||||
# assert_raises(NotImplementedError, cf.bounds)
|
||||
|
||||
|
||||
def test_zeros():
|
||||
""" Test zero/zero_suppression interaction
|
||||
"""
|
||||
fs = FileSettings()
|
||||
assert_equal(fs.zero_suppression, 'trailing')
|
||||
assert_equal(fs.zeros, 'leading')
|
||||
|
|
@ -89,3 +95,25 @@ def test_zeros():
|
|||
assert_equal(fs.zeros, 'leading')
|
||||
assert_equal(fs.zero_suppression, 'trailing')
|
||||
|
||||
|
||||
def test_filesettings_validation():
|
||||
""" Test FileSettings constructor argument validation
|
||||
"""
|
||||
assert_raises(ValueError, FileSettings, 'absolute-ish', 'inch', None, (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'degrees kelvin', None, (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', 'leading', (2, 5), 'leading')
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', 'following', (2, 5), None)
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', None, (2, 5), 'following')
|
||||
assert_raises(ValueError, FileSettings, 'absolute', 'inch', None, (2, 5, 6), None)
|
||||
|
||||
def test_key_validation():
|
||||
fs = FileSettings()
|
||||
assert_raises(KeyError, fs.__getitem__, 'octopus')
|
||||
assert_raises(KeyError, fs.__setitem__, 'octopus', 'do not care')
|
||||
assert_raises(ValueError, fs.__setitem__, 'notation', 'absolute-ish')
|
||||
assert_raises(ValueError, fs.__setitem__, 'units', 'degrees kelvin')
|
||||
assert_raises(ValueError, fs.__setitem__, 'zero_suppression', 'following')
|
||||
assert_raises(ValueError, fs.__setitem__, 'zeros', 'following')
|
||||
assert_raises(ValueError, fs.__setitem__, 'format', (2, 5, 6))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,5 +20,12 @@ def test_file_type_detection():
|
|||
"""
|
||||
ncdrill = read(NCDRILL_FILE)
|
||||
top_copper = read(TOP_COPPER_FILE)
|
||||
assert(isinstance(ncdrill, ExcellonFile))
|
||||
assert(isinstance(top_copper, GerberFile))
|
||||
assert_true(isinstance(ncdrill, ExcellonFile))
|
||||
assert_true(isinstance(top_copper, GerberFile))
|
||||
|
||||
def test_file_type_validation():
|
||||
""" Test file format validation
|
||||
"""
|
||||
assert_raises(TypeError, read, 'LICENSE')
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ def test_chamfer_rectangle_ctor():
|
|||
assert_equal(r.chamfer, chamfer)
|
||||
assert_array_almost_equal(r.corners, corners)
|
||||
|
||||
|
||||
def test_chamfer_rectangle_bounds():
|
||||
""" Test chamfer rectangle bounding box calculation
|
||||
"""
|
||||
|
|
@ -191,7 +192,8 @@ def test_round_rectangle_ctor():
|
|||
assert_equal(r.height, height)
|
||||
assert_equal(r.radius, radius)
|
||||
assert_array_almost_equal(r.corners, corners)
|
||||
|
||||
|
||||
|
||||
def test_round_rectangle_bounds():
|
||||
""" Test round rectangle bounding box calculation
|
||||
"""
|
||||
|
|
@ -203,7 +205,76 @@ def test_round_rectangle_bounds():
|
|||
xbounds, ybounds = r.bounding_box
|
||||
assert_array_almost_equal(xbounds, (-math.sqrt(2), math.sqrt(2)))
|
||||
assert_array_almost_equal(ybounds, (-math.sqrt(2), math.sqrt(2)))
|
||||
|
||||
|
||||
def test_obround_ctor():
|
||||
""" Test obround creation
|
||||
"""
|
||||
test_cases = (((0,0), 1, 1),
|
||||
((0, 0), 1, 2),
|
||||
((1,1), 1, 2))
|
||||
for pos, width, height in test_cases:
|
||||
o = Obround(pos, width, height)
|
||||
assert_equal(o.position, pos)
|
||||
assert_equal(o.width, width)
|
||||
assert_equal(o.height, height)
|
||||
|
||||
|
||||
def test_obround_bounds():
|
||||
""" Test obround bounding box calculation
|
||||
"""
|
||||
o = Obround((2,2),2,4)
|
||||
xbounds, ybounds = o.bounding_box
|
||||
assert_array_almost_equal(xbounds, (1, 3))
|
||||
assert_array_almost_equal(ybounds, (0, 4))
|
||||
o = Obround((2,2),4,2)
|
||||
xbounds, ybounds = o.bounding_box
|
||||
assert_array_almost_equal(xbounds, (0, 4))
|
||||
assert_array_almost_equal(ybounds, (1, 3))
|
||||
|
||||
|
||||
def test_obround_orientation():
|
||||
o = Obround((0, 0), 2, 1)
|
||||
assert_equal(o.orientation, 'horizontal')
|
||||
o = Obround((0, 0), 1, 2)
|
||||
assert_equal(o.orientation, 'vertical')
|
||||
|
||||
|
||||
def test_obround_subshapes():
|
||||
o = Obround((0,0), 1, 4)
|
||||
ss = o.subshapes
|
||||
assert_array_almost_equal(ss['rectangle'].position, (0, 0))
|
||||
assert_array_almost_equal(ss['circle1'].position, (0, 1.5))
|
||||
assert_array_almost_equal(ss['circle2'].position, (0, -1.5))
|
||||
o = Obround((0,0), 4, 1)
|
||||
ss = o.subshapes
|
||||
assert_array_almost_equal(ss['rectangle'].position, (0, 0))
|
||||
assert_array_almost_equal(ss['circle1'].position, (1.5, 0))
|
||||
assert_array_almost_equal(ss['circle2'].position, (-1.5, 0))
|
||||
|
||||
|
||||
|
||||
|
||||
def test_polygon_ctor():
|
||||
""" Test polygon creation
|
||||
"""
|
||||
test_cases = (((0,0), 3, 5),
|
||||
((0, 0), 5, 6),
|
||||
((1,1), 7, 7))
|
||||
for pos, sides, radius in test_cases:
|
||||
p = Polygon(pos, sides, radius)
|
||||
assert_equal(p.position, pos)
|
||||
assert_equal(p.sides, sides)
|
||||
assert_equal(p.radius, radius)
|
||||
|
||||
def test_polygon_bounds():
|
||||
""" Test polygon bounding box calculation
|
||||
"""
|
||||
p = Polygon((2,2), 3, 2)
|
||||
xbounds, ybounds = p.bounding_box
|
||||
assert_array_almost_equal(xbounds, (0, 4))
|
||||
assert_array_almost_equal(ybounds, (0, 4))
|
||||
p = Polygon((2,2),3, 4)
|
||||
xbounds, ybounds = p.bounding_box
|
||||
assert_array_almost_equal(xbounds, (-2, 6))
|
||||
assert_array_almost_equal(ybounds, (-2, 6))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
|
||||
|
||||
from .tests import assert_equal
|
||||
from ..utils import decimal_string, parse_gerber_value, write_gerber_value
|
||||
from .tests import assert_equal, assert_raises
|
||||
from ..utils import decimal_string, parse_gerber_value, write_gerber_value, detect_file_format
|
||||
|
||||
|
||||
def test_zero_suppression():
|
||||
|
|
@ -22,8 +22,8 @@ def test_zero_suppression():
|
|||
('-100000', -1.0), ('-1000000', -10.0),
|
||||
('0', 0.0)]
|
||||
for string, value in test_cases:
|
||||
assert(value == parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert(string == write_gerber_value(value, fmt, zero_suppression))
|
||||
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
|
||||
|
||||
# Test trailing zero suppression
|
||||
zero_suppression = 'trailing'
|
||||
|
|
@ -34,8 +34,8 @@ def test_zero_suppression():
|
|||
('-000001', -0.0001), ('-0000001', -0.00001),
|
||||
('0', 0.0)]
|
||||
for string, value in test_cases:
|
||||
assert(value == parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert(string == write_gerber_value(value, fmt, zero_suppression))
|
||||
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
|
||||
|
||||
|
||||
def test_format():
|
||||
|
|
@ -51,8 +51,8 @@ def test_format():
|
|||
((2, 2), '-1', -0.01), ((2, 1), '-1', -0.1),
|
||||
((2, 6), '0', 0) ]
|
||||
for fmt, string, value in test_cases:
|
||||
assert(value == parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert(string == write_gerber_value(value, fmt, zero_suppression))
|
||||
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
|
||||
|
||||
zero_suppression = 'trailing'
|
||||
test_cases = [((6, 5), '1', 100000.0), ((5, 5), '1', 10000.0),
|
||||
|
|
@ -63,8 +63,8 @@ def test_format():
|
|||
((2, 5), '-1', -10.0), ((1, 5), '-1', -1.0),
|
||||
((2, 5), '0', 0)]
|
||||
for fmt, string, value in test_cases:
|
||||
assert(value == parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert(string == write_gerber_value(value, fmt, zero_suppression))
|
||||
assert_equal(value, parse_gerber_value(string, fmt, zero_suppression))
|
||||
assert_equal(string, write_gerber_value(value, fmt, zero_suppression))
|
||||
|
||||
|
||||
def test_decimal_truncation():
|
||||
|
|
@ -74,7 +74,7 @@ def test_decimal_truncation():
|
|||
for x in range(10):
|
||||
result = decimal_string(value, precision=x)
|
||||
calculated = '1.' + ''.join(str(y) for y in range(1,x+1))
|
||||
assert(result == calculated)
|
||||
assert_equal(result, calculated)
|
||||
|
||||
|
||||
def test_decimal_padding():
|
||||
|
|
@ -85,6 +85,25 @@ def test_decimal_padding():
|
|||
assert_equal(decimal_string(value, precision=4, padding=True), '1.1230')
|
||||
assert_equal(decimal_string(value, precision=5, padding=True), '1.12300')
|
||||
assert_equal(decimal_string(value, precision=6, padding=True), '1.123000')
|
||||
|
||||
assert_equal(decimal_string(0, precision=6, padding=True), '0.000000')
|
||||
|
||||
|
||||
def test_parse_format_validation():
|
||||
""" Test parse_gerber_value() format validation
|
||||
"""
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (7, 5))
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (5, 8))
|
||||
assert_raises(ValueError, parse_gerber_value, '00001111', (13,1))
|
||||
|
||||
def test_write_format_validation():
|
||||
""" Test write_gerber_value() format validation
|
||||
"""
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (7, 5))
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (5, 8))
|
||||
assert_raises(ValueError, write_gerber_value, 69.0, (13,1))
|
||||
|
||||
|
||||
def test_detect_format_with_short_file():
|
||||
""" Verify file format detection works with short files
|
||||
"""
|
||||
assert_equal('unknown', detect_file_format('gerber/tests/__init__.py'))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue