Bunch of small fixes to improve Gerber read/write.
This commit is contained in:
parent
f5abd5b0bd
commit
ab69ee0172
6 changed files with 53 additions and 42 deletions
|
|
@ -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: %s>' % 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 '<Aperture: %d>' % self.d
|
||||
|
|
|
|||
|
|
@ -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<i>{number}))?(J(?P<j>{number}))?"
|
||||
r"(?P<op>{op})?\*".format(number=NUMBER, function=FUNCTION, op=COORD_OP)))
|
||||
|
||||
APERTURE_STMT = re.compile(r"(G54)?D(?P<d>\d+)\*")
|
||||
APERTURE_STMT = re.compile(r"(?P<deprecated>G54)?D(?P<d>\d+)\*")
|
||||
|
||||
COMMENT_STMT = re.compile(r"G04(?P<comment>[^*]*)(\*)?")
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue