Fix rendering for line with rectangular aperture per #12. Still need to do the same for arcs.
This commit is contained in:
parent
8f69c1dfa2
commit
5e23d07bcb
5 changed files with 108 additions and 27 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue