aboutsummaryrefslogtreecommitdiff
path: root/gerberex
diff options
context:
space:
mode:
Diffstat (limited to 'gerberex')
-rw-r--r--gerberex/am_expression.py4
-rw-r--r--gerberex/am_primitive.py16
-rw-r--r--gerberex/composition.py4
-rw-r--r--gerberex/excellon.py17
-rw-r--r--gerberex/gerber_statements.py115
-rw-r--r--gerberex/rs274x.py26
-rw-r--r--gerberex/statements.py70
-rw-r--r--gerberex/utility.py3
8 files changed, 163 insertions, 92 deletions
diff --git a/gerberex/am_expression.py b/gerberex/am_expression.py
index 7aa95c8..b758df1 100644
--- a/gerberex/am_expression.py
+++ b/gerberex/am_expression.py
@@ -49,7 +49,7 @@ class AMConstantExpression(AMExpression):
return self
def to_gerber(self, settings=None):
- return str(self._value)
+ return '%.6g' % self._value
def to_instructions(self):
return [(OpCode.PUSH, self._value)]
@@ -179,5 +179,3 @@ def eval_macro(instructions):
elif opcode == OpCode.PRIM:
yield (argument, stack)
stack = []
-
-
diff --git a/gerberex/am_primitive.py b/gerberex/am_primitive.py
index 82370f6..df55573 100644
--- a/gerberex/am_primitive.py
+++ b/gerberex/am_primitive.py
@@ -117,14 +117,14 @@ class AMVectorLinePrimitiveDef(AMPrimitiveDef):
self.start_x = self.start_x.to_inch().optimize()
self.start_y = self.start_y.to_inch().optimize()
self.end_x = self.end_x.to_inch().optimize()
- self.end_y = self.end_x.to_inch().optimize()
+ self.end_y = self.end_y.to_inch().optimize()
def to_metric(self):
self.width = self.width.to_metric().optimize()
self.start_x = self.start_x.to_metric().optimize()
self.start_y = self.start_y.to_metric().optimize()
self.end_x = self.end_x.to_metric().optimize()
- self.end_y = self.end_x.to_metric().optimize()
+ self.end_y = self.end_y.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
@@ -197,11 +197,11 @@ class AMCenterLinePrimitiveDef(AMPrimitiveDef):
class AMOutlinePrimitiveDef(AMPrimitiveDef):
@classmethod
def from_modifiers(cls, code, modifiers):
- num_points = modifiers[1] + 1
+ num_points = int(modifiers[1].value + 1)
code = code
exposure = 'on' if modifiers[0].value == 1 else 'off'
- addrs = modifiers[2:num_points * 2]
- rotation = modifiers[3 + num_points * 2]
+ addrs = modifiers[2:num_points * 2 + 2]
+ rotation = modifiers[2 + num_points * 2]
return cls(code, exposure, addrs, rotation)
def __init__(self, code, exposure, addrs, rotation):
@@ -209,10 +209,10 @@ class AMOutlinePrimitiveDef(AMPrimitiveDef):
self.addrs = addrs
def to_inch(self):
- self.addrs = [i.to_inch() for i in self.addrs]
+ self.addrs = [i.to_inch().optimize() for i in self.addrs]
def to_metric(self):
- self.addrs = [i.to_metric() for i in self.addrs]
+ self.addrs = [i.to_metric().optimize() for i in self.addrs]
def to_gerber(self, settings=None):
def strs():
@@ -262,7 +262,7 @@ class AMPolygonPrimitiveDef(AMPrimitiveDef):
def to_metric(self):
self.x = self.x.to_metric().optimize()
self.y = self.y.to_metric().optimize()
- self.diameter = self.diameter.to_inch().optimize()
+ self.diameter = self.diameter.to_metric().optimize()
def to_gerber(self, settings=None):
data = dict(code = self.code,
diff --git a/gerberex/composition.py b/gerberex/composition.py
index 73f5702..7abf090 100644
--- a/gerberex/composition.py
+++ b/gerberex/composition.py
@@ -202,8 +202,8 @@ class DrillComposition(Composition):
file.to_inch()
if not self.header1_statements:
- self.header1_statements = file.header
- self.header2_statements = file.header2
+ self.header1_statements = [file.header]
+ self.header2_statements = [file.header2]
tool = self._register_tool(ExcellonTool(self.settings, number=1, diameter=file.width))
self.dxf_statements.append((tool.number, file.statements))
diff --git a/gerberex/excellon.py b/gerberex/excellon.py
index 90d6742..b72b95b 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 gerber.utils import inch, metric
from gerberex.utility import rotate
def loads(data, filename=None, settings=None, tools=None, format=None):
@@ -33,6 +34,19 @@ class ExcellonFileEx(ExcellonFile):
return
for hit in self.hits:
hit.position = rotate(hit.position[0], hit.position[1], angle, center)
+
+ def to_inch(self):
+ if self.units == 'metric':
+ super(ExcellonFileEx, self).to_inch()
+ for hit in self.hits:
+ hit.position = (inch(hit.position[0]), inch(hit.position[1]))
+
+ def to_metric(self):
+ if self.units == 'inch':
+ super(ExcellonFileEx, self).to_metric()
+ for hit in self.hits:
+ hit.position = (metric(hit.position[0]), metric(hit.position[1]))
+
class UnitStmtEx(UnitStmt):
@classmethod
@@ -43,7 +57,8 @@ class UnitStmtEx(UnitStmt):
super(UnitStmtEx, self).__init__(units, zeros, format, **kwargs)
def to_excellon(self, settings=None):
+ format = settings.format if settings else self.format
stmt = '%s,%s,%s.%s' % ('INCH' if self.units == 'inch' else 'METRIC',
'LZ' if self.zeros == 'leading' else 'TZ',
- '0' * self.format[0], '0' * self.format[1])
+ '0' * format[0], '0' * format[1])
return stmt
diff --git a/gerberex/gerber_statements.py b/gerberex/gerber_statements.py
new file mode 100644
index 0000000..c2eb565
--- /dev/null
+++ b/gerberex/gerber_statements.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
+
+from gerber.gerber_statements import AMParamStmt, ADParamStmt
+from gerber.utils import inch, metric
+from gerberex.am_primitive import to_primitive_defs
+
+class AMParamStmtEx(AMParamStmt):
+ @classmethod
+ def from_stmt(cls, stmt):
+ return cls(stmt.param, stmt.name, stmt.macro, stmt.units)
+
+ @classmethod
+ def circle(cls, name, units):
+ return cls('AM', name, '1,1,$1,0,0,0*1,0,$2,0,0,0', units)
+
+ @classmethod
+ def rectangle(cls, name, units):
+ return cls('AM', name, '21,1,$1,$2,0,0,0*1,0,$3,0,0,0', units)
+
+ @classmethod
+ def landscape_obround(cls, name, units):
+ return cls(
+ 'AM', name,
+ '$4=$1-$2*'
+ '$5=$1-$4*'
+ '21,1,$5,$2,0,0,0*'
+ '1,1,$4,$4/2,0,0*'
+ '1,1,$4,-$4/2,0,0*'
+ '1,0,$3,0,0,0', units)
+
+ @classmethod
+ def portrate_obround(cls, name, units):
+ return cls(
+ 'AM', name,
+ '$4=$2-$1*'
+ '$5=$2-$4*'
+ '21,1,$1,$5,0,0,0*'
+ '1,1,$4,0,$4/2,0*'
+ '1,1,$4,0,-$4/2,0*'
+ '1,0,$3,0,0,0', units)
+
+ @classmethod
+ def polygon(cls, name, units):
+ return cls('AM', name, '5,1,$2,0,0,$1,$3*1,0,$4,0,0,0', units)
+
+ def __init__(self, param, name, macro, units):
+ super(AMParamStmtEx, self).__init__(param, name, macro)
+ self.units = units
+ self.primitive_defs = list(to_primitive_defs(self.instructions))
+
+ def to_inch(self):
+ if self.units == 'metric':
+ self.units = 'inch'
+ for p in self.primitive_defs:
+ p.to_inch()
+
+ def to_metric(self):
+ if self.units == 'inch':
+ self.units = 'metric'
+ for p in self.primitive_defs:
+ p.to_metric()
+
+ def to_gerber(self, settings = None):
+ def plist():
+ 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)
+
+class ADParamStmtEx(ADParamStmt):
+ GEOMETRIES = {
+ 'C': [0,1],
+ 'R': [0,1,2],
+ 'O': [0,1,2],
+ 'P': [0,3],
+ }
+
+ @classmethod
+ def from_stmt(cls, stmt):
+ modstr = ','.join([
+ 'X'.join(['{0}'.format(x) for x in modifier])
+ for modifier in stmt.modifiers])
+ return cls(stmt.param, stmt.d, stmt.shape, modstr, stmt.units)
+
+ def __init__(self, param, d, shape, modifiers, units):
+ super(ADParamStmtEx, self).__init__(param, d, shape, modifiers)
+ self.units = units
+
+ def to_inch(self):
+ if self.units == 'inch':
+ return
+ self.units = 'inch'
+ if self.shape in self.GEOMETRIES:
+ indices = self.GEOMETRIES[self.shape]
+ self.modifiers = [tuple([
+ inch(self.modifiers[0][i]) if i in indices else self.modifiers[0][i] \
+ for i in range(len(self.modifiers[0]))
+ ])]
+
+ def to_metric(self):
+ if self.units == 'metric':
+ return
+ self.units = 'metric'
+ if self.shape in self.GEOMETRIES:
+ indices = self.GEOMETRIES[self.shape]
+ self.modifiers = [tuple([
+ metric(self.modifiers[0][i]) if i in indices else self.modifiers[0][i] \
+ for i in range(len(self.modifiers[0]))
+ ])]
diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py
index 4eb317d..13d3421 100644
--- a/gerberex/rs274x.py
+++ b/gerberex/rs274x.py
@@ -5,7 +5,7 @@
import gerber.rs274x
from gerber.gerber_statements import ADParamStmt, CoordStmt
-from gerberex.statements import AMParamStmt, AMParamStmtEx
+from gerberex.gerber_statements import AMParamStmt, AMParamStmtEx, ADParamStmtEx
from gerberex.utility import rotate
class GerberFile(gerber.rs274x.GerberFile):
@@ -17,6 +17,8 @@ class GerberFile(gerber.rs274x.GerberFile):
def swap_statement(statement):
if isinstance(statement, AMParamStmt) and not isinstance(statement, AMParamStmtEx):
return AMParamStmtEx.from_stmt(statement)
+ elif isinstance(statement, ADParamStmt) and not isinstance(statement, AMParamStmtEx):
+ return ADParamStmtEx.from_stmt(statement)
else:
return statement
statements = [swap_statement(statement) for statement in gerber_file.statements]
@@ -26,6 +28,18 @@ class GerberFile(gerber.rs274x.GerberFile):
def __init__(self, statements, settings, primitives, apertures, filename=None):
super(GerberFile, self).__init__(statements, settings, primitives, apertures, filename)
+ def offset(self, x_offset=0, y_offset=0):
+ for statement in self.statements:
+ if isinstance(statement, CoordStmt):
+ if statement.x is not None:
+ statement.x += x_offset
+ if statement.y is not None:
+ statement.y += y_offset
+ else:
+ statement.offset(x_offset, y_offset)
+ for primitive in self.primitives:
+ primitive.offset(x_offset, y_offset)
+
def rotate(self, angle, center=(0,0)):
if angle % 360 == 0:
return
@@ -84,7 +98,7 @@ class GerberFile(gerber.rs274x.GerberFile):
while name in macros:
name = '%s_%d' % (macro_def[0], num)
num += 1
- self.statements.insert(insert_point, macro_def[1](name))
+ self.statements.insert(insert_point, macro_def[1](name, self.units))
macro_defs[idx] = (name, macro_def[1])
for idx in range(insert_point, last_aperture + len(macro_defs) + 1):
statement = self.statements[idx]
@@ -92,10 +106,10 @@ class GerberFile(gerber.rs274x.GerberFile):
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
+ x = statement.modifiers[0][0] \
+ if len(statement.modifiers[0]) > 0 else 0
+ y = statement.modifiers[0][1] \
+ if len(statement.modifiers[0]) > 1 else 0
statement.shape = macro_defs[LANDSCAPE_OBROUND][0] \
if x > y else macro_defs[PORTRATE_OBROUND][0]
elif statement.shape == 'P':
diff --git a/gerberex/statements.py b/gerberex/statements.py
deleted file mode 100644
index c41acb9..0000000
--- a/gerberex/statements.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
-
-from gerber.gerber_statements import AMParamStmt
-from gerberex.am_primitive import to_primitive_defs
-
-class AMParamStmtEx(AMParamStmt):
- @classmethod
- 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 = list(to_primitive_defs(self.instructions))
-
- def to_inch(self):
- if self.units == 'metric':
- self.units = 'inch'
- for p in self.primitive_defs:
- p.to_inch()
-
- def to_metric(self):
- if self.units == 'inch':
- self.units = 'metric'
- for p in self.primitive_defs:
- p.to_metric()
-
- def to_gerber(self, settings = None):
- def plist():
- 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
index f90df96..4c89fa6 100644
--- a/gerberex/utility.py
+++ b/gerberex/utility.py
@@ -13,8 +13,7 @@ def rotate(x, y, angle, center):
sin(angle) * x0 + cos(angle) * y0 + center[1])
def is_equal_value(a, b, error_range=0):
- return a - b <= error_range and a - b >= -error_range
-
+ return (a - b) * (a - b) <= error_range * error_range
def is_equal_point(a, b, error_range=0):
return is_equal_value(a[0], b[0], error_range) and \