summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gerber/__init__.py16
-rw-r--r--gerber/__main__.py31
-rw-r--r--gerber/parser.py (renamed from gerber/gerber.py)248
-rw-r--r--gerber/render.py140
-rw-r--r--gerber/render_svg.py106
5 files changed, 311 insertions, 230 deletions
diff --git a/gerber/__init__.py b/gerber/__init__.py
index e69de29..0bf7c24 100644
--- a/gerber/__init__.py
+++ b/gerber/__init__.py
@@ -0,0 +1,16 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2013-2014 Paulo Henrique Silva <ph.silva@gmail.com>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gerber/__main__.py b/gerber/__main__.py
new file mode 100644
index 0000000..6f861cf
--- /dev/null
+++ b/gerber/__main__.py
@@ -0,0 +1,31 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2013-2014 Paulo Henrique Silva <ph.silva@gmail.com>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if __name__ == '__main__':
+ from .parser import GerberParser
+ from .render import GerberContext
+
+ import sys
+
+ if len(sys.argv) < 2:
+ print >> sys.stderr, "Usage: python -m gerber <filename> <filename>..."
+ sys.exit(1)
+
+ for filename in sys.argv[1:]:
+ print "parsing %s" % filename
+ g = GerberParser(GerberContext())
+ g.parse(filename)
diff --git a/gerber/gerber.py b/gerber/parser.py
index ee7fcbc..cf755d4 100644
--- a/gerber/gerber.py
+++ b/gerber/parser.py
@@ -1,15 +1,22 @@
-#!/usr/bin/env python
+#! /usr/bin/env python
# -*- coding: utf-8 -*-
-import re
-import json
-import traceback
+# Copyright 2013-2014 Paulo Henrique Silva <ph.silva@gmail.com>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
-import svgwrite
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
-def red(s):
- return '\033[1;31m{0}\033[0;m'.format(s)
+import re
+import json
class Statement(object):
@@ -33,7 +40,7 @@ class ParamStmt(Statement):
class FSParamStmt(ParamStmt):
- def __init__(self, param, zero = "L", notation = "A", x = "24", y = "24"):
+ def __init__(self, param, zero="L", notation="A", x="24", y="24"):
ParamStmt.__init__(self, param)
self.zero = zero
self.notation = notation
@@ -127,213 +134,7 @@ class UnknownStmt(Statement):
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):
+class GerberParser(object):
NUMBER = r"[\+-]?\d+"
DECIMAL = r"[\+-]?\d+([.]?\d+)?"
STRING = r"[a-zA-Z0-9_+\-/!?<>”’(){}.\|&@# :]+"
@@ -374,9 +175,9 @@ class Gerber(object):
EOF_STMT = re.compile(r"(?P<eof>M02)\*")
- def __init__(self):
+ def __init__(self, ctx):
self.statements = []
- self.ctx = SvgContext()
+ self.ctx = ctx
def parse(self, filename):
fp = open(filename, "r")
@@ -552,16 +353,3 @@ class Gerber(object):
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()
-
diff --git a/gerber/render.py b/gerber/render.py
new file mode 100644
index 0000000..dd30ae0
--- /dev/null
+++ b/gerber/render.py
@@ -0,0 +1,140 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2013-2014 Paulo Henrique Silva <ph.silva@gmail.com>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+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
diff --git a/gerber/render_svg.py b/gerber/render_svg.py
new file mode 100644
index 0000000..bfe6859
--- /dev/null
+++ b/gerber/render_svg.py
@@ -0,0 +1,106 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2013-2014 Paulo Henrique Silva <ph.silva@gmail.com>
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .render import GerberContext, INTERPOLATION_LINEAR, INTERPOLATION_ARC
+import svgwrite
+
+
+class Shape(object):
+ pass
+
+
+class Circle(Shape):
+ def __init__(self, diameter=0.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, 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 GerberSvgContext(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(GerberSvgContext, 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(GerberSvgContext, 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(GerberSvgContext, self).arc(x, y)
+
+ def flash(self, x, y):
+ super(GerberSvgContext, 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")