summaryrefslogtreecommitdiff
path: root/gerber
diff options
context:
space:
mode:
Diffstat (limited to 'gerber')
-rw-r--r--gerber/gerber_statements.py16
-rw-r--r--gerber/primitives.py21
-rw-r--r--gerber/tests/test_cam.py18
-rw-r--r--gerber/tests/test_gerber_statements.py92
-rw-r--r--gerber/tests/test_primitives.py119
-rw-r--r--gerber/tests/test_utils.py10
-rw-r--r--gerber/utils.py10
7 files changed, 240 insertions, 46 deletions
diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py
index d84b5e0..0978aca 100644
--- a/gerber/gerber_statements.py
+++ b/gerber/gerber_statements.py
@@ -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':
+ 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'
- elif stmt_dict.get('mo').lower() == 'mm':
- mo = 'metric'
else:
- mo = None
+ 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)
diff --git a/gerber/primitives.py b/gerber/primitives.py
index da05127..2d666b8 100644
--- a/gerber/primitives.py
+++ b/gerber/primitives.py
@@ -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
diff --git a/gerber/tests/test_cam.py b/gerber/tests/test_cam.py
index 8e0270c..185e716 100644
--- a/gerber/tests/test_cam.py
+++ b/gerber/tests/test_cam.py
@@ -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
diff --git a/gerber/tests/test_gerber_statements.py b/gerber/tests/test_gerber_statements.py
index ff967f9..e797d5a 100644
--- a/gerber/tests/test_gerber_statements.py
+++ b/gerber/tests/test_gerber_statements.py
@@ -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_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_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_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')
+
+
+
+ \ No newline at end of file
diff --git a/gerber/tests/test_primitives.py b/gerber/tests/test_primitives.py
index e5ae770..14a3d39 100644
--- a/gerber/tests/test_primitives.py
+++ b/gerber/tests/test_primitives.py
@@ -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))
+
+ \ No newline at end of file
diff --git a/gerber/tests/test_utils.py b/gerber/tests/test_utils.py
index 1077022..1c3f1e5 100644
--- a/gerber/tests/test_utils.py
+++ b/gerber/tests/test_utils.py
@@ -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'))
diff --git a/gerber/utils.py b/gerber/utils.py
index 64cd6ed..86119ba 100644
--- a/gerber/utils.py
+++ b/gerber/utils.py
@@ -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')