diff options
Diffstat (limited to 'gerber/gerber.py')
-rw-r--r-- | gerber/gerber.py | 567 |
1 files changed, 0 insertions, 567 deletions
diff --git a/gerber/gerber.py b/gerber/gerber.py deleted file mode 100644 index ee7fcbc..0000000 --- a/gerber/gerber.py +++ /dev/null @@ -1,567 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import re -import json -import traceback - -import svgwrite - - -def red(s): - return '\033[1;31m{0}\033[0;m'.format(s) - - -class Statement(object): - def __init__(self, type): - self.type = type - - def __str__(self): - s = "<{0} ".format(self.__class__.__name__) - - for key, value in self.__dict__.items(): - s += "{0}={1} ".format(key, value) - - s = s.rstrip() + ">" - return s - - -class ParamStmt(Statement): - def __init__(self, param): - Statement.__init__(self, "PARAM") - self.param = param - - -class FSParamStmt(ParamStmt): - def __init__(self, param, zero = "L", notation = "A", x = "24", y = "24"): - ParamStmt.__init__(self, param) - self.zero = zero - self.notation = notation - self.x = x - self.y = y - - -class MOParamStmt(ParamStmt): - def __init__(self, param, mo): - ParamStmt.__init__(self, param) - self.mo = mo - - -class IPParamStmt(ParamStmt): - def __init__(self, param, ip): - ParamStmt.__init__(self, param) - self.ip = ip - - -class OFParamStmt(ParamStmt): - def __init__(self, param, a, b): - ParamStmt.__init__(self, param) - self.a = a - self.b = b - - -class LPParamStmt(ParamStmt): - def __init__(self, param, lp): - ParamStmt.__init__(self, param) - self.lp = lp - - -class ADParamStmt(ParamStmt): - def __init__(self, param, d, shape, modifiers): - ParamStmt.__init__(self, param) - self.d = d - self.shape = shape - self.modifiers = [[x for x in m.split("X")] for m in modifiers.split(",")] - - -class AMParamStmt(ParamStmt): - def __init__(self, param, name, macro): - ParamStmt.__init__(self, param) - self.name = name - self.macro = macro - - -class INParamStmt(ParamStmt): - def __init__(self, param, name): - ParamStmt.__init__(self, param) - self.name = name - - -class LNParamStmt(ParamStmt): - def __init__(self, param, name): - ParamStmt.__init__(self, param) - self.name = name - - -class CoordStmt(Statement): - def __init__(self, function, x, y, i, j, op): - Statement.__init__(self, "COORD") - self.function = function - self.x = x - self.y = y - self.i = i - self.j = j - self.op = op - - -class ApertureStmt(Statement): - def __init__(self, d): - Statement.__init__(self, "APERTURE") - self.d = int(d) - - -class CommentStmt(Statement): - def __init__(self, comment): - Statement.__init__(self, "COMMENT") - self.comment = comment - - -class EofStmt(Statement): - def __init__(self): - Statement.__init__(self, "EOF") - - -class UnknownStmt(Statement): - def __init__(self, line): - Statement.__init__(self, "UNKNOWN") - self.line = line - - -IMAGE_POLARITY_POSITIVE = 1 -IMAGE_POLARITY_NEGATIVE = 2 - -LEVEL_POLARITY_DARK = 1 -LEVEL_POLARITY_CLEAR = 2 - -NOTATION_ABSOLUTE = 1 -NOTATION_INCREMENTAL = 2 - -UNIT_INCH = 1 -UNIT_MM = 2 - -INTERPOLATION_LINEAR = 1 -INTERPOLATION_ARC = 2 - - -class GerberCoordFormat(object): - def __init__(self, zeroes, x, y): - self.omit_leading_zeroes = True if zeroes == "L" else False - self.omit_trailing_zeroes = True if zeroes == "T" else False - self.x_int_digits, self.x_dec_digits = [int(d) for d in x] - self.y_int_digits, self.y_dec_digits = [int(d) for d in y] - - def resolve(self, x, y): - new_x = x - new_y = y - - if new_x is not None: - negative = "-" in new_x - new_x = new_x.replace("-", "") - - missing_zeroes = (self.x_int_digits + self.x_dec_digits) - len(new_x) - - if missing_zeroes and self.omit_leading_zeroes: - new_x = (missing_zeroes * "0") + new_x - elif missing_zeroes and self.omit_trailing_zeroes: - new_x += missing_zeroes * "0" - - new_x = float("{0}{1}.{2}".format("-" if negative else "", new_x[:self.x_int_digits], new_x[self.x_int_digits:])) - - if new_y is not None: - negative = "-" in new_y - new_y = new_y.replace("-", "") - - missing_zeroes = (self.y_int_digits + self.y_dec_digits) - len(new_y) - - if missing_zeroes and self.omit_leading_zeroes: - new_y = (missing_zeroes * "0") + new_y - elif missing_zeroes and self.omit_trailing_zeroes: - new_y += missing_zeroes * "0" - - new_y = float("{0}{1}.{2}".format("-" if negative else "", new_y[:self.y_int_digits], new_y[self.y_int_digits:])) - - return new_x, new_y - - -class GerberContext(object): - coord_format = None - coord_notation = NOTATION_ABSOLUTE - coord_unit = None - - x = 0 - y = 0 - - aperture = 0 - interpolation = INTERPOLATION_LINEAR - - image_polarity = IMAGE_POLARITY_POSITIVE - level_polarity = LEVEL_POLARITY_DARK - - def __init__(self): - pass - - def set_coord_format(self, zeroes, x, y): - self.coord_format = GerberCoordFormat(zeroes, x, y) - - def set_coord_notation(self, notation): - self.coord_notation = NOTATION_ABSOLUTE if notation == "A" else NOTATION_INCREMENTAL - - def set_coord_unit(self, unit): - self.coord_unit = UNIT_INCH if unit == "IN" else UNIT_MM - - def set_image_polarity(self, polarity): - self.image_polarity = IMAGE_POLARITY_POSITIVE if polarity == "POS" else IMAGE_POLARITY_NEGATIVE - - def set_level_polarity(self, polarity): - self.level_polarity = LEVEL_POLARITY_DARK if polarity == "D" else LEVEL_POLARITY_CLEAR - - def set_interpolation(self, interpolation): - self.interpolation = INTERPOLATION_LINEAR if interpolation in ("G01", "G1") else INTERPOLATION_ARC - - def set_aperture(self, d): - self.aperture = d - - def resolve(self, x, y): - x, y = self.coord_format.resolve(x, y) - return x or self.x, y or self.y - - def define_aperture(self, d, shape, modifiers): - pass - - def move(self, x, y, resolve=True): - if resolve: - self.x, self.y = self.resolve(x, y) - else: - self.x, self.y = x, y - - def stroke(self, x, y): - pass - - def line(self, x, y): - pass - - def arc(self, x, y): - pass - - def flash(self, x, y): - pass - - -class Shape(object): - pass - - -class Circle(Shape): - def __init__(self, diameter=0): - self.diameter = diameter - - def draw(self, ctx, x, y): - return ctx.dwg.line(start=(ctx.x*300, ctx.y*300), end=(x*300, y*300), stroke="rgb(184, 115, 51)", stroke_width=2, - stroke_linecap="round") - - def flash(self, ctx, x, y): - return ctx.dwg.circle(center=(x*300, y*300), r=300*(self.diameter/2.0), fill="rgb(184, 115, 51)") - - -class Rect(Shape): - def __init__(self, size=0): - self.size = size - - def draw(self, ctx, x, y): - return ctx.dwg.line(start=(ctx.x*300, ctx.y*300), end=(x*300, y*300), stroke="rgb(184, 115, 51)", stroke_width=2, - stroke_linecap="butt") - - def flash(self, ctx, x, y): - return ctx.dwg.rect(insert=(300*x, 300*y), size=(300*float(self.size[0]), 300*float(self.size[1])), fill="rgb(184, 115, 51)") - - -class SvgContext(GerberContext): - def __init__(self): - GerberContext.__init__(self) - - self.apertures = {} - self.dwg = svgwrite.Drawing() - self.dwg.add(self.dwg.rect(insert=(0, 0), size=(2000, 2000), fill="black")) - - def define_aperture(self, d, shape, modifiers): - aperture = None - if shape == "C": - aperture = Circle(diameter=float(modifiers[0][0])) - elif shape == "R": - aperture = Rect(size=modifiers[0][0:2]) - - self.apertures[d] = aperture - - def stroke(self, x, y): - super(SvgContext, self).stroke(x, y) - - if self.interpolation == INTERPOLATION_LINEAR: - self.line(x, y) - elif self.interpolation == INTERPOLATION_ARC: - self.arc(x, y) - - def line(self, x, y): - super(SvgContext, self).line(x, y) - - x, y = self.resolve(x, y) - - ap = self.apertures.get(str(self.aperture), None) - if ap is None: - return - - self.dwg.add(ap.draw(self, x, y)) - - self.move(x, y, resolve=False) - - def arc(self, x, y): - super(SvgContext, self).arc(x, y) - - def flash(self, x, y): - super(SvgContext, self).flash(x, y) - - x, y = self.resolve(x, y) - - ap = self.apertures.get(str(self.aperture), None) - if ap is None: - return - - self.dwg.add(ap.flash(self, x, y)) - - self.move(x, y, resolve=False) - - def dump(self): - self.dwg.saveas("teste.svg") - - -class Gerber(object): - NUMBER = r"[\+-]?\d+" - DECIMAL = r"[\+-]?\d+([.]?\d+)?" - STRING = r"[a-zA-Z0-9_+\-/!?<>”’(){}.\|&@# :]+" - NAME = "[a-zA-Z_$][a-zA-Z_$0-9]+" - FUNCTION = r"G\d{2}" - - COORD_OP = r"D[0]?[123]" - - FS = r"(?P<param>FS)(?P<zero>(L|T))?(?P<notation>(A|I))X(?P<x>[0-7][0-7])Y(?P<y>[0-7][0-7])" - MO = r"(?P<param>MO)(?P<mo>(MM|IN))" - IP = r"(?P<param>IP)(?P<ip>(POS|NEG))" - LP = r"(?P<param>LP)(?P<lp>(D|C))" - AD_CIRCLE = r"(?P<param>AD)D(?P<d>\d+)(?P<shape>C)[,](?P<modifiers>[^,]*)" - AD_RECT = r"(?P<param>AD)D(?P<d>\d+)(?P<shape>R)[,](?P<modifiers>[^,]*)" - AD_OBROUND = r"(?P<param>AD)D(?P<d>\d+)(?P<shape>O)[,](?P<modifiers>[^,]*)" - AD_POLY = r"(?P<param>AD)D(?P<d>\d+)(?P<shape>P)[,](?P<modifiers>[^,]*)" - AD_MACRO = r"(?P<param>AD)D(?P<d>\d+)+(?P<shape>{name})[,](?P<modifiers>[^,]*)".format(name=NAME) - AM = r"(?P<param>AM)(?P<name>{name})\*(?P<macro>.*)".format(name=NAME) - - # begin deprecated - OF = r"(?P<param>OF)(A(?P<a>{decimal}))?(B(?P<b>{decimal}))?".format(decimal=DECIMAL) - IN = r"(?P<param>IN)(?P<name>.*)" - LN = r"(?P<param>LN)(?P<name>.*)" - # end deprecated - - PARAMS = (FS, MO, IP, LP, AD_CIRCLE, AD_RECT, AD_OBROUND, AD_MACRO, AD_POLY, AM, OF, IN, LN) - PARAM_STMT = [re.compile(r"%{0}\*%".format(p)) for p in PARAMS] - - COORD_STMT = re.compile(( - r"(?P<function>{function})?" - r"(X(?P<x>{number}))?(Y(?P<y>{number}))?" - r"(I(?P<i>{number}))?(J(?P<j>{number}))?" - r"(?P<op>{op})?\*".format(number=NUMBER, function=FUNCTION, op=COORD_OP))) - - APERTURE_STMT = re.compile(r"(G54)?D(?P<d>\d+)\*") - - COMMENT_STMT = re.compile(r"G04(?P<comment>{string})(\*)?".format(string=STRING)) - - EOF_STMT = re.compile(r"(?P<eof>M02)\*") - - def __init__(self): - self.statements = [] - self.ctx = SvgContext() - - def parse(self, filename): - fp = open(filename, "r") - data = fp.readlines() - - for stmt in self._parse(data): - self.statements.append(stmt) - self._evaluate(stmt) - - def dump_json(self): - stmts = {"statements": [stmt.__dict__ for stmt in self.statements]} - return json.dumps(stmts) - - def dump_str(self): - s = "" - for stmt in self.statements: - s += str(stmt) + "\n" - return s - - def dump(self): - self.ctx.dump() - - def _parse(self, data): - multiline = None - - for i, line in enumerate(data): - # remove EOL - if multiline: - line = multiline + line.strip() - else: - line = line.strip() - - # skip empty lines - if not len(line): - continue - - # deal with multi-line parameters - if line.startswith("%") and not line.endswith("%"): - multiline = line - continue - else: - multiline = None - - # coord - coords = self._match_many(self.COORD_STMT, line) - if coords: - for coord in coords: - yield CoordStmt(**coord) - continue - - # aperture selection - aperture = self._match_one(self.APERTURE_STMT, line) - if aperture: - yield ApertureStmt(**aperture) - continue - - # comment - comment = self._match_one(self.COMMENT_STMT, line) - if comment: - yield CommentStmt(comment["comment"]) - continue - - # parameter - param = self._match_one_from_many(self.PARAM_STMT, line) - if param: - if param["param"] == "FS": - yield FSParamStmt(**param) - elif param["param"] == "MO": - yield MOParamStmt(**param) - elif param["param"] == "IP": - yield IPParamStmt(**param) - elif param["param"] == "LP": - yield LPParamStmt(**param) - elif param["param"] == "AD": - yield ADParamStmt(**param) - elif param["param"] == "AM": - yield AMParamStmt(**param) - elif param["param"] == "OF": - yield OFParamStmt(**param) - elif param["param"] == "IN": - yield INParamStmt(**param) - elif param["param"] == "LN": - yield LNParamStmt(**param) - else: - yield UnknownStmt(line) - - continue - - # eof - eof = self._match_one(self.EOF_STMT, line) - if eof: - yield EofStmt() - continue - - if False: - print self.COORD_STMT.pattern - print self.APERTURE_STMT.pattern - print self.COMMENT_STMT.pattern - print self.EOF_STMT.pattern - for i in self.PARAM_STMT: - print i.pattern - - yield UnknownStmt(line) - - def _match_one(self, expr, data): - match = expr.match(data) - if match is None: - return {} - else: - return match.groupdict() - - def _match_one_from_many(self, exprs, data): - for expr in exprs: - match = expr.match(data) - if match: - return match.groupdict() - - return {} - - def _match_many(self, expr, data): - result = [] - pos = 0 - while True: - match = expr.match(data, pos) - if match: - result.append(match.groupdict()) - pos = match.endpos - else: - break - - return result - - def _evaluate(self, stmt): - if isinstance(stmt, (CommentStmt, UnknownStmt, EofStmt)): - return - - elif isinstance(stmt, ParamStmt): - self._evaluate_param(stmt) - - elif isinstance(stmt, CoordStmt): - self._evaluate_coord(stmt) - - elif isinstance(stmt, ApertureStmt): - self._evaluate_aperture(stmt) - - else: - raise Exception("Invalid statement to evaluate") - - def _evaluate_param(self, stmt): - if stmt.param == "FS": - self.ctx.set_coord_format(stmt.zero, stmt.x, stmt.y) - self.ctx.set_coord_notation(stmt.notation) - elif stmt.param == "MO:": - self.ctx.set_coord_unit(stmt.mo) - elif stmt.param == "IP:": - self.ctx.set_image_polarity(stmt.ip) - elif stmt.param == "LP:": - self.ctx.set_level_polarity(stmt.lp) - elif stmt.param == "AD": - self.ctx.define_aperture(stmt.d, stmt.shape, stmt.modifiers) - - def _evaluate_coord(self, stmt): - - if stmt.function in ("G01", "G1", "G02", "G2", "G03", "G3"): - self.ctx.set_interpolation(stmt.function) - - if stmt.op == "D01": - self.ctx.stroke(stmt.x, stmt.y) - elif stmt.op == "D02": - self.ctx.move(stmt.x, stmt.y) - elif stmt.op == "D03": - self.ctx.flash(stmt.x, stmt.y) - - def _evaluate_aperture(self, stmt): - self.ctx.set_aperture(stmt.d) - - -if __name__ == "__main__": - import sys - - for f in sys.argv[1:]: - print "parsing: %s" % f - try: - g = Gerber() - g.parse(f) - except Exception, e: - traceback.print_exc() - |