From 9febca7da6a730b3b3ca3a54129a9f88e5c44d14 Mon Sep 17 00:00:00 2001 From: opiopan Date: Thu, 21 Mar 2019 22:00:32 +0900 Subject: initial commit --- gerberex/rs274x.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 gerberex/rs274x.py (limited to 'gerberex/rs274x.py') diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py new file mode 100644 index 0000000..e9d82cd --- /dev/null +++ b/gerberex/rs274x.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2019 Hiroshi Murayama + +import gerber.rs274x +from gerberex.statements import (AMParamStmt, AMParamStmtEx) + +class GerberFile(gerber.rs274x.GerberFile): + @classmethod + def from_gerber_file(cls, gerber_file): + if not isinstance(gerber_file, gerber.rs274x.GerberFile): + raise Exception('only gerber.rs274x.GerberFile object is specified') + + def swap_statement(statement): + if isinstance(statement, AMParamStmt) and not isinstance(statement, AMParamStmtEx): + return AMParamStmtEx.from_stmt(statement) + else: + return statement + statements = [swap_statement(statement) for statement in gerber_file.statements] + return cls(statements, gerber_file.settings, gerber_file.primitives,\ + gerber_file.apertures, gerber_file.filename) + + def __init__(self, statements, settings, primitives, apertures, filename=None): + super(GerberFile, self).__init__(statements, settings, primitives, apertures, filename) -- cgit From 690df56bb71020901167605a87ec451081fa18d7 Mon Sep 17 00:00:00 2001 From: opiopan Date: Sat, 23 Mar 2019 21:59:13 +0900 Subject: add rotation fuction --- gerberex/rs274x.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) (limited to 'gerberex/rs274x.py') 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 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] -- cgit From cb420e39e278f7ab6f002600a7698d7be101eb7d Mon Sep 17 00:00:00 2001 From: opiopan Date: Wed, 3 Apr 2019 00:30:00 +0900 Subject: fix a rotaion issue --- gerberex/rs274x.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'gerberex/rs274x.py') diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py index 4b477d3..4eb317d 100644 --- a/gerberex/rs274x.py +++ b/gerberex/rs274x.py @@ -30,11 +30,25 @@ class GerberFile(gerber.rs274x.GerberFile): if angle % 360 == 0: return self._generalize_aperture() + last_x = 0 + last_y = 0 + last_rx = 0 + last_ry = 0 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) + if statement.i != None and statement.j != None: + cx = last_x + statement.i + cy = last_y + statement.j + cx, cy = rotate(cx, cy, angle, center) + statement.i = cx - last_rx + statement.j = cy - last_ry + last_x = statement.x + last_y = statement.y + last_rx, last_ry = rotate(statement.x, statement.y, angle, center) + statement.x = last_rx + statement.y = last_ry def _generalize_aperture(self): RECTANGLE = 0 -- cgit 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/rs274x.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'gerberex/rs274x.py') 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': -- cgit From 2b1c751ff76ebd6901633235ee694cc93dabce81 Mon Sep 17 00:00:00 2001 From: Hiroshi Murayama Date: Mon, 9 Sep 2019 09:07:38 +0900 Subject: improve compatibility with RS-274x specification: - can merge multiple files having different file scope modifier, such as AS, MI, OF, SF, and IR - support modal coordinate notation --- gerberex/rs274x.py | 241 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 210 insertions(+), 31 deletions(-) (limited to 'gerberex/rs274x.py') diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py index 13d3421..fae0b32 100644 --- a/gerberex/rs274x.py +++ b/gerberex/rs274x.py @@ -3,10 +3,29 @@ # Copyright 2019 Hiroshi Murayama +from gerber.cam import FileSettings import gerber.rs274x -from gerber.gerber_statements import ADParamStmt, CoordStmt +from gerber.gerber_statements import * from gerberex.gerber_statements import AMParamStmt, AMParamStmtEx, ADParamStmtEx from gerberex.utility import rotate +import re + +def loads(data, filename=None): + cls = gerber.rs274x.GerberParser + cls.SF = \ + r"(?PSF)(A(?P{decimal}))?(B(?P{decimal}))?".format(decimal=cls.DECIMAL) + cls.PARAMS = (cls.FS, cls.MO, cls.LP, cls.AD_CIRCLE, + cls.AD_RECT, cls.AD_OBROUND, cls.AD_POLY, + cls.AD_MACRO, cls.AM, cls.AS, cls.IF, cls.IN, + cls.IP, cls.IR, cls.MI, cls.OF, cls.SF, cls.LN) + cls.PARAM_STMT = [re.compile(r"%?{0}\*%?".format(p)) for p in cls.PARAMS] + return cls().parse_raw(data, filename) + +def write_gerber_header(file, settings): + file.write('%s\n%s\n%%IPPOS*%%\n' % ( + MOParamStmt('MO', settings.units).to_gerber(settings), + FSParamStmt('FS', settings.zero_suppression, + settings.notation, settings.format).to_gerber(settings))) class GerberFile(gerber.rs274x.GerberFile): @classmethod @@ -14,29 +33,74 @@ class GerberFile(gerber.rs274x.GerberFile): if not isinstance(gerber_file, gerber.rs274x.GerberFile): raise Exception('only gerber.rs274x.GerberFile object is specified') - 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] - return cls(statements, gerber_file.settings, gerber_file.primitives,\ + return cls(gerber_file.statements, gerber_file.settings, gerber_file.primitives,\ gerber_file.apertures, gerber_file.filename) def __init__(self, statements, settings, primitives, apertures, filename=None): super(GerberFile, self).__init__(statements, settings, primitives, apertures, filename) + self.context = GerberContext.from_settings(self.settings) + self.aperture_macros = {} + self.aperture_defs = [] + self.main_statements = [] + for stmt in self.statements: + type, stmts = self.context.normalize_statement(stmt) + if type == self.context.TYPE_AM: + for mdef in stmts: + self.aperture_macros[mdef.name] = mdef + elif type == self.context.TYPE_AD: + self.aperture_defs.extend(stmts) + elif type == self.context.TYPE_MAIN: + self.main_statements.extend(stmts) + if self.context.angle != 0: + self.rotate(self.context.angle) + self.context.notation = 'absolute' + self.context.zeros = 'trailing' + + def write(self, filename=None): + self.context.notation = 'absolute' + self.context.zeros = 'trailing' + self.context.format = self.format + self.units = self.units + filename=filename if filename is not None else self.filename + with open(filename, 'w') as f: + write_gerber_header(f, self.context) + for macro in self.aperture_macros: + f.write(self.aperture_macros[macro].to_gerber(self.context) + '\n') + for aperture in self.aperture_defs: + f.write(aperture.to_gerber(self.context) + '\n') + for statement in self.main_statements: + f.write(statement.to_gerber(self.context) + '\n') + f.write('M02*\n') + + def to_inch(self): + if self.units == 'metric': + for macro in self.aperture_macros: + self.aperture_macros[macro].to_inch() + for aperture in self.aperture_defs: + aperture.to_inch() + for statement in self.statements: + statement.to_inch() + self.units = 'inch' + self.context.units = 'inch' + + def to_metric(self): + if self.units == 'inch': + for macro in self.aperture_macros: + self.aperture_macros[macro].to_metric() + for aperture in self.aperture_defs: + aperture.to_metric() + for statement in self.statements: + statement.to_metric() + self.units='metric' + self.context.units='metric' def offset(self, x_offset=0, y_offset=0): - for statement in self.statements: + for statement in self.main_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) @@ -48,10 +112,10 @@ class GerberFile(gerber.rs274x.GerberFile): last_y = 0 last_rx = 0 last_ry = 0 - 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: + for name in self.aperture_macros: + self.aperture_macros[name].rotate(angle, center) + for statement in self.main_statements: + if isinstance(statement, CoordStmt) and statement.x != None and statement.y != None: if statement.i != None and statement.j != None: cx = last_x + statement.i cy = last_y + statement.j @@ -77,31 +141,21 @@ class GerberFile(gerber.rs274x.GerberFile): ] 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 + for statement in self.aperture_defs: 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: + while name in self.aperture_macros: name = '%s_%d' % (macro_def[0], num) num += 1 - self.statements.insert(insert_point, macro_def[1](name, self.units)) + self.aperture_macros[name] = 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] + for statement in self.aperture_defs: if isinstance(statement, ADParamStmt): if statement.shape == 'R': statement.shape = macro_defs[RECTANGLE][0] @@ -114,3 +168,128 @@ class GerberFile(gerber.rs274x.GerberFile): if x > y else macro_defs[PORTRATE_OBROUND][0] elif statement.shape == 'P': statement.shape = macro_defs[POLYGON][0] + +class GerberContext(FileSettings): + TYPE_NONE = 'none' + TYPE_AM = 'am' + TYPE_AD = 'ad' + TYPE_MAIN = 'main' + IP_LINEAR = 'lenear' + IP_ARC = 'arc' + DIR_CLOCKWISE = 'cw' + DIR_COUNTERCLOCKWISE = 'ccw' + + ignored_stmt = ('FSParamStmt', 'MOParamStmt', 'ASParamStmt', + 'INParamStmt', 'IPParamStmt', 'IRParamStmt', + 'MIParamStmt', 'OFParamStmt', 'SFParamStmt', + 'LNParamStmt', 'CommentStmt', 'EofStmt',) + + @classmethod + def from_settings(cls, settings): + return cls(settings.notation, settings.units, settings.zero_suppression, + settings.format, settings.zeros, settings.angle_units) + + def __init__(self, notation='absolute', units='inch', + zero_suppression=None, format=(2, 5), zeros=None, + angle_units='degrees', + name=None, + mirror=(False, False), offset=(0., 0.), scale=(1., 1.), + angle=0., axis='xy'): + super(GerberContext, self).__init__(notation, units, zero_suppression, + format, zeros, angle_units) + self.name = name + self.mirror = mirror + self.offset = offset + self.scale = scale + self.angle = angle + self.axis = axis + + self.matrix = (1, 0, + 1, 0, + 1, 1) + + self.op = None + self.interpolation = self.IP_LINEAR + self.direction = self.DIR_CLOCKWISE + self.x = 0. + self.y = 0. + + def normalize_statement(self, stmt): + if isinstance(stmt, INParamStmt): + self.name = stmt.name + elif isinstance(stmt, MIParamStmt): + self.mirror = (stmt.a, stmt.b) + self._update_matrix() + elif isinstance(stmt, OFParamStmt): + self.offset = (stmt.a, stmt.b) + self._update_matrix() + elif isinstance(stmt, SFParamStmt): + self.scale = (stmt.a, stmt.b) + self._update_matrix() + elif isinstance(stmt, ASParamStmt): + self.axis = 'yx' if stmt.mode == 'AYBX' else 'xy' + self._update_matrix() + elif isinstance(stmt, IRParamStmt): + self.angle = stmt.angle + elif isinstance(stmt, AMParamStmt) and not isinstance(stmt, AMParamStmtEx): + stmt = AMParamStmtEx.from_stmt(stmt) + return (self.TYPE_AM, [stmt]) + elif isinstance(stmt, ADParamStmt) and not isinstance(stmt, AMParamStmtEx): + stmt = ADParamStmtEx.from_stmt(stmt) + return (self.TYPE_AD, [stmt]) + elif isinstance(stmt, CoordStmt): + self._normalize_coordinate(stmt) + + if type(stmt).__name__ in self.ignored_stmt: + return (self.TYPE_NONE, None) + else: + return (self.TYPE_MAIN, [stmt]) + + def _update_matrix(self): + if self.axis == 'xy': + mx = -1 if self.mirror[0] else 1 + my = -1 if self.mirror[1] else 1 + self.matrix = ( + self.scale[0] * mx, self.offset[0], + self.scale[1] * my, self.offset[1], + self.scale[0] * mx, self.scale[1] * my) + else: + mx = -1 if self.mirror[1] else 1 + my = -1 if self.mirror[0] else 1 + self.matrix = ( + self.scale[1] * mx, self.offset[1], + self.scale[0] * my, self.offset[0], + self.scale[1] * mx, self.scale[0] * my) + + def _normalize_coordinate(self, stmt): + if stmt.only_function: + if stmt.function == 'G01' or stmt.function == 'G1': + self.interpolation = self.IP_LINEAR + elif stmt.function == 'G02' or stmt.function == 'G2': + self.interpolation = self.IP_ARC + self.direction = self.DIR_CLOCKWISE + if self.mirror[0] != self.mirror[1]: + self.direction = self.DIR_COUNTERCLOCKWISE + stmt.function = 'G03' + elif stmt.function == 'G03' or stmt.function == 'G3': + self.interpolation = self.IP_ARC + self.direction = self.DIR_COUNTERCLOCKWISE + if self.mirror[0] != self.mirror[1]: + self.direction = self.DIR_CLOCKWISE + stmt.function = 'G02' + return + if self.notation == 'absolute': + x = stmt.x if stmt.x is not None else self.x + y = stmt.y if stmt.y is not None else self.y + else: + x = self.x + stmt.x if stmt.x is not None else 0 + y = self.y + stmt.y if stmt.y is not None else 0 + self.x, self.y = x, y + self.op = stmt.op if stmt.op is not None else self.op + + stmt.op = self.op + stmt.x = self.matrix[0] * x + self.matrix[1] + stmt.y = self.matrix[2] * y + self.matrix[3] + if stmt.op == 'D01' and self.interpolation == self.IP_ARC: + stmt.i = self.matrix[4] * stmt.i if stmt.i is not None else 0 + stmt.j = self.matrix[5] * stmt.j if stmt.j is not None else 0 -- cgit From 4c4ba0762b30fdd4633a6d2868c508184d681b7d Mon Sep 17 00:00:00 2001 From: Hiroshi Murayama Date: Mon, 9 Sep 2019 21:52:52 +0900 Subject: fix issue #2: single quadrant mode is supported --- gerberex/rs274x.py | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'gerberex/rs274x.py') diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py index fae0b32..42ae17d 100644 --- a/gerberex/rs274x.py +++ b/gerberex/rs274x.py @@ -208,6 +208,7 @@ class GerberContext(FileSettings): 1, 0, 1, 1) + self.in_single_quadrant_mode = False self.op = None self.interpolation = self.IP_LINEAR self.direction = self.DIR_CLOCKWISE @@ -237,6 +238,9 @@ class GerberContext(FileSettings): elif isinstance(stmt, ADParamStmt) and not isinstance(stmt, AMParamStmtEx): stmt = ADParamStmtEx.from_stmt(stmt) return (self.TYPE_AD, [stmt]) + elif isinstance(stmt, QuadrantModeStmt): + self.in_single_quadrant_mode = stmt.mode == 'single-quadrant' + stmt.mode = 'multi-quadrant' elif isinstance(stmt, CoordStmt): self._normalize_coordinate(stmt) @@ -262,22 +266,23 @@ class GerberContext(FileSettings): self.scale[1] * mx, self.scale[0] * my) def _normalize_coordinate(self, stmt): + if stmt.function == 'G01' or stmt.function == 'G1': + self.interpolation = self.IP_LINEAR + elif stmt.function == 'G02' or stmt.function == 'G2': + self.interpolation = self.IP_ARC + self.direction = self.DIR_CLOCKWISE + if self.mirror[0] != self.mirror[1]: + stmt.function = 'G03' + elif stmt.function == 'G03' or stmt.function == 'G3': + self.interpolation = self.IP_ARC + self.direction = self.DIR_COUNTERCLOCKWISE + if self.mirror[0] != self.mirror[1]: + stmt.function = 'G02' if stmt.only_function: - if stmt.function == 'G01' or stmt.function == 'G1': - self.interpolation = self.IP_LINEAR - elif stmt.function == 'G02' or stmt.function == 'G2': - self.interpolation = self.IP_ARC - self.direction = self.DIR_CLOCKWISE - if self.mirror[0] != self.mirror[1]: - self.direction = self.DIR_COUNTERCLOCKWISE - stmt.function = 'G03' - elif stmt.function == 'G03' or stmt.function == 'G3': - self.interpolation = self.IP_ARC - self.direction = self.DIR_COUNTERCLOCKWISE - if self.mirror[0] != self.mirror[1]: - self.direction = self.DIR_CLOCKWISE - stmt.function = 'G02' return + + last_x = self.x + last_y = self.y if self.notation == 'absolute': x = stmt.x if stmt.x is not None else self.x y = stmt.y if stmt.y is not None else self.y @@ -291,5 +296,15 @@ class GerberContext(FileSettings): stmt.x = self.matrix[0] * x + self.matrix[1] stmt.y = self.matrix[2] * y + self.matrix[3] if stmt.op == 'D01' and self.interpolation == self.IP_ARC: - stmt.i = self.matrix[4] * stmt.i if stmt.i is not None else 0 - stmt.j = self.matrix[5] * stmt.j if stmt.j is not None else 0 + qx, qy = 1, 1 + if self.in_single_quadrant_mode: + if self.direction == self.DIR_CLOCKWISE: + qx = 1 if y > last_y else -1 + qy = 1 if x < last_x else -1 + else: + qx = 1 if y < last_y else -1 + qy = 1 if x > last_x else -1 + if last_x == x and last_y == y: + qx, qy = 0, 0 + stmt.i = qx * self.matrix[4] * stmt.i if stmt.i is not None else 0 + stmt.j = qy * self.matrix[5] * stmt.j if stmt.j is not None else 0 -- cgit From 00351ebe277aeb90e7463d1b0bd55402249c4687 Mon Sep 17 00:00:00 2001 From: Hiroshi Murayama Date: Thu, 12 Sep 2019 23:44:50 +0900 Subject: add IP command handling function --- gerberex/rs274x.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'gerberex/rs274x.py') diff --git a/gerberex/rs274x.py b/gerberex/rs274x.py index 42ae17d..3a3a712 100644 --- a/gerberex/rs274x.py +++ b/gerberex/rs274x.py @@ -53,6 +53,8 @@ class GerberFile(gerber.rs274x.GerberFile): self.main_statements.extend(stmts) if self.context.angle != 0: self.rotate(self.context.angle) + if self.context.is_negative: + self.nagate_polarity() self.context.notation = 'absolute' self.context.zeros = 'trailing' @@ -128,6 +130,11 @@ class GerberFile(gerber.rs274x.GerberFile): statement.x = last_rx statement.y = last_ry + def nagate_polarity(self): + for statement in self.main_statements: + if isinstance(statement, LPParamStmt): + statement.lp = 'dark' if statement.lp == 'clear' else 'clear' + def _generalize_aperture(self): RECTANGLE = 0 LANDSCAPE_OBROUND = 1 @@ -208,6 +215,9 @@ class GerberContext(FileSettings): 1, 0, 1, 1) + self.is_negative = False + self.is_first_coordinate = True + self.no_polarity = True self.in_single_quadrant_mode = False self.op = None self.interpolation = self.IP_LINEAR @@ -216,6 +226,7 @@ class GerberContext(FileSettings): self.y = 0. def normalize_statement(self, stmt): + additional_stmts = None if isinstance(stmt, INParamStmt): self.name = stmt.name elif isinstance(stmt, MIParamStmt): @@ -241,14 +252,24 @@ class GerberContext(FileSettings): elif isinstance(stmt, QuadrantModeStmt): self.in_single_quadrant_mode = stmt.mode == 'single-quadrant' stmt.mode = 'multi-quadrant' + elif isinstance(stmt, IPParamStmt): + self.is_negative = stmt.ip == 'negative' + elif isinstance(stmt, LPParamStmt): + self.no_polarity = False elif isinstance(stmt, CoordStmt): self._normalize_coordinate(stmt) + if self.is_first_coordinate: + self.is_first_coordinate = False + if self.no_polarity: + additional_stmts = [LPParamStmt('LP', 'dark'), stmt] if type(stmt).__name__ in self.ignored_stmt: return (self.TYPE_NONE, None) + elif additional_stmts is not None: + return (self.TYPE_MAIN, additional_stmts) else: return (self.TYPE_MAIN, [stmt]) - + def _update_matrix(self): if self.axis == 'xy': mx = -1 if self.mirror[0] else 1 -- cgit