From ab69ee0172353e64fbe5099a974341e88feaf24b Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Mon, 10 Nov 2014 12:24:09 -0200 Subject: Bunch of small fixes to improve Gerber read/write. --- gerber/gerber_statements.py | 54 ++++++++++++++++---------------- gerber/rs274x.py | 4 +-- gerber/tests/test_excellon_statements.py | 6 ++-- gerber/tests/test_gerber_statements.py | 8 ++--- gerber/tests/test_utils.py | 15 ++++++--- gerber/utils.py | 8 +++-- 6 files changed, 53 insertions(+), 42 deletions(-) diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py index 32d3784..4aaa1d0 100644 --- a/gerber/gerber_statements.py +++ b/gerber/gerber_statements.py @@ -215,8 +215,8 @@ class OFParamStmt(ParamStmt): @classmethod def from_dict(cls, stmt_dict): param = stmt_dict.get('param') - a = float(stmt_dict.get('a')) - b = float(stmt_dict.get('b')) + a = float(stmt_dict.get('a', 0)) + b = float(stmt_dict.get('b', 0)) return cls(param, a, b) def __init__(self, param, a, b): @@ -245,17 +245,17 @@ class OFParamStmt(ParamStmt): def to_gerber(self): ret = '%OF' - if self.a: - ret += 'A' + decimal_string(self.a, precision=6) - if self.b: - ret += 'B' + decimal_string(self.b, precision=6) + if self.a is not None: + ret += 'A' + decimal_string(self.a, precision=5) + if self.b is not None: + ret += 'B' + decimal_string(self.b, precision=5) return ret + '*%' def __str__(self): offset_str = '' - if self.a: + if self.a is not None: offset_str += ('X: %f' % self.a) - if self.b: + if self.b is not None: offset_str += ('Y: %f' % self.b) return ('' % offset_str) @@ -341,7 +341,7 @@ class ADParamStmt(ParamStmt): else: self.modifiers = [] - def to_gerber(self, settings): + def to_gerber(self): return '%ADD{0}{1},{2}*%'.format(self.d, self.shape, ','.join(['X'.join(e) for e in self.modifiers])) @@ -540,18 +540,14 @@ class CoordStmt(Statement): ret = '' if self.function: ret += self.function - if self.x: - ret += 'X{0}'.format(write_gerber_value(self.x, self.zeros, - self.format)) - if self.y: - ret += 'Y{0}'.format(write_gerber_value(self.y, self. zeros, - self.format)) - if self.i: - ret += 'I{0}'.format(write_gerber_value(self.i, self.zeros, - self.format)) - if self.j: - ret += 'J{0}'.format(write_gerber_value(self.j, self.zeros, - self.format)) + if self.x is not None: + ret += 'X{0}'.format(write_gerber_value(self.x, self.format, self.zero_suppression)) + if self.y is not None: + ret += 'Y{0}'.format(write_gerber_value(self.y, self.format, self.zero_suppression)) + if self.i is not None: + ret += 'I{0}'.format(write_gerber_value(self.i, self.format, self.zero_suppression)) + if self.j is not None: + ret += 'J{0}'.format(write_gerber_value(self.j, self.format, self.zero_suppression)) if self.op: ret += self.op return ret + '*' @@ -560,13 +556,13 @@ class CoordStmt(Statement): coord_str = '' if self.function: coord_str += 'Fn: %s ' % self.function - if self.x: + if self.x is not None: coord_str += 'X: %f ' % self.x - if self.y: + if self.y is not None: coord_str += 'Y: %f ' % self.y - if self.i: + if self.i is not None: coord_str += 'I: %f ' % self.i - if self.j: + if self.j is not None: coord_str += 'J: %f ' % self.j if self.op: if self.op == 'D01': @@ -585,12 +581,16 @@ class CoordStmt(Statement): class ApertureStmt(Statement): """ Aperture Statement """ - def __init__(self, d): + def __init__(self, d, deprecated=None): Statement.__init__(self, "APERTURE") self.d = int(d) + self.deprecated = True if deprecated is not None else False def to_gerber(self): - return 'G54D{0}*'.format(self.d) + if self.deprecated: + return 'G54D{0}*'.format(self.d) + else: + return 'D{0}*'.format(self.d) def __str__(self): return '' % self.d diff --git a/gerber/rs274x.py b/gerber/rs274x.py index f7be44d..a41760e 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -109,7 +109,7 @@ class GerberFile(CamFile): """ with open(filename, 'w') as f: for statement in self.statements: - f.write(statement.to_gerber()) + f.write(statement.to_gerber() + "\n") class GerberParser(object): @@ -149,7 +149,7 @@ class GerberParser(object): r"(I(?P{number}))?(J(?P{number}))?" r"(?P{op})?\*".format(number=NUMBER, function=FUNCTION, op=COORD_OP))) - APERTURE_STMT = re.compile(r"(G54)?D(?P\d+)\*") + APERTURE_STMT = re.compile(r"(?PG54)?D(?P\d+)\*") COMMENT_STMT = re.compile(r"G04(?P[^*]*)(\*)?") diff --git a/gerber/tests/test_excellon_statements.py b/gerber/tests/test_excellon_statements.py index f2e17ee..0e1efa6 100644 --- a/gerber/tests/test_excellon_statements.py +++ b/gerber/tests/test_excellon_statements.py @@ -23,9 +23,9 @@ def test_excellontool_factory(): def test_excellontool_dump(): """ Test ExcellonTool to_excellon() """ - exc_lines = ['T1F00S00C0.01200', 'T2F00S00C0.01500', 'T3F00S00C0.01968', - 'T4F00S00C0.02800', 'T5F00S00C0.03300', 'T6F00S00C0.03800', - 'T7F00S00C0.04300', 'T8F00S00C0.12500', 'T9F00S00C0.13000', ] + exc_lines = ['T1F0S0C0.01200', 'T2F0S0C0.01500', 'T3F0S0C0.01968', + 'T4F0S0C0.02800', 'T5F0S0C0.03300', 'T6F0S0C0.03800', + 'T7F0S0C0.04300', 'T8F0S0C0.12500', 'T9F0S0C0.13000', ] settings = FileSettings(format=(2, 5), zero_suppression='trailing', units='inch', notation='absolute') for line in exc_lines: diff --git a/gerber/tests/test_gerber_statements.py b/gerber/tests/test_gerber_statements.py index a463c9d..62b99b4 100644 --- a/gerber/tests/test_gerber_statements.py +++ b/gerber/tests/test_gerber_statements.py @@ -123,7 +123,7 @@ def test_IPParamStmt_dump(): def test_OFParamStmt_factory(): - """ Test OFParamStmt factory + """ Test OFParamStmt factory """ stmt = {'param': 'OF', 'a': '0.1234567', 'b': '0.1234567'} of = OFParamStmt.from_dict(stmt) @@ -139,13 +139,13 @@ def test_OFParamStmt(): assert_equal(stmt.param, param) assert_equal(stmt.a, val) assert_equal(stmt.b, val) - + def test_OFParamStmt_dump(): """ Test OFParamStmt to_gerber() """ - stmt = {'param': 'OF', 'a': '0.1234567', 'b': '0.1234567'} + stmt = {'param': 'OF', 'a': '0.123456', 'b': '0.123456'} of = OFParamStmt.from_dict(stmt) - assert_equal(of.to_gerber(), '%OFA0.123456B0.123456*%') + assert_equal(of.to_gerber(), '%OFA0.12345B0.12345*%') def test_LPParamStmt_factory(): diff --git a/gerber/tests/test_utils.py b/gerber/tests/test_utils.py index 001a32f..706fa65 100644 --- a/gerber/tests/test_utils.py +++ b/gerber/tests/test_utils.py @@ -19,7 +19,8 @@ def test_zero_suppression(): ('1000', 0.01), ('10000', 0.1), ('100000', 1.0), ('1000000', 10.0), ('-1', -0.00001), ('-10', -0.0001), ('-100', -0.001), ('-1000', -0.01), ('-10000', -0.1), - ('-100000', -1.0), ('-1000000', -10.0), ] + ('-100000', -1.0), ('-1000000', -10.0), + ('0', 0.0)] for string, value in test_cases: assert(value == parse_gerber_value(string, fmt, zero_suppression)) assert(string == write_gerber_value(value, fmt, zero_suppression)) @@ -30,7 +31,8 @@ def test_zero_suppression(): ('00001', 0.001), ('000001', 0.0001), ('0000001', 0.00001), ('-1', -10.0), ('-01', -1.0), ('-001', -0.1), ('-0001', -0.01), ('-00001', -0.001), - ('-000001', -0.0001), ('-0000001', -0.00001)] + ('-000001', -0.0001), ('-0000001', -0.00001), + ('0', 0.0)] for string, value in test_cases: assert(value == parse_gerber_value(string, fmt, zero_suppression)) assert(string == write_gerber_value(value, fmt, zero_suppression)) @@ -46,7 +48,8 @@ def test_format(): ((2, 1), '1', 0.1), ((2, 7), '-1', -0.0000001), ((2, 6), '-1', -0.000001), ((2, 5), '-1', -0.00001), ((2, 4), '-1', -0.0001), ((2, 3), '-1', -0.001), - ((2, 2), '-1', -0.01), ((2, 1), '-1', -0.1), ] + ((2, 2), '-1', -0.01), ((2, 1), '-1', -0.1), + ((2, 6), '0', 0) ] for fmt, string, value in test_cases: assert(value == parse_gerber_value(string, fmt, zero_suppression)) assert(string == write_gerber_value(value, fmt, zero_suppression)) @@ -57,7 +60,8 @@ def test_format(): ((2, 5), '1', 10.0), ((1, 5), '1', 1.0), ((6, 5), '-1', -100000.0), ((5, 5), '-1', -10000.0), ((4, 5), '-1', -1000.0), ((3, 5), '-1', -100.0), - ((2, 5), '-1', -10.0), ((1, 5), '-1', -1.0), ] + ((2, 5), '-1', -10.0), ((1, 5), '-1', -1.0), + ((2, 5), '0', 0)] for fmt, string, value in test_cases: assert(value == parse_gerber_value(string, fmt, zero_suppression)) assert(string == write_gerber_value(value, fmt, zero_suppression)) @@ -81,3 +85,6 @@ def test_decimal_padding(): assert_equal(decimal_string(value, precision=4, padding=True), '1.1230') assert_equal(decimal_string(value, precision=5, padding=True), '1.12300') assert_equal(decimal_string(value, precision=6, padding=True), '1.123000') + + assert_equal(decimal_string(0, precision=6, padding=True), '0.000000') + diff --git a/gerber/utils.py b/gerber/utils.py index 7749e22..56b675f 100644 --- a/gerber/utils.py +++ b/gerber/utils.py @@ -125,9 +125,9 @@ def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'): if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7: raise ValueError('Parser only supports precision up to 6:7 format') - # Edge case... + # Edge case... (per Gerber spec we should return 0 in all cases, see page 77) if value == 0: - return '00' + return '0' # negative sign affects padding, so deal with it at the end... negative = value < 0.0 @@ -173,10 +173,14 @@ def decimal_string(value, precision=6, padding=False): integer, decimal = floatstr.split('.') elif ',' in floatstr: integer, decimal = floatstr.split(',') + else: + integer, decimal = floatstr, "0" + if len(decimal) > precision: decimal = decimal[:precision] elif padding: decimal = decimal + (precision - len(decimal)) * '0' + if integer or decimal: return ''.join([integer, '.', decimal]) else: -- cgit