This commit is contained in:
Hamilton Kibbe 2015-02-02 11:42:47 -05:00
parent d98d23f8b5
commit 1cc20b351c
7 changed files with 238 additions and 44 deletions

View file

@ -145,12 +145,14 @@ class MOParamStmt(ParamStmt):
@classmethod
def from_dict(cls, stmt_dict):
param = stmt_dict.get('param')
if stmt_dict.get('mo').lower() == 'in':
mo = 'inch'
elif stmt_dict.get('mo').lower() == 'mm':
mo = 'metric'
else:
if stmt_dict.get('mo') is None:
mo = None
elif stmt_dict.get('mo').lower() not in ('in', 'mm'):
raise ValueError('Mode may be mm or in')
elif stmt_dict.get('mo').lower() == 'in':
mo = 'inch'
else:
mo = 'metric'
return cls(param, mo)
def __init__(self, param, mo):
@ -347,7 +349,7 @@ class AMOutlinePrimitive(AMPrimitive):
return "{code},{exposure},{n_points},{start_point},{points},{rotation}".format(**data)
class AMUnsupportPrimitive:
class AMUnsupportPrimitive(object):
@classmethod
def from_gerber(cls, primitive):
return cls(primitive)
@ -652,9 +654,9 @@ class OFParamStmt(ParamStmt):
def __str__(self):
offset_str = ''
if self.a is not None:
offset_str += ('X: %f' % self.a)
offset_str += ('X: %f ' % self.a)
if self.b is not None:
offset_str += ('Y: %f' % self.b)
offset_str += ('Y: %f ' % self.b)
return ('<Offset: %s>' % offset_str)

View file

@ -16,6 +16,7 @@
# limitations under the License.
import math
from operator import sub
from .utils import validate_coordinates
class Primitive(object):
@ -45,7 +46,7 @@ class Primitive(object):
Return ((min x, max x), (min y, max y))
"""
pass
raise NotImplementedError('Bounding box calculation must be implemented in subclass')
class Line(Primitive):
@ -155,6 +156,7 @@ class Circle(Primitive):
"""
def __init__(self, position, diameter, **kwargs):
super(Circle, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.diameter = diameter
@ -180,6 +182,7 @@ class Ellipse(Primitive):
"""
def __init__(self, position, width, height, **kwargs):
super(Ellipse, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.width = width
self.height = height
@ -205,6 +208,7 @@ class Rectangle(Primitive):
"""
def __init__(self, position, width, height, **kwargs):
super(Rectangle, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.width = width
self.height = height
@ -239,6 +243,7 @@ class Diamond(Primitive):
"""
def __init__(self, position, width, height, **kwargs):
super(Diamond, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.width = width
self.height = height
@ -272,6 +277,7 @@ class ChamferRectangle(Primitive):
"""
def __init__(self, position, width, height, chamfer, corners, **kwargs):
super(ChamferRectangle, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.width = width
self.height = height
@ -307,6 +313,7 @@ class RoundRectangle(Primitive):
"""
def __init__(self, position, width, height, radius, corners, **kwargs):
super(RoundRectangle, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.width = width
self.height = height
@ -342,6 +349,7 @@ class Obround(Primitive):
"""
def __init__(self, position, width, height, **kwargs):
super(Obround, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.width = width
self.height = height
@ -397,6 +405,7 @@ class Polygon(Primitive):
"""
def __init__(self, position, sides, radius, **kwargs):
super(Polygon, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.sides = sides
self.radius = radius
@ -432,6 +441,7 @@ class RoundButterfly(Primitive):
"""
def __init__(self, position, diameter, **kwargs):
super(RoundButterfly, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.diameter = diameter
@ -452,6 +462,7 @@ class SquareButterfly(Primitive):
"""
def __init__(self, position, side, **kwargs):
super(SquareButterfly, self).__init__(**kwargs)
validate_coordinates(position)
self.position = position
self.side = side
@ -470,8 +481,14 @@ class Donut(Primitive):
"""
def __init__(self, position, shape, inner_diameter, outer_diameter, **kwargs):
super(Donut, self).__init__(**kwargs)
if len(position) != 2:
raise TypeError('Position must be a tuple (n=2) of coordinates')
self.position = position
if shape not in ('round', 'square', 'hexagon', 'octagon'):
raise ValueError('Valid shapes are round, square, hexagon or octagon')
self.shape = shape
if inner_diameter >= outer_diameter:
raise ValueError('Outer diameter must be larger than inner diameter.')
self.inner_diameter = inner_diameter
self.outer_diameter = outer_diameter
if self.shape in ('round', 'square', 'octagon'):
@ -507,6 +524,8 @@ class Drill(Primitive):
"""
def __init__(self, position, diameter):
super(Drill, self).__init__('dark')
if len(position) != 2:
raise TypeError('Position must be a tuple (n=2) of coordinates')
self.position = position
self.diameter = diameter

View file

@ -77,7 +77,6 @@ def test_zeros():
assert_equal(fs.zero_suppression, 'trailing')
assert_equal(fs.zeros, 'leading')
fs['zero_suppression'] = 'leading'
assert_equal(fs.zero_suppression, 'leading')
assert_equal(fs.zeros, 'trailing')
@ -90,11 +89,26 @@ def test_zeros():
assert_equal(fs.zeros, 'trailing')
assert_equal(fs.zero_suppression, 'leading')
fs.zeros= 'leading'
assert_equal(fs.zeros, 'leading')
assert_equal(fs.zero_suppression, 'trailing')
fs = FileSettings(zeros='leading')
assert_equal(fs.zeros, 'leading')
assert_equal(fs.zero_suppression, 'trailing')
fs = FileSettings(zero_suppression='leading')
assert_equal(fs.zeros, 'trailing')
assert_equal(fs.zero_suppression, 'leading')
fs = FileSettings(zeros='leading', zero_suppression='trailing')
assert_equal(fs.zeros, 'leading')
assert_equal(fs.zero_suppression, 'trailing')
fs = FileSettings(zeros='trailing', zero_suppression='leading')
assert_equal(fs.zeros, 'trailing')
assert_equal(fs.zero_suppression, 'leading')
def test_filesettings_validation():
""" Test FileSettings constructor argument validation

View file

@ -82,6 +82,12 @@ def test_MOParamStmt_factory():
assert_equal(mo.param, 'MO')
assert_equal(mo.mode, 'metric')
stmt = {'param': 'MO'}
mo = MOParamStmt.from_dict(stmt)
assert_equal(mo.mode, None)
stmt = {'param': 'MO', 'mo': 'degrees kelvin'}
assert_raises(ValueError, MOParamStmt.from_dict, stmt)
def test_MOParamStmt():
""" Test MOParamStmt initialization
@ -182,6 +188,13 @@ def test_OFParamStmt_dump():
assert_equal(of.to_gerber(), '%OFA0.12345B0.12345*%')
def test_OFParamStmt_string():
""" Test OFParamStmt __str__
"""
stmt = {'param': 'OF', 'a': '0.123456', 'b': '0.123456'}
of = OFParamStmt.from_dict(stmt)
assert_equal(str(of), '<Offset: X: 0.123456 Y: 0.123456 >')
def test_LPParamStmt_factory():
""" Test LPParamStmt factory
"""
@ -377,38 +390,47 @@ def test_ADParamStmt_factory():
assert_equal(ad.d, 1)
assert_equal(ad.shape, 'R')
stmt = {'param': 'AD', 'd': 2, 'shape': 'O'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(ad.d, 2)
assert_equal(ad.shape, 'O')
def test_MIParamStmt_factory():
stmt = {'param': 'MI', 'a': 1, 'b': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(mi.a, 1)
assert_equal(mi.b, 1)
def test_MIParamStmt_dump():
stmt = {'param': 'MI', 'a': 1, 'b': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(mi.to_gerber(), '%MIA1B1*%')
stmt = {'param': 'MI', 'a': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(mi.to_gerber(), '%MIA1B0*%')
stmt = {'param': 'MI', 'b': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(mi.to_gerber(), '%MIA0B1*%')
def test_MIParamStmt_string():
stmt = {'param': 'MI', 'a': 1, 'b': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(str(mi), '<Image Mirror: A=1 B=1>')
stmt = {'param': 'MI', 'b': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(str(mi), '<Image Mirror: A=0 B=1>')
stmt = {'param': 'MI', 'a': 1}
mi = MIParamStmt.from_dict(stmt)
assert_equal(str(mi), '<Image Mirror: A=1 B=0>')
def test_ADParamStmt_dump():
""" Test ADParamStmt.to_gerber()
"""
stmt = {'param': 'AD', 'd': 0, 'shape': 'C'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(ad.to_gerber(),'%ADD0C*%')
stmt = {'param': 'AD', 'd': 1, 'shape': 'R'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(ad.to_gerber(),'%ADD1R*%')
stmt = {'param': 'AD', 'd': 2, 'shape': 'O'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(ad.to_gerber(),'%ADD2O*%')
def test_ADParamStmt_string():
""" Test ADParamStmt.__str__()
"""
stmt = {'param': 'AD', 'd': 0, 'shape': 'C'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(str(ad), '<Aperture Definition: 0: circle>')
stmt = {'param': 'AD', 'd': 1, 'shape': 'R'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(str(ad), '<Aperture Definition: 1: rectangle>')
stmt = {'param': 'AD', 'd': 2, 'shape': 'O'}
ad = ADParamStmt.from_dict(stmt)
assert_equal(str(ad), '<Aperture Definition: 2: obround>')
def test_coordstmt_ctor():
cs = CoordStmt('G04', 0.0, 0.1, 0.2, 0.3, 'D01', FileSettings())
assert_equal(cs.function, 'G04')
assert_equal(cs.x, 0.0)
assert_equal(cs.y, 0.1)
assert_equal(cs.i, 0.2)
assert_equal(cs.j, 0.3)
assert_equal(cs.op, 'D01')

View file

@ -6,6 +6,11 @@ from ..primitives import *
from tests import *
def test_primitive_implementation_warning():
p = Primitive()
assert_raises(NotImplementedError, p.bounding_box)
def test_line_angle():
""" Test Line primitive angle calculation
"""
@ -277,4 +282,118 @@ def test_polygon_bounds():
assert_array_almost_equal(ybounds, (-2, 6))
def test_region_ctor():
""" Test Region creation
"""
points = ((0, 0), (1,0), (1,1), (0,1))
r = Region(points)
for i, point in enumerate(points):
assert_array_almost_equal(r.points[i], point)
def test_region_bounds():
""" Test region bounding box calculation
"""
points = ((0, 0), (1,0), (1,1), (0,1))
r = Region(points)
xbounds, ybounds = r.bounding_box
assert_array_almost_equal(xbounds, (0, 1))
assert_array_almost_equal(ybounds, (0, 1))
def test_round_butterfly_ctor():
""" Test round butterfly creation
"""
test_cases = (((0,0), 3), ((0, 0), 5), ((1,1), 7))
for pos, diameter in test_cases:
b = RoundButterfly(pos, diameter)
assert_equal(b.position, pos)
assert_equal(b.diameter, diameter)
assert_equal(b.radius, diameter/2.)
def test_round_butterfly_ctor_validation():
""" Test RoundButterfly argument validation
"""
assert_raises(TypeError, RoundButterfly, 3, 5)
assert_raises(TypeError, RoundButterfly, (3,4,5), 5)
def test_round_butterfly_bounds():
""" Test RoundButterfly bounding box calculation
"""
b = RoundButterfly((0, 0), 2)
xbounds, ybounds = b.bounding_box
assert_array_almost_equal(xbounds, (-1, 1))
assert_array_almost_equal(ybounds, (-1, 1))
def test_square_butterfly_ctor():
""" Test SquareButterfly creation
"""
test_cases = (((0,0), 3), ((0, 0), 5), ((1,1), 7))
for pos, side in test_cases:
b = SquareButterfly(pos, side)
assert_equal(b.position, pos)
assert_equal(b.side, side)
def test_square_butterfly_ctor_validation():
""" Test SquareButterfly argument validation
"""
assert_raises(TypeError, SquareButterfly, 3, 5)
assert_raises(TypeError, SquareButterfly, (3,4,5), 5)
def test_square_butterfly_bounds():
""" Test SquareButterfly bounding box calculation
"""
b = SquareButterfly((0, 0), 2)
xbounds, ybounds = b.bounding_box
assert_array_almost_equal(xbounds, (-1, 1))
assert_array_almost_equal(ybounds, (-1, 1))
def test_donut_ctor():
""" Test Donut primitive creation
"""
test_cases = (((0,0), 'round', 3, 5), ((0, 0), 'square', 5, 7),
((1,1), 'hexagon', 7, 9), ((2, 2), 'octagon', 9, 11))
for pos, shape, in_d, out_d in test_cases:
d = Donut(pos, shape, in_d, out_d)
assert_equal(d.position, pos)
assert_equal(d.shape, shape)
assert_equal(d.inner_diameter, in_d)
assert_equal(d.outer_diameter, out_d)
def test_donut_ctor_validation():
assert_raises(TypeError, Donut, 3, 'round', 5, 7)
assert_raises(TypeError, Donut, (3, 4, 5), 'round', 5, 7)
assert_raises(ValueError, Donut, (0, 0), 'triangle', 3, 5)
assert_raises(ValueError, Donut, (0, 0), 'round', 5, 3)
def test_donut_bounds():
pass
def test_drill_ctor():
""" Test drill primitive creation
"""
test_cases = (((0, 0), 2), ((1, 1), 3), ((2, 2), 5))
for position, diameter in test_cases:
d = Drill(position, diameter)
assert_equal(d.position, position)
assert_equal(d.diameter, diameter)
assert_equal(d.radius, diameter/2.)
def test_drill_ctor_validation():
""" Test drill argument validation
"""
assert_raises(TypeError, Drill, 3, 5)
assert_raises(TypeError, Drill, (3,4,5), 5)
def test_drill_bounds():
d = Drill((0, 0), 2)
xbounds, ybounds = d.bounding_box
assert_array_almost_equal(xbounds, (-1, 1))
assert_array_almost_equal(ybounds, (-1, 1))
d = Drill((1, 2), 2)
xbounds, ybounds = d.bounding_box
assert_array_almost_equal(xbounds, (0, 2))
assert_array_almost_equal(ybounds, (1, 3))

View file

@ -4,7 +4,7 @@
# Author: Hamilton Kibbe <ham@hamiltonkib.be>
from .tests import assert_equal, assert_raises
from ..utils import decimal_string, parse_gerber_value, write_gerber_value, detect_file_format
from ..utils import *
def test_zero_suppression():
@ -107,3 +107,11 @@ 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'))
def test_validate_coordinates():
assert_raises(TypeError, validate_coordinates, 3)
assert_raises(TypeError, validate_coordinates, 3.1)
assert_raises(TypeError, validate_coordinates, '14')
assert_raises(TypeError, validate_coordinates, (0,))
assert_raises(TypeError, validate_coordinates, (0,1,2))
assert_raises(TypeError, validate_coordinates, (0,'string'))

View file

@ -220,3 +220,13 @@ def detect_file_format(filename):
elif '%FS' in line:
return'rs274x'
return 'unknown'
def validate_coordinates(position):
if position is not None:
if len(position) != 2:
raise TypeError('Position must be a tuple (n=2) of coordinates')
else:
for coord in position:
if not (isinstance(coord, int) or isinstance(coord, float)):
raise TypeError('Coordinates must be integers or floats')