summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHamilton Kibbe <hamilton.kibbe@gmail.com>2014-10-08 22:49:49 -0400
committerHamilton Kibbe <hamilton.kibbe@gmail.com>2014-10-08 22:49:49 -0400
commitbcb6cbc50dea975954b8a3864690f68ab5e984b7 (patch)
tree761ded543fd1209183a4d5bdc9aa1b38d33bbe79
parent1653ae5cbe88757e453bccf499dc1b8ccb278e58 (diff)
downloadgerbonara-bcb6cbc50dea975954b8a3864690f68ab5e984b7.tar.gz
gerbonara-bcb6cbc50dea975954b8a3864690f68ab5e984b7.tar.bz2
gerbonara-bcb6cbc50dea975954b8a3864690f68ab5e984b7.zip
start arc
-rw-r--r--gerber/gerber.py7
-rw-r--r--gerber/gerber_statements.py23
-rw-r--r--gerber/render/apertures.py21
-rw-r--r--gerber/render/render.py42
-rw-r--r--gerber/render/svgwrite_backend.py70
5 files changed, 116 insertions, 47 deletions
diff --git a/gerber/gerber.py b/gerber/gerber.py
index 04203fa..07ecd78 100644
--- a/gerber/gerber.py
+++ b/gerber/gerber.py
@@ -206,6 +206,13 @@ class GerberParser(object):
while did_something and len(line) > 0:
did_something = False
+ # region mode
+ #if 'G36' in line or 'G37' in line:
+ # yield RegionModeStmt.from_gerber(line)
+ # did_something = True
+ # line = ''
+ # continue
+
# coord
(coord, r) = self._match_one(self.COORD_STMT, line)
if coord:
diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py
index 2f58a37..90952b2 100644
--- a/gerber/gerber_statements.py
+++ b/gerber/gerber_statements.py
@@ -12,7 +12,7 @@ from .utils import parse_gerber_value, write_gerber_value, decimal_string
__all__ = ['FSParamStmt', 'MOParamStmt', 'IPParamStmt', 'OFParamStmt',
'LPParamStmt', 'ADParamStmt', 'AMParamStmt', 'INParamStmt',
'LNParamStmt', 'CoordStmt', 'ApertureStmt', 'CommentStmt',
- 'EofStmt', 'UnknownStmt']
+ 'EofStmt', 'QuadrantModeStmt', 'RegionModeStmt', 'UnknownStmt']
class Statement(object):
@@ -601,7 +601,7 @@ class QuadrantModeStmt(Statement):
def __init__(self, mode):
super(QuadrantModeStmt, self).__init__('Quadrant Mode')
- mode = mode.lower
+ mode = mode.lower()
if mode not in ['single-quadrant', 'multi-quadrant']:
raise ValueError('Quadrant mode must be "single-quadrant" \
or "multi-quadrant"')
@@ -610,6 +610,25 @@ class QuadrantModeStmt(Statement):
def to_gerber(self):
return 'G74*' if self.mode == 'single-quadrant' else 'G75*'
+class RegionModeStmt(Statement):
+
+ @classmethod
+ def from_gerber(cls, line):
+ line = line.strip()
+ if 'G36' not in line and 'G37' not in line:
+ raise ValueError('%s is not a valid region mode statement' % line)
+ return (cls('on') if line[:3] == 'G36' else cls('off'))
+
+ def __init__(self, mode):
+ super(RegionModeStmt, self).__init__('Region Mode')
+ mode = mode.lower()
+ if mode not in ['on', 'off']:
+ raise ValueError('Valid modes are "on" or "off"')
+ self.mode = mode
+
+ def to_gerber(self):
+ return 'G36*' if self.mode == 'on' else 'G37*'
+
class UnknownStmt(Statement):
""" Unknown Statement
diff --git a/gerber/render/apertures.py b/gerber/render/apertures.py
index f163b1f..52ae50c 100644
--- a/gerber/render/apertures.py
+++ b/gerber/render/apertures.py
@@ -22,16 +22,31 @@ gerber.render.apertures
This module provides base classes for gerber apertures. These are used by
the rendering engine to draw the gerber file.
"""
-
+import math
class Aperture(object):
""" Gerber Aperture base class
"""
def draw(self, ctx, x, y):
- raise NotImplementedError('The draw method must be implemented in an Aperture subclass.')
+ raise NotImplementedError('The draw method must be implemented \
+ in an Aperture subclass.')
def flash(self, ctx, x, y):
- raise NotImplementedError('The flash method must be implemented in an Aperture subclass.')
+ raise NotImplementedError('The flash method must be implemented \
+ in an Aperture subclass.')
+
+ def _arc_params(self, startx, starty, x, y, i, j):
+ center = (startx + i, starty + j)
+ radius = math.sqrt(math.pow(center[0] - x, 2) +
+ math.pow(center[1] - y, 2))
+ delta_x0 = startx - center[0]
+ delta_y0 = center[1] - starty
+ delta_x1 = x - center[0]
+ delta_y1 = center[1] - y
+ start_angle = math.atan2(delta_y0, delta_x0)
+ end_angle = math.atan2(delta_y1, delta_x1)
+ return {'center': center, 'radius': radius,
+ 'start_angle': start_angle, 'end_angle': end_angle}
class Circle(Aperture):
diff --git a/gerber/render/render.py b/gerber/render/render.py
index e40960d..e7ec6ee 100644
--- a/gerber/render/render.py
+++ b/gerber/render/render.py
@@ -22,19 +22,20 @@ from ..gerber_statements import (
class GerberContext(object):
- settings = {}
-
- x = 0
- y = 0
-
- aperture = 0
- interpolation = 'linear'
-
- image_polarity = 'positive'
- level_polarity = 'dark'
def __init__(self):
- pass
+ self.settings = {}
+ self.x = 0
+ self.y = 0
+
+ self.aperture = 0
+ self.interpolation = 'linear'
+ self.direction = 'clockwise'
+ self.image_polarity = 'positive'
+ self.level_polarity = 'dark'
+ self.region_mode = 'off'
+ self.color = (0.7215, 0.451, 0.200)
+ self.drill_color = (0.25, 0.25, 0.25)
def set_format(self, settings):
self.settings = settings
@@ -62,6 +63,12 @@ class GerberContext(object):
def set_aperture(self, d):
self.aperture = d
+ def set_color(self, color):
+ self.color = color
+
+ def set_drill_color(self, color):
+ self.drill_color = color
+
def resolve(self, x, y):
x = x if x is not None else self.x
y = y if y is not None else self.y
@@ -76,13 +83,13 @@ class GerberContext(object):
else:
self.x, self.y = x, y
- def stroke(self, x, y):
+ def stroke(self, x, y, i, j):
pass
def line(self, x, y):
pass
- def arc(self, x, y):
+ def arc(self, x, y, i, j):
pass
def flash(self, x, y):
@@ -109,7 +116,8 @@ class GerberContext(object):
def _evaluate_param(self, stmt):
if stmt.param == "FS":
- self.set_coord_format(stmt.zero_suppression, stmt.format, stmt.notation)
+ self.set_coord_format(stmt.zero_suppression, stmt.format,
+ stmt.notation)
self.set_coord_notation(stmt.notation)
elif stmt.param == "MO:":
self.set_coord_unit(stmt.mode)
@@ -123,9 +131,11 @@ class GerberContext(object):
def _evaluate_coord(self, stmt):
if stmt.function in ("G01", "G1", "G02", "G2", "G03", "G3"):
self.set_interpolation(stmt.function)
-
+ if stmt.function not in ('G01', 'G1'):
+ self.direction = ('clockwise' if stmt.function in ('G02', 'G2')
+ else 'counterclockwise')
if stmt.op == "D01":
- self.stroke(stmt.x, stmt.y)
+ self.stroke(stmt.x, stmt.y, stmt.i, stmt.j)
elif stmt.op == "D02":
self.move(stmt.x, stmt.y)
elif stmt.op == "D03":
diff --git a/gerber/render/svgwrite_backend.py b/gerber/render/svgwrite_backend.py
index 8d84da1..3b2f3c1 100644
--- a/gerber/render/svgwrite_backend.py
+++ b/gerber/render/svgwrite_backend.py
@@ -23,64 +23,71 @@ import svgwrite
SCALE = 300
+def convert_color(color):
+ color = tuple([int(ch * 255) for ch in color])
+ return 'rgb(%d, %d, %d)' % color
+
class SvgCircle(Circle):
- def draw(self, ctx, x, y):
+ def line(self, ctx, x, y, color='rgb(184, 115, 51)'):
return ctx.dwg.line(start=(ctx.x * SCALE, -ctx.y * SCALE),
end=(x * SCALE, -y * SCALE),
- stroke="rgb(184, 115, 51)",
+ stroke=color,
stroke_width=SCALE * self.diameter,
stroke_linecap="round")
- def flash(self, ctx, x, y):
+ def arc(self, ctx, x, y, i, j, direction, color='rgb(184, 115, 51)'):
+ pass
+
+ def flash(self, ctx, x, y, color='rgb(184, 115, 51)'):
return [ctx.dwg.circle(center=(x * SCALE, -y * SCALE),
r = SCALE * (self.diameter / 2.0),
- fill='rgb(184, 115, 51)'), ]
+ fill=color), ]
class SvgRect(Rect):
- def draw(self, ctx, x, y):
+ def line(self, ctx, x, y, color='rgb(184, 115, 51)'):
return ctx.dwg.line(start=(ctx.x * SCALE, -ctx.y * SCALE),
end=(x * SCALE, -y * SCALE),
- stroke="rgb(184, 115, 51)", stroke_width=2,
+ stroke=color, stroke_width=2,
stroke_linecap="butt")
- def flash(self, ctx, x, y):
+ def flash(self, ctx, x, y, color='rgb(184, 115, 51)'):
xsize, ysize = self.size
return [ctx.dwg.rect(insert=(SCALE * (x - (xsize / 2)),
-SCALE * (y + (ysize / 2))),
size=(SCALE * xsize, SCALE * ysize),
- fill="rgb(184, 115, 51)"), ]
+ fill=color), ]
class SvgObround(Obround):
- def draw(self, ctx, x, y):
+ def line(self, ctx, x, y, color='rgb(184, 115, 51)'):
pass
- def flash(self, ctx, x, y):
+ def flash(self, ctx, x, y, color='rgb(184, 115, 51)'):
xsize, ysize = self.size
# horizontal obround
if xsize == ysize:
return [ctx.dwg.circle(center=(x * SCALE, -y * SCALE),
r = SCALE * (x / 2.0),
- fill='rgb(184, 115, 51)'), ]
+ fill=color), ]
if xsize > ysize:
rectx = xsize - ysize
recty = ysize
lcircle = ctx.dwg.circle(center=((x - (rectx / 2.0)) * SCALE,
-y * SCALE),
r = SCALE * (ysize / 2.0),
- fill='rgb(184, 115, 51)')
+ fill=color)
rcircle = ctx.dwg.circle(center=((x + (rectx / 2.0)) * SCALE,
-y * SCALE),
r = SCALE * (ysize / 2.0),
- fill='rgb(184, 115, 51)')
+ fill=color)
rect = ctx.dwg.rect(insert=(SCALE * (x - (xsize / 2.)),
-SCALE * (y + (ysize / 2.))),
size=(SCALE * xsize, SCALE * ysize),
- fill='rgb(184, 115, 51)')
+ fill=color)
return [lcircle, rcircle, rect, ]
# Vertical obround
@@ -90,17 +97,17 @@ class SvgObround(Obround):
lcircle = ctx.dwg.circle(center=(x * SCALE,
(y - (recty / 2.)) * -SCALE),
r = SCALE * (xsize / 2.),
- fill='rgb(184, 115, 51)')
+ fill=color)
ucircle = ctx.dwg.circle(center=(x * SCALE,
(y + (recty / 2.)) * -SCALE),
r = SCALE * (xsize / 2.),
- fill='rgb(184, 115, 51)')
+ fill=color)
rect = ctx.dwg.rect(insert=(SCALE * (x - (xsize / 2.)),
-SCALE * (y + (ysize / 2.))),
size=(SCALE * xsize, SCALE * ysize),
- fill='rgb(184, 115, 51)')
+ fill=color)
return [lcircle, ucircle, rect, ]
@@ -116,7 +123,9 @@ class GerberSvgContext(GerberContext):
xbounds, ybounds = bounds
size = (SCALE * (xbounds[1] - xbounds[0]), SCALE * (ybounds[1] - ybounds[0]))
if not self.background:
- self.dwg.add(self.dwg.rect(insert=(SCALE * xbounds[0], -SCALE * ybounds[1]), size=size, fill="black"))
+ self.dwg.add(self.dwg.rect(insert=(SCALE * xbounds[0],
+ -SCALE * ybounds[1]),
+ size=size, fill="black"))
self.background = True
def define_aperture(self, d, shape, modifiers):
@@ -129,13 +138,13 @@ class GerberSvgContext(GerberContext):
aperture = SvgObround(size=modifiers[0][0:2])
self.apertures[d] = aperture
- def stroke(self, x, y):
- super(GerberSvgContext, self).stroke(x, y)
+ def stroke(self, x, y, i, j):
+ super(GerberSvgContext, self).stroke(x, y, i, j)
if self.interpolation == 'linear':
self.line(x, y)
elif self.interpolation == 'arc':
- self.arc(x, y)
+ self.arc(x, y, i, j)
def line(self, x, y):
super(GerberSvgContext, self).line(x, y)
@@ -143,11 +152,18 @@ class GerberSvgContext(GerberContext):
ap = self.apertures.get(self.aperture, None)
if ap is None:
return
- self.dwg.add(ap.draw(self, x, y))
+ self.dwg.add(ap.line(self, x, y, convert_color(self.color)))
self.move(x, y, resolve=False)
- def arc(self, x, y):
- super(GerberSvgContext, self).arc(x, y)
+ def arc(self, x, y, i, j):
+ super(GerberSvgContext, self).arc(x, y, i, j)
+ x, y = self.resolve(x, y)
+ ap = self.apertures.get(self.aperture, None)
+ if ap is None:
+ return
+ #self.dwg.add(ap.arc(self, x, y, i, j, self.direction,
+ # convert_color(self.color)))
+ self.move(x, y, resolve=False)
def flash(self, x, y):
super(GerberSvgContext, self).flash(x, y)
@@ -155,12 +171,14 @@ class GerberSvgContext(GerberContext):
ap = self.apertures.get(self.aperture, None)
if ap is None:
return
- for shape in ap.flash(self, x, y):
+ for shape in ap.flash(self, x, y, convert_color(self.color)):
self.dwg.add(shape)
self.move(x, y, resolve=False)
def drill(self, x, y, diameter):
- hit = self.dwg.circle(center=(x*SCALE, -y*SCALE), r=SCALE*(diameter/2.0), fill='gray')
+ hit = self.dwg.circle(center=(x*SCALE, -y*SCALE),
+ r=SCALE*(diameter/2.0),
+ fill=convert_color(self.drill_color))
self.dwg.add(hit)
def dump(self, filename):