start arc
This commit is contained in:
parent
1653ae5cbe
commit
bcb6cbc50d
5 changed files with 116 additions and 47 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue