Fix rendering for line with rectangular aperture per #12. Still need to do the same for arcs.

This commit is contained in:
Hamilton Kibbe 2015-02-13 09:37:27 -05:00
parent 8f69c1dfa2
commit 5e23d07bcb
5 changed files with 108 additions and 27 deletions

View file

@ -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:

View file

@ -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):

View file

@ -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)

View file

@ -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))

View file

@ -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():