summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gerber/cam.py5
-rw-r--r--gerber/primitives.py72
-rw-r--r--gerber/render/cairo_backend.py13
-rw-r--r--gerber/render/svgwrite_backend.py10
-rw-r--r--gerber/tests/test_primitives.py35
5 files changed, 108 insertions, 27 deletions
diff --git a/gerber/cam.py b/gerber/cam.py
index 9c731aa..f49f5dd 100644
--- a/gerber/cam.py
+++ b/gerber/cam.py
@@ -21,7 +21,7 @@ CAM File
This module provides common base classes for Excellon/Gerber CNC files
"""
-
+from operator import mul
class FileSettings(object):
""" CAM File Settings
@@ -241,7 +241,8 @@ class CamFile(object):
filename : string <optional>
If provided, save the rendered image to `filename`
"""
- ctx.set_bounds(self.bounds)
+ bounds = [tuple([x * 1.2, y*1.2]) for x, y in self.bounds]
+ ctx.set_bounds(bounds)
for p in self.primitives:
ctx.render(p)
if filename is not None:
diff --git a/gerber/primitives.py b/gerber/primitives.py
index a239cab..1663a53 100644
--- a/gerber/primitives.py
+++ b/gerber/primitives.py
@@ -64,14 +64,70 @@ class Line(Primitive):
angle = math.atan2(delta_y, delta_x)
return angle
- #@property
- #def bounding_box(self):
- # width_2 = self.width / 2.
- # min_x = min(self.start[0], self.end[0]) - width_2
- # max_x = max(self.start[0], self.end[0]) + width_2
- # min_y = min(self.start[1], self.end[1]) - width_2
- # max_y = max(self.start[1], self.end[1]) + width_2
- # return ((min_x, max_x), (min_y, max_y))
+ @property
+ def bounding_box(self):
+ if isinstance(self.aperture, Circle):
+ width_2 = self.aperture.radius
+ height_2 = width_2
+ else:
+ width_2 = self.aperture.width / 2.
+ height_2 = self.aperture.height / 2.
+ min_x = min(self.start[0], self.end[0]) - width_2
+ max_x = max(self.start[0], self.end[0]) + width_2
+ min_y = min(self.start[1], self.end[1]) - height_2
+ max_y = max(self.start[1], self.end[1]) + height_2
+ return ((min_x, max_x), (min_y, max_y))
+
+ @property
+ def vertices(self):
+ if not isinstance(self.aperture, Rectangle):
+ return None
+ else:
+ start = self.start
+ end = self.end
+ width = self.aperture.width
+ height = self.aperture.height
+
+ # Find all the corners of the start and end position
+ start_ll = (start[0] - (width / 2.),
+ start[1] - (height / 2.))
+ start_lr = (start[0] - (width / 2.),
+ start[1] + (height / 2.))
+ start_ul = (start[0] + (width / 2.),
+ start[1] - (height / 2.))
+ start_ur = (start[0] + (width / 2.),
+ start[1] + (height / 2.))
+ end_ll = (end[0] - (width / 2.),
+ end[1] - (height / 2.))
+ end_lr = (end[0] - (width / 2.),
+ end[1] + (height / 2.))
+ end_ul = (end[0] + (width / 2.),
+ end[1] - (height / 2.))
+ end_ur = (end[0] + (width / 2.),
+ end[1] + (height / 2.))
+
+ if end[0] == start[0] and end[1] == start[1]:
+ return (start_ll, start_lr, start_ur, start_ul)
+ elif end[0] == start[0] and end[1] > start[1]:
+ return (start_ll, start_lr, end_ur, end_ul)
+ elif end[0] > start[0] and end[1] > start[1]:
+ return (start_ll, start_lr, end_lr, end_ur, end_ul, start_ul)
+ elif end[0] > start[0] and end[1] == start[1]:
+ return (start_ll, end_lr, end_ur, start_ul)
+ elif end[0] > start[0] and end[1] < start[1]:
+ return (start_ll, end_ll, end_lr, end_ur, start_ur, start_ul)
+ elif end[0] == start[0] and end[1] < start[1]:
+ return (end_ll, end_lr, start_ur, start_ul)
+ elif end[0] < start[0] and end[1] < start[1]:
+ return (end_ll, end_lr, start_lr, start_ur, start_ul, end_ul)
+ elif end[0] < start[0] and end[1] == start[1]:
+ return (end_ll, start_lr, start_ur, end_ul)
+ elif end[0] < start[0] and end[1] > start[1]:
+ return (start_ll, start_lr, start_ur, end_ur, end_ul, end_ll)
+ else:
+ return None
+
+
class Arc(Primitive):
diff --git a/gerber/render/cairo_backend.py b/gerber/render/cairo_backend.py
index c1df87a..999269b 100644
--- a/gerber/render/cairo_backend.py
+++ b/gerber/render/cairo_backend.py
@@ -26,7 +26,7 @@ SCALE = 400.
class GerberCairoContext(GerberContext):
- def __init__(self, surface=None, size=(1000, 1000)):
+ def __init__(self, surface=None, size=(10000, 10000)):
GerberContext.__init__(self)
if surface is None:
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
@@ -58,9 +58,14 @@ class GerberCairoContext(GerberContext):
self.ctx.move_to(*start)
self.ctx.line_to(*end)
self.ctx.stroke()
- elif isinstance(line.aperture, rectangle):
- # TODO: Render rectangle strokes as a polygon...
- pass
+ elif isinstance(line.aperture, Rectangle):
+ points = [tuple(map(mul, x, self.scale)) for x in line.vertices]
+ self.ctx.set_source_rgba(*color, alpha=self.alpha)
+ self.ctx.set_line_width(0)
+ self.ctx.move_to(*points[0])
+ for point in points[1:]:
+ self.ctx.line_to(*point)
+ self.ctx.fill()
def _render_arc(self, arc, color):
center = map(mul, arc.center, self.scale)
diff --git a/gerber/render/svgwrite_backend.py b/gerber/render/svgwrite_backend.py
index 279d90f..9e6a5e4 100644
--- a/gerber/render/svgwrite_backend.py
+++ b/gerber/render/svgwrite_backend.py
@@ -68,8 +68,14 @@ class GerberSvgContext(GerberContext):
aline.stroke(opacity=self.alpha)
self.dwg.add(aline)
elif isinstance(line.aperture, Rectangle):
- # TODO: Render rectangle strokes as a polygon...
- pass
+ points = [tuple(map(mul, point, self.scale)) for point in line.vertices]
+ path = self.dwg.path(d='M %f, %f' % points[0],
+ fill=svg_color(color),
+ stroke='none')
+ path.fill(opacity=self.alpha)
+ for point in points[1:]:
+ path.push('L %f, %f' % point)
+ self.dwg.add(path)
def _render_arc(self, arc, color):
start = tuple(map(mul, arc.start, self.scale))
diff --git a/gerber/tests/test_primitives.py b/gerber/tests/test_primitives.py
index 912cebb..877823d 100644
--- a/gerber/tests/test_primitives.py
+++ b/gerber/tests/test_primitives.py
@@ -27,17 +27,30 @@ def test_line_angle():
line_angle = (l.angle + 2 * math.pi) % (2 * math.pi)
assert_almost_equal(line_angle, expected)
-# Need to update bounds calculation using aperture
-#def test_line_bounds():
-# """ Test Line primitive bounding box calculation
-# """
-# cases = [((0, 0), (1, 1), ((0, 1), (0, 1))),
-# ((-1, -1), (1, 1), ((-1, 1), (-1, 1))),
-# ((1, 1), (-1, -1), ((-1, 1), (-1, 1))),
-# ((-1, 1), (1, -1), ((-1, 1), (-1, 1))),]
-# for start, end, expected in cases:
-# l = Line(start, end, 0)
-# assert_equal(l.bounding_box, expected)
+def test_line_bounds():
+ """ Test Line primitive bounding box calculation
+ """
+ cases = [((0, 0), (1, 1), ((-1, 2), (-1, 2))),
+ ((-1, -1), (1, 1), ((-2, 2), (-2, 2))),
+ ((1, 1), (-1, -1), ((-2, 2), (-2, 2))),
+ ((-1, 1), (1, -1), ((-2, 2), (-2, 2))),]
+
+ c = Circle((0, 0), 2)
+ r = Rectangle((0, 0), 2, 2)
+ for shape in (c, r):
+ for start, end, expected in cases:
+ l = Line(start, end, shape)
+ assert_equal(l.bounding_box, expected)
+ # Test a non-square rectangle
+ r = Rectangle((0, 0), 3, 2)
+ cases = [((0, 0), (1, 1), ((-1.5, 2.5), (-1, 2))),
+ ((-1, -1), (1, 1), ((-2.5, 2.5), (-2, 2))),
+ ((1, 1), (-1, -1), ((-2.5, 2.5), (-2, 2))),
+ ((-1, 1), (1, -1), ((-2.5, 2.5), (-2, 2))),]
+ for start, end, expected in cases:
+ l = Line(start, end, r)
+ assert_equal(l.bounding_box, expected)
+
def test_arc_radius():