diff options
Diffstat (limited to 'gerber')
-rw-r--r-- | gerber/cam.py | 5 | ||||
-rw-r--r-- | gerber/primitives.py | 72 | ||||
-rw-r--r-- | gerber/render/cairo_backend.py | 13 | ||||
-rw-r--r-- | gerber/render/svgwrite_backend.py | 10 | ||||
-rw-r--r-- | gerber/tests/test_primitives.py | 35 |
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(): |