From e3c59e39cf9bc64ce9d76c324b82956a65515f16 Mon Sep 17 00:00:00 2001 From: opiopan Date: Sun, 7 Apr 2019 22:22:33 +0900 Subject: expand test and fix many issues --- gerberex/am_expression.py | 4 +- gerberex/am_primitive.py | 16 +++--- gerberex/composition.py | 4 +- gerberex/excellon.py | 17 ++++++- gerberex/gerber_statements.py | 115 ++++++++++++++++++++++++++++++++++++++++++ gerberex/rs274x.py | 26 +++++++--- gerberex/statements.py | 70 ------------------------- gerberex/utility.py | 3 +- 8 files changed, 163 insertions(+), 92 deletions(-) create mode 100644 gerberex/gerber_statements.py delete mode 100644 gerberex/statements.py (limited to 'gerberex') 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 + +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 - -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 \ -- cgit