aboutsummaryrefslogtreecommitdiff
path: root/gerberex
diff options
context:
space:
mode:
Diffstat (limited to 'gerberex')
-rw-r--r--gerberex/am_primitive.py35
-rw-r--r--gerberex/composition.py2
-rw-r--r--gerberex/dxf.py2
-rw-r--r--gerberex/excellon.py7
-rw-r--r--gerberex/rs274x.py65
-rw-r--r--gerberex/statements.py38
-rw-r--r--gerberex/utility.py13
7 files changed, 146 insertions, 16 deletions
diff --git a/gerberex/am_primitive.py b/gerberex/am_primitive.py
index 3ce047a..82370f6 100644
--- a/gerberex/am_primitive.py
+++ b/gerberex/am_primitive.py
@@ -7,13 +7,21 @@ from gerber.utils import *
from gerber.am_statements import *
from gerber.am_eval import OpCode
-from gerberex.am_expression import eval_macro
+from gerberex.am_expression import eval_macro, AMConstantExpression, AMOperatorExpression
class AMPrimitiveDef(AMPrimitive):
- def __init__(self, code, exposure=None, rotation=0):
+ def __init__(self, code, exposure=None, rotation=None):
super(AMPrimitiveDef, self).__init__(code, exposure)
+ if not rotation:
+ rotation = AMConstantExpression(0)
self.rotation = rotation
+ def rotate(self, angle, center=None):
+ self.rotation = AMOperatorExpression(AMOperatorExpression.ADD,
+ self.rotation,
+ AMConstantExpression(float(angle)))
+ self.rotation = self.rotation.optimize()
+
def to_inch(self):
pass
@@ -44,12 +52,12 @@ class AMCommentPrimitiveDef(AMPrimitiveDef):
class AMCirclePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
- exposure = 'on' if modifiers[0] == 1 else 'off',
- diameter = modifiers[1],
- center_x = modifiers[2],
- center_y = modifiers[3],
+ exposure = 'on' if modifiers[0].value == 1 else 'off'
+ diameter = modifiers[1]
+ center_x = modifiers[2]
+ center_y = modifiers[3]
rotation = modifiers[4]
- return cls(code, expressions, center_x, center_y, rotation)
+ return cls(code, exposure, diameter, center_x, center_y, rotation)
def __init__(self, code, exposure, diameter, center_x, center_y, rotation):
super(AMCirclePrimitiveDef, self).__init__(code, exposure, rotation)
@@ -87,7 +95,7 @@ class AMVectorLinePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
- exposure = 'on' if modifiers[0] == 1 else 'off'
+ exposure = 'on' if modifiers[0].value == 1 else 'off'
width = modifiers[1]
start_x = modifiers[2]
start_y = modifiers[3]
@@ -141,7 +149,7 @@ class AMCenterLinePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
- exposure = 'on' if modifiers[0] == 1 else 'off'
+ exposure = 'on' if modifiers[0].value == 1 else 'off'
width = modifiers[1]
height = modifiers[2]
x = modifiers[3]
@@ -191,7 +199,7 @@ class AMOutlinePrimitiveDef(AMPrimitiveDef):
def from_modifiers(cls, code, modifiers):
num_points = modifiers[1] + 1
code = code
- exposure = 'on' if modifiers[0] == 1 else 'off'
+ exposure = 'on' if modifiers[0].value == 1 else 'off'
addrs = modifiers[2:num_points * 2]
rotation = modifiers[3 + num_points * 2]
return cls(code, exposure, addrs, rotation)
@@ -231,7 +239,7 @@ class AMPolygonPrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
code = code
- exposure = 'on' if modifiers[0] == 1 else 'off'
+ exposure = 'on' if modifiers[0].value == 1 else 'off'
vertices = modifiers[1]
x = modifiers[2]
y = modifiers[3]
@@ -417,6 +425,9 @@ class AMVariableDef(object):
yield i
yield (OpCode.STORE, self.number)
+ def rotate(self, angle, center=None):
+ pass
+
def to_primitive_defs(instructions):
classes = {
0: AMCommentPrimitiveDef,
@@ -434,4 +445,4 @@ def to_primitive_defs(instructions):
yield AMVariableDef(-code, modifiers[0])
else:
primitive = classes[code]
- yield primitive.from_modifiers(code, modifiers) \ No newline at end of file
+ yield primitive.from_modifiers(code, modifiers)
diff --git a/gerberex/composition.py b/gerberex/composition.py
index 2613a61..afcaf97 100644
--- a/gerberex/composition.py
+++ b/gerberex/composition.py
@@ -101,7 +101,7 @@ class GerberComposition(Composition):
if not self.settings:
self.settings = file.settings
- self.param_statements = file.header
+ self.param_statements = [file.header]
def _register_aperture_macro(self, statement):
diff --git a/gerberex/dxf.py b/gerberex/dxf.py
index b641924..11072b5 100644
--- a/gerberex/dxf.py
+++ b/gerberex/dxf.py
@@ -111,7 +111,7 @@ class DxfArcStatement(DxfStatement):
settings.zero_suppression),
write_gerber_value(begin_y, settings.format,
settings.zero_suppression),
- '03' if deg0 > deg1 else '02',
+ '03',
write_gerber_value(end_x, settings.format,
settings.zero_suppression),
write_gerber_value(end_y, settings.format,
diff --git a/gerberex/excellon.py b/gerberex/excellon.py
index 78e6e5f..90d6742 100644
--- a/gerberex/excellon.py
+++ b/gerberex/excellon.py
@@ -6,6 +6,7 @@
from gerber.excellon import (ExcellonParser, detect_excellon_format, ExcellonFile)
from gerber.excellon_statements import UnitStmt
from gerber.cam import FileSettings
+from gerberex.utility import rotate
def loads(data, filename=None, settings=None, tools=None, format=None):
if not settings:
@@ -27,6 +28,12 @@ class ExcellonFileEx(ExcellonFile):
def __init__(self, statements, tools, hits, settings, filename=None):
super(ExcellonFileEx, self).__init__(statements, tools, hits, settings, filename)
+ def rotate(self, angle, center=(0,0)):
+ if angle % 360 == 0:
+ return
+ for hit in self.hits:
+ hit.position = rotate(hit.position[0], hit.position[1], angle, center)
+
class UnitStmtEx(UnitStmt):
@classmethod
def from_statement(cls, stmt):
diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py
index e9d82cd..4b477d3 100644
--- a/gerberex/rs274x.py
+++ b/gerberex/rs274x.py
@@ -4,7 +4,9 @@
# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
import gerber.rs274x
-from gerberex.statements import (AMParamStmt, AMParamStmtEx)
+from gerber.gerber_statements import ADParamStmt, CoordStmt
+from gerberex.statements import AMParamStmt, AMParamStmtEx
+from gerberex.utility import rotate
class GerberFile(gerber.rs274x.GerberFile):
@classmethod
@@ -23,3 +25,64 @@ class GerberFile(gerber.rs274x.GerberFile):
def __init__(self, statements, settings, primitives, apertures, filename=None):
super(GerberFile, self).__init__(statements, settings, primitives, apertures, filename)
+
+ def rotate(self, angle, center=(0,0)):
+ if angle % 360 == 0:
+ return
+ self._generalize_aperture()
+ for statement in self.statements:
+ if isinstance(statement, AMParamStmtEx):
+ statement.rotate(angle, center)
+ elif isinstance(statement, CoordStmt) and statement.x != None and statement.y != None:
+ statement.x, statement.y = rotate(statement.x, statement.y, angle, center)
+
+ def _generalize_aperture(self):
+ RECTANGLE = 0
+ LANDSCAPE_OBROUND = 1
+ PORTRATE_OBROUND = 2
+ POLYGON = 3
+ macro_defs = [
+ ('MACR', AMParamStmtEx.rectangle),
+ ('MACLO', AMParamStmtEx.landscape_obround),
+ ('MACPO', AMParamStmtEx.portrate_obround),
+ ('MACP', AMParamStmtEx.polygon)
+ ]
+
+ need_to_change = False
+ insert_point = 0
+ last_aperture = 0
+ macros = {}
+ for idx in range(0, len(self.statements)):
+ statement = self.statements[idx]
+ if isinstance(statement, AMParamStmtEx):
+ macros[statement.name] = statement
+ if not need_to_change:
+ insert_point = idx + 1
+ if isinstance(statement, ADParamStmt) and statement.shape in ['R', 'O', 'P']:
+ need_to_change = True
+ last_aperture = idx
+
+ if need_to_change:
+ for idx in range(0, len(macro_defs)):
+ macro_def = macro_defs[idx]
+ name = macro_def[0]
+ num = 1
+ while name in macros:
+ name = '%s_%d' % (macro_def[0], num)
+ num += 1
+ self.statements.insert(insert_point, macro_def[1](name))
+ macro_defs[idx] = (name, macro_def[1])
+ for idx in range(insert_point, last_aperture + len(macro_defs) + 1):
+ statement = self.statements[idx]
+ if isinstance(statement, ADParamStmt):
+ if statement.shape == 'R':
+ statement.shape = macro_defs[RECTANGLE][0]
+ elif statement.shape == 'O':
+ x = statement.modifiers[0] \
+ if len(statement.modifiers) > 0 else 0
+ y = statement.modifiers[1] \
+ if len(statement.modifiers) > 1 else 0
+ statement.shape = macro_defs[LANDSCAPE_OBROUND][0] \
+ if x > y else macro_defs[PORTRATE_OBROUND][0]
+ elif statement.shape == 'P':
+ statement.shape = macro_defs[POLYGON][0]
diff --git a/gerberex/statements.py b/gerberex/statements.py
index 77ca235..c41acb9 100644
--- a/gerberex/statements.py
+++ b/gerberex/statements.py
@@ -11,9 +11,41 @@ class AMParamStmtEx(AMParamStmt):
def from_stmt(cls, stmt):
return cls(stmt.param, stmt.name, stmt.macro)
+ @classmethod
+ def circle(cls, name):
+ return cls('AM', name, '1,1,$1,0,0,0*1,0,$2,0,0,0')
+
+ @classmethod
+ def rectangle(cls, name):
+ return cls('AM', name, '21,1,$1,$2,0,0,0*1,0,$3,0,0,0')
+
+ @classmethod
+ def landscape_obround(cls, name):
+ return cls(
+ 'AM', name,
+ '$4=$1-$2*'
+ '21,1,$1-$4,$2,0,0,0*'
+ '1,1,$4,$4/2,0,0*'
+ '1,1,$4,-$4/2,0,0*'
+ '1,0,$3,0,0,0')
+
+ @classmethod
+ def portrate_obround(cls, name):
+ return cls(
+ 'AM', name,
+ '$4=$2-$1*'
+ '21,1,$1,$2-$4,0,0,0*'
+ '1,1,$4,0,$4/2,0*'
+ '1,1,$4,0,-$4/2,0*'
+ '1,0,$3,0,0,0')
+
+ @classmethod
+ def polygon(cls, name):
+ return cls('AM', name, '5,1,$2,0,0,$1,$3*1,0,$4,0,0,0')
+
def __init__(self, param, name, macro):
super(AMParamStmtEx, self).__init__(param, name, macro)
- self.primitive_defs = to_primitive_defs(self.instructions)
+ self.primitive_defs = list(to_primitive_defs(self.instructions))
def to_inch(self):
if self.units == 'metric':
@@ -32,3 +64,7 @@ class AMParamStmtEx(AMParamStmt):
for p in self.primitive_defs:
yield p.to_gerber(settings)
return "%%AM%s*\n%s%%" % (self.name, '\n'.join(plist()))
+
+ def rotate(self, angle, center=None):
+ for primitive_def in self.primitive_defs:
+ primitive_def.rotate(angle, center)
diff --git a/gerberex/utility.py b/gerberex/utility.py
new file mode 100644
index 0000000..852519a
--- /dev/null
+++ b/gerberex/utility.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
+
+from math import cos, sin, pi
+
+def rotate(x, y, angle, center):
+ x0 = x - center[0]
+ y0 = y - center[1]
+ angle = angle * pi / 180.0
+ return (cos(angle) * x0 - sin(angle) * y0 + center[0],
+ sin(angle) * x0 + cos(angle) * y0 + center[1])