From 76c03a55c91addff71339d80cf17560926f1580b Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Fri, 10 Oct 2014 20:36:38 -0400 Subject: Working region fills and level polarity. Renders Altium-generated gerbers like a champ! --- gerber/gerber_statements.py | 16 ++++++++-------- gerber/render/render.py | 23 +++++++++++++++-------- gerber/render/svgwrite_backend.py | 22 ++++++++++++++++++++-- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py index 9072b58..a22eae2 100644 --- a/gerber/gerber_statements.py +++ b/gerber/gerber_statements.py @@ -17,9 +17,9 @@ __all__ = ['FSParamStmt', 'MOParamStmt', 'IPParamStmt', 'OFParamStmt', class Statement(object): """ Gerber statement Base class - + The statement class provides a type attribute. - + Parameters ---------- type : string @@ -27,7 +27,7 @@ class Statement(object): Attributes ---------- - type : string + type : string String identifying the statement type. """ def __init__(self, stype): @@ -45,9 +45,9 @@ class Statement(object): class ParamStmt(Statement): """ Gerber parameter statement Base class - + The parameter statement class provides a parameter type attribute. - + Parameters ---------- param : string @@ -55,7 +55,7 @@ class ParamStmt(Statement): Attributes ---------- - param : string + param : string Parameter type code """ def __init__(self, param): @@ -260,7 +260,7 @@ class LPParamStmt(ParamStmt): @classmethod def from_dict(cls, stmt_dict): - param = stmt_dict.get('lp') + param = stmt_dict['param'] lp = 'clear' if stmt_dict.get('lp') == 'C' else 'dark' return cls(param, lp) @@ -667,6 +667,6 @@ class UnknownStmt(Statement): def __init__(self, line): Statement.__init__(self, "UNKNOWN") self.line = line - + def to_gerber(self): return self.line diff --git a/gerber/render/render.py b/gerber/render/render.py index e76aed1..48a53f8 100644 --- a/gerber/render/render.py +++ b/gerber/render/render.py @@ -96,7 +96,7 @@ class GerberContext(object): self.level_polarity = 'dark' self.region_mode = 'off' self.quadrant_mode = 'multi-quadrant' - + self.step_and_repeat = (1, 1, 0, 0) self.color = (0.7215, 0.451, 0.200) self.drill_color = (0.25, 0.25, 0.25) self.background_color = (0.0, 0.0, 0.0) @@ -415,6 +415,12 @@ class GerberContext(object): """ pass + def region_contour(self, x, y): + pass + + def fill_region(self): + pass + def evaluate(self, stmt): """ Evaluate Gerber statement and update image accordingly. @@ -450,7 +456,7 @@ class GerberContext(object): def _evaluate_mode(self, stmt): if stmt.type == 'RegionMode': if self.region_mode == 'on' and stmt.mode == 'off': - self._fill_region() + self.fill_region() self.region_mode = stmt.mode elif stmt.type == 'QuadrantMode': self.quadrant_mode = stmt.mode @@ -460,11 +466,11 @@ class GerberContext(object): self.set_coord_format(stmt.zero_suppression, stmt.format, stmt.notation) self.set_coord_notation(stmt.notation) - elif stmt.param == "MO:": + elif stmt.param == "MO": self.set_coord_unit(stmt.mode) - elif stmt.param == "IP:": + elif stmt.param == "IP": self.set_image_polarity(stmt.ip) - elif stmt.param == "LP:": + elif stmt.param == "LP": self.set_level_polarity(stmt.lp) elif stmt.param == "AD": self.define_aperture(stmt.d, stmt.shape, stmt.modifiers) @@ -477,7 +483,10 @@ class GerberContext(object): self.direction = ('clockwise' if stmt.function in ('G02', 'G2') else 'counterclockwise') if stmt.op == "D01": - self.stroke(stmt.x, stmt.y, stmt.i, stmt.j) + if self.region_mode == 'on': + self.region_contour(stmt.x, stmt.y) + else: + self.stroke(stmt.x, stmt.y, stmt.i, stmt.j) elif stmt.op == "D02": self.move(stmt.x, stmt.y) elif stmt.op == "D03": @@ -486,5 +495,3 @@ class GerberContext(object): def _evaluate_aperture(self, stmt): self.set_aperture(stmt.d) - def _fill_region(self): - pass diff --git a/gerber/render/svgwrite_backend.py b/gerber/render/svgwrite_backend.py index 7570c84..886b4f8 100644 --- a/gerber/render/svgwrite_backend.py +++ b/gerber/render/svgwrite_backend.py @@ -118,6 +118,7 @@ class GerberSvgContext(GerberContext): self.apertures = {} self.dwg = svgwrite.Drawing() self.background = False + self.region_path = None def set_bounds(self, bounds): xbounds, ybounds = bounds @@ -125,7 +126,7 @@ class GerberSvgContext(GerberContext): if not self.background: self.dwg.add(self.dwg.rect(insert=(SCALE * xbounds[0], -SCALE * ybounds[1]), - size=size, fill="black")) + size=size, fill=convert_color(self.background_color))) self.background = True def define_aperture(self, d, shape, modifiers): @@ -173,7 +174,8 @@ class GerberSvgContext(GerberContext): ap = self.apertures.get(self.aperture, None) if ap is None: return - color = (convert_color(self.color) if self.level_polarity == 'dark' + + color = (convert_color(self.color) if self.level_polarity == 'dark' else convert_color(self.background_color)) for shape in ap.flash(self, x, y, color): self.dwg.add(shape) @@ -185,5 +187,21 @@ class GerberSvgContext(GerberContext): fill=convert_color(self.drill_color)) self.dwg.add(hit) + def region_contour(self, x, y): + super(GerberSvgContext, self).region_contour(x, y) + x, y = self.resolve(x, y) + color = (convert_color(self.color) if self.level_polarity == 'dark' + else convert_color(self.background_color)) + if self.region_path is None: + self.region_path = self.dwg.path(d = 'M %f, %f' % + (self.x*SCALE, -self.y*SCALE), + fill = color, stroke = 'none') + self.region_path.push('L %f, %f' % (x*SCALE, -y*SCALE)) + self.move(x, y, resolve=False) + + def fill_region(self): + self.dwg.add(self.region_path) + self.region_path = None + def dump(self, filename): self.dwg.saveas(filename) -- cgit