summaryrefslogtreecommitdiff
path: root/gerber/excellon_statements.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerber/excellon_statements.py')
-rw-r--r--gerber/excellon_statements.py703
1 files changed, 625 insertions, 78 deletions
diff --git a/gerber/excellon_statements.py b/gerber/excellon_statements.py
index feeda44..ac9c528 100644
--- a/gerber/excellon_statements.py
+++ b/gerber/excellon_statements.py
@@ -21,27 +21,54 @@ Excellon Statements
"""
-from .utils import parse_gerber_value, write_gerber_value, decimal_string
import re
+import uuid
+from .utils import (parse_gerber_value, write_gerber_value, decimal_string,
+ inch, metric)
+
__all__ = ['ExcellonTool', 'ToolSelectionStmt', 'CoordinateStmt',
'CommentStmt', 'HeaderBeginStmt', 'HeaderEndStmt',
'RewindStopStmt', 'EndOfProgramStmt', 'UnitStmt',
'IncrementalModeStmt', 'VersionStmt', 'FormatStmt', 'LinkToolStmt',
- 'MeasuringModeStmt', 'UnknownStmt',
- ]
+ 'MeasuringModeStmt', 'RouteModeStmt', 'LinearModeStmt', 'DrillModeStmt',
+ 'AbsoluteModeStmt', 'RepeatHoleStmt', 'UnknownStmt',
+ 'ExcellonStatement', 'ZAxisRoutPositionStmt',
+ 'RetractWithClampingStmt', 'RetractWithoutClampingStmt',
+ 'CutterCompensationOffStmt', 'CutterCompensationLeftStmt',
+ 'CutterCompensationRightStmt', 'ZAxisInfeedRateStmt',
+ 'NextToolSelectionStmt', 'SlotStmt']
class ExcellonStatement(object):
""" Excellon Statement abstract base class
"""
+
@classmethod
def from_excellon(cls, line):
- pass
+ raise NotImplementedError('from_excellon must be implemented in a '
+ 'subclass')
+
+ def __init__(self, unit='inch', id=None):
+ self.units = unit
+ self.id = uuid.uuid4().int if id is None else id
+
+ def to_excellon(self, settings=None):
+ raise NotImplementedError('to_excellon must be implemented in a '
+ 'subclass')
- def to_excellon(self):
+ def to_inch(self):
+ self.units = 'inch'
+
+ def to_metric(self):
+ self.units = 'metric'
+
+ def offset(self, x_offset=0, y_offset=0):
pass
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
+
class ExcellonTool(ExcellonStatement):
""" Excellon Tool class
@@ -86,9 +113,29 @@ class ExcellonTool(ExcellonStatement):
hit_count : integer
Number of tool hits in excellon file.
"""
+
+ PLATED_UNKNOWN = None
+ PLATED_YES = 'plated'
+ PLATED_NO = 'nonplated'
+ PLATED_OPTIONAL = 'optional'
+
+ @classmethod
+ def from_tool(cls, tool):
+ args = {}
+
+ args['depth_offset'] = tool.depth_offset
+ args['diameter'] = tool.diameter
+ args['feed_rate'] = tool.feed_rate
+ args['max_hit_count'] = tool.max_hit_count
+ args['number'] = tool.number
+ args['plated'] = tool.plated
+ args['retract_rate'] = tool.retract_rate
+ args['rpm'] = tool.rpm
+
+ return cls(None, **args)
@classmethod
- def from_excellon(cls, line, settings):
+ def from_excellon(cls, line, settings, id=None, plated=None):
""" Create a Tool from an excellon file tool definition line.
Parameters
@@ -107,6 +154,7 @@ class ExcellonTool(ExcellonStatement):
commands = re.split('([BCFHSTZ])', line)[1:]
commands = [(command, value) for command, value in pairwise(commands)]
args = {}
+ args['id'] = id
nformat = settings.format
zero_suppression = settings.zero_suppression
for cmd, val in commands:
@@ -124,6 +172,10 @@ class ExcellonTool(ExcellonStatement):
args['number'] = int(val)
elif cmd == 'Z':
args['depth_offset'] = parse_gerber_value(val, nformat, zero_suppression)
+
+ if plated != ExcellonTool.PLATED_UNKNOWN:
+ # Sometimees we can can parse the
+ args['plated'] = plated
return cls(settings, **args)
@classmethod
@@ -143,9 +195,11 @@ class ExcellonTool(ExcellonStatement):
tool : ExcellonTool
An ExcellonTool initialized with the parameters in tool_dict.
"""
- return cls(settings, tool_dict)
+ return cls(settings, **tool_dict)
def __init__(self, settings, **kwargs):
+ if kwargs.get('id') is not None:
+ super(ExcellonTool, self).__init__(id=kwargs.get('id'))
self.settings = settings
self.number = kwargs.get('number')
self.feed_rate = kwargs.get('feed_rate')
@@ -154,12 +208,16 @@ class ExcellonTool(ExcellonStatement):
self.diameter = kwargs.get('diameter')
self.max_hit_count = kwargs.get('max_hit_count')
self.depth_offset = kwargs.get('depth_offset')
+ self.plated = kwargs.get('plated')
+
self.hit_count = 0
- def to_excellon(self):
- fmt = self.settings.format
- zs = self.settings.format
- stmt = 'T%d' % self.number
+ def to_excellon(self, settings=None):
+ if self.settings and not settings:
+ settings = self.settings
+ fmt = settings.format
+ zs = settings.zero_suppression
+ stmt = 'T%02d' % self.number
if self.retract_rate is not None:
stmt += 'B%s' % write_gerber_value(self.retract_rate, fmt, zs)
if self.feed_rate is not None:
@@ -170,25 +228,55 @@ class ExcellonTool(ExcellonStatement):
if self.rpm < 100000.:
stmt += 'S%s' % write_gerber_value(self.rpm / 1000., fmt, zs)
else:
- stmt += 'S%g' % self.rpm / 1000.
+ stmt += 'S%g' % (self.rpm / 1000.)
if self.diameter is not None:
stmt += 'C%s' % decimal_string(self.diameter, fmt[1], True)
if self.depth_offset is not None:
stmt += 'Z%s' % write_gerber_value(self.depth_offset, fmt, zs)
return stmt
+ def to_inch(self):
+ if self.settings.units != 'inch':
+ self.settings.units = 'inch'
+ if self.diameter is not None:
+ self.diameter = inch(self.diameter)
+
+ def to_metric(self):
+ if self.settings.units != 'metric':
+ self.settings.units = 'metric'
+ if self.diameter is not None:
+ self.diameter = metric(self.diameter)
+
def _hit(self):
self.hit_count += 1
+
+ def equivalent(self, other):
+ """
+ Is the other tool equal to this, ignoring the tool number, and other file specified properties
+ """
+
+ if type(self) != type(other):
+ return False
+
+ return (self.diameter == other.diameter
+ and self.feed_rate == other.feed_rate
+ and self.retract_rate == other.retract_rate
+ and self.rpm == other.rpm
+ and self.depth_offset == other.depth_offset
+ and self.max_hit_count == other.max_hit_count
+ and self.plated == other.plated
+ and self.settings.units == other.settings.units)
def __repr__(self):
unit = 'in.' if self.settings.units == 'inch' else 'mm'
- return '<ExcellonTool %d: %0.3f%s dia.>' % (self.number, self.diameter, unit)
+ fmtstr = '<ExcellonTool %%02d: %%%d.%dg%%s dia.>' % self.settings.format
+ return fmtstr % (self.number, self.diameter, unit)
class ToolSelectionStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
""" Create a ToolSelectionStmt from an excellon file line.
Parameters
@@ -203,103 +291,335 @@ class ToolSelectionStmt(ExcellonStatement):
"""
line = line[1:]
compensation_index = None
- tool = int(line[:2])
- if len(line) > 2:
+
+ # up to 3 characters for tool number (Frizting uses that)
+ if len(line) <= 3:
+ tool = int(line)
+ else:
+ tool = int(line[:2])
compensation_index = int(line[2:])
- return cls(tool, compensation_index)
- def __init__(self, tool, compensation_index=None):
+ return cls(tool, compensation_index, **kwargs)
+
+ def __init__(self, tool, compensation_index=None, **kwargs):
+ super(ToolSelectionStmt, self).__init__(**kwargs)
tool = int(tool)
compensation_index = (int(compensation_index) if compensation_index
is not None else None)
self.tool = tool
self.compensation_index = compensation_index
- def to_excellon(self):
+ def to_excellon(self, settings=None):
stmt = 'T%02d' % self.tool
if self.compensation_index is not None:
stmt += '%02d' % self.compensation_index
return stmt
+
+class NextToolSelectionStmt(ExcellonStatement):
+
+ # TODO the statement exists outside of the context of the file,
+ # so it is imposible to know that it is really the next tool
+
+ def __init__(self, cur_tool, next_tool, **kwargs):
+ """
+ Select the next tool in the wheel.
+ Parameters
+ ----------
+ cur_tool : the tool that is currently selected
+ next_tool : the that that is now selected
+ """
+ super(NextToolSelectionStmt, self).__init__(**kwargs)
+
+ self.cur_tool = cur_tool
+ self.next_tool = next_tool
+
+ def to_excellon(self, settings=None):
+ stmt = 'M00'
+ return stmt
+
+class ZAxisInfeedRateStmt(ExcellonStatement):
+
+ @classmethod
+ def from_excellon(cls, line, **kwargs):
+ """ Create a ZAxisInfeedRate from an excellon file line.
+
+ Parameters
+ ----------
+ line : string
+ Line from an Excellon file
+
+ Returns
+ -------
+ z_axis_infeed_rate : ToolSelectionStmt
+ ToolSelectionStmt representation of `line.`
+ """
+ rate = int(line[1:])
+
+ return cls(rate, **kwargs)
+
+ def __init__(self, rate, **kwargs):
+ super(ZAxisInfeedRateStmt, self).__init__(**kwargs)
+ self.rate = rate
+
+ def to_excellon(self, settings=None):
+ return 'F%02d' % self.rate
class CoordinateStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line, nformat=(2, 5), zero_suppression='trailing'):
+ def from_point(cls, point, mode=None):
+
+ stmt = cls(point[0], point[1])
+ if mode:
+ stmt.mode = mode
+ return stmt
+
+ @classmethod
+ def from_excellon(cls, line, settings, **kwargs):
x_coord = None
y_coord = None
if line[0] == 'X':
splitline = line.strip('X').split('Y')
- x_coord = parse_gerber_value(splitline[0], nformat,
- zero_suppression)
+ x_coord = parse_gerber_value(splitline[0], settings.format,
+ settings.zero_suppression)
if len(splitline) == 2:
- y_coord = parse_gerber_value(splitline[1], nformat,
- zero_suppression)
+ y_coord = parse_gerber_value(splitline[1], settings.format,
+ settings.zero_suppression)
else:
- y_coord = parse_gerber_value(line.strip(' Y'), nformat,
- zero_suppression)
- return cls(x_coord, y_coord)
-
- def __init__(self, x=None, y=None):
+ y_coord = parse_gerber_value(line.strip(' Y'), settings.format,
+ settings.zero_suppression)
+ c = cls(x_coord, y_coord, **kwargs)
+ c.units = settings.units
+ return c
+
+ def __init__(self, x=None, y=None, **kwargs):
+ super(CoordinateStmt, self).__init__(**kwargs)
self.x = x
self.y = y
+ self.mode = None
- def to_excellon(self):
+ def to_excellon(self, settings):
stmt = ''
+ if self.mode == "ROUT":
+ stmt += "G00"
+ if self.mode == "LINEAR":
+ stmt += "G01"
if self.x is not None:
- stmt += 'X%s' % write_gerber_value(self.x)
+ stmt += 'X%s' % write_gerber_value(self.x, settings.format,
+ settings.zero_suppression)
if self.y is not None:
- stmt += 'Y%s' % write_gerber_value(self.y)
+ stmt += 'Y%s' % write_gerber_value(self.y, settings.format,
+ settings.zero_suppression)
return stmt
+ def to_inch(self):
+ if self.units == 'metric':
+ self.units = 'inch'
+ if self.x is not None:
+ self.x = inch(self.x)
+ if self.y is not None:
+ self.y = inch(self.y)
+
+ def to_metric(self):
+ if self.units == 'inch':
+ self.units = 'metric'
+ if self.x is not None:
+ self.x = metric(self.x)
+ if self.y is not None:
+ self.y = metric(self.y)
+
+ def offset(self, x_offset=0, y_offset=0):
+ if self.x is not None:
+ self.x += x_offset
+ if self.y is not None:
+ self.y += y_offset
+
+ def __str__(self):
+ coord_str = ''
+ if self.x is not None:
+ coord_str += 'X: %g ' % self.x
+ if self.y is not None:
+ coord_str += 'Y: %g ' % self.y
+
+ return '<Coordinate Statement: %s>' % coord_str
+
+
+class RepeatHoleStmt(ExcellonStatement):
+
+ @classmethod
+ def from_excellon(cls, line, settings, **kwargs):
+ match = re.compile(r'R(?P<rcount>[0-9]*)X?(?P<xdelta>[+\-]?\d*\.?\d*)?Y?'
+ '(?P<ydelta>[+\-]?\d*\.?\d*)?').match(line)
+ stmt = match.groupdict()
+ count = int(stmt['rcount'])
+ xdelta = (parse_gerber_value(stmt['xdelta'], settings.format,
+ settings.zero_suppression)
+ if stmt['xdelta'] is not '' else None)
+ ydelta = (parse_gerber_value(stmt['ydelta'], settings.format,
+ settings.zero_suppression)
+ if stmt['ydelta'] is not '' else None)
+ c = cls(count, xdelta, ydelta, **kwargs)
+ c.units = settings.units
+ return c
+
+ def __init__(self, count, xdelta=0.0, ydelta=0.0, **kwargs):
+ super(RepeatHoleStmt, self).__init__(**kwargs)
+ self.count = count
+ self.xdelta = xdelta
+ self.ydelta = ydelta
+
+ def to_excellon(self, settings):
+ stmt = 'R%d' % self.count
+ if self.xdelta is not None and self.xdelta != 0.0:
+ stmt += 'X%s' % write_gerber_value(self.xdelta, settings.format,
+ settings.zero_suppression)
+ if self.ydelta is not None and self.ydelta != 0.0:
+ stmt += 'Y%s' % write_gerber_value(self.ydelta, settings.format,
+ settings.zero_suppression)
+ return stmt
+
+ def to_inch(self):
+ if self.units == 'metric':
+ self.units = 'inch'
+ if self.xdelta is not None:
+ self.xdelta = inch(self.xdelta)
+ if self.ydelta is not None:
+ self.ydelta = inch(self.ydelta)
+
+ def to_metric(self):
+ if self.units == 'inch':
+ self.units = 'metric'
+ if self.xdelta is not None:
+ self.xdelta = metric(self.xdelta)
+ if self.ydelta is not None:
+ self.ydelta = metric(self.ydelta)
+
+ def __str__(self):
+ return '<Repeat Hole: %d times, offset X: %g Y: %g>' % (
+ self.count,
+ self.xdelta if self.xdelta is not None else 0,
+ self.ydelta if self.ydelta is not None else 0)
+
class CommentStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
return cls(line.lstrip(';'))
- def __init__(self, comment):
+ def __init__(self, comment, **kwargs):
+ super(CommentStmt, self).__init__(**kwargs)
self.comment = comment
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return ';%s' % self.comment
class HeaderBeginStmt(ExcellonStatement):
- def __init__(self):
- pass
+ def __init__(self, **kwargs):
+ super(HeaderBeginStmt, self).__init__(**kwargs)
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return 'M48'
class HeaderEndStmt(ExcellonStatement):
- def __init__(self):
- pass
+ def __init__(self, **kwargs):
+ super(HeaderEndStmt, self).__init__(**kwargs)
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return 'M95'
class RewindStopStmt(ExcellonStatement):
- def __init__(self):
- pass
+ def __init__(self, **kwargs):
+ super(RewindStopStmt, self).__init__(**kwargs)
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return '%'
+class ZAxisRoutPositionStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(ZAxisRoutPositionStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'M15'
+
+
+class RetractWithClampingStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(RetractWithClampingStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'M16'
+
+
+class RetractWithoutClampingStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(RetractWithoutClampingStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'M17'
+
+
+class CutterCompensationOffStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(CutterCompensationOffStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G40'
+
+
+class CutterCompensationLeftStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(CutterCompensationLeftStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G41'
+
+
+class CutterCompensationRightStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(CutterCompensationRightStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G42'
+
+
class EndOfProgramStmt(ExcellonStatement):
- def __init__(self, x=None, y=None):
+ @classmethod
+ def from_excellon(cls, line, settings, **kwargs):
+ match = re.compile(r'M30X?(?P<x>\d*\.?\d*)?Y?'
+ '(?P<y>\d*\.?\d*)?').match(line)
+ stmt = match.groupdict()
+ x = (parse_gerber_value(stmt['x'], settings.format,
+ settings.zero_suppression)
+ if stmt['x'] is not '' else None)
+ y = (parse_gerber_value(stmt['y'], settings.format,
+ settings.zero_suppression)
+ if stmt['y'] is not '' else None)
+ c = cls(x, y, **kwargs)
+ c.units = settings.units
+ return c
+
+ def __init__(self, x=None, y=None, **kwargs):
+ super(EndOfProgramStmt, self).__init__(**kwargs)
self.x = x
self.y = y
- def to_excellon(self):
+ def to_excellon(self, settings=None):
stmt = 'M30'
if self.x is not None:
stmt += 'X%s' % write_gerber_value(self.x)
@@ -307,119 +627,346 @@ class EndOfProgramStmt(ExcellonStatement):
stmt += 'Y%s' % write_gerber_value(self.y)
return stmt
+ def to_inch(self):
+ if self.units == 'metric':
+ self.units = 'inch'
+ if self.x is not None:
+ self.x = inch(self.x)
+ if self.y is not None:
+ self.y = inch(self.y)
+
+ def to_metric(self):
+ if self.units == 'inch':
+ self.units = 'metric'
+ if self.x is not None:
+ self.x = metric(self.x)
+ if self.y is not None:
+ self.y = metric(self.y)
+
+ def offset(self, x_offset=0, y_offset=0):
+ if self.x is not None:
+ self.x += x_offset
+ if self.y is not None:
+ self.y += y_offset
+
class UnitStmt(ExcellonStatement):
+
+ @classmethod
+ def from_settings(cls, settings):
+ """Create the unit statement from the FileSettings"""
+
+ return cls(settings.units, settings.zeros)
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
units = 'inch' if 'INCH' in line else 'metric'
- zero_suppression = 'trailing' if 'LZ' in line else 'leading'
- return cls(units, zero_suppression)
+ zeros = 'leading' if 'LZ' in line else 'trailing'
+ if '0000.00' in line:
+ format = (4, 2)
+ elif '000.000' in line:
+ format = (3, 3)
+ elif '00.0000' in line:
+ format = (2, 4)
+ else:
+ format = None
+ return cls(units, zeros, format, **kwargs)
- def __init__(self, units='inch', zero_suppression='trailing'):
+ def __init__(self, units='inch', zeros='leading', format=None, **kwargs):
+ super(UnitStmt, self).__init__(**kwargs)
self.units = units.lower()
- self.zero_suppression = zero_suppression
+ self.zeros = zeros
+ self.format = format
- def to_excellon(self):
+ def to_excellon(self, settings=None):
+ # TODO This won't export the invalid format statement if it exists
stmt = '%s,%s' % ('INCH' if self.units == 'inch' else 'METRIC',
- 'LZ' if self.zero_suppression == 'trailing'
+ 'LZ' if self.zeros == 'leading'
else 'TZ')
return stmt
+ def to_inch(self):
+ self.units = 'inch'
+
+ def to_metric(self):
+ self.units = 'metric'
+
class IncrementalModeStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
- return cls('off') if 'OFF' in line else cls('on')
+ def from_excellon(cls, line, **kwargs):
+ return cls('off', **kwargs) if 'OFF' in line else cls('on', **kwargs)
- def __init__(self, mode='off'):
+ def __init__(self, mode='off', **kwargs):
+ super(IncrementalModeStmt, self).__init__(**kwargs)
if mode.lower() not in ['on', 'off']:
raise ValueError('Mode may be "on" or "off"')
self.mode = mode
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return 'ICI,%s' % ('OFF' if self.mode == 'off' else 'ON')
class VersionStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
version = int(line.split(',')[1])
- return cls(version)
+ return cls(version, **kwargs)
- def __init__(self, version=1):
+ def __init__(self, version=1, **kwargs):
+ super(VersionStmt, self).__init__(**kwargs)
version = int(version)
if version not in [1, 2]:
raise ValueError('Valid versions are 1 or 2')
self.version = version
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return 'VER,%d' % self.version
class FormatStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
fmt = int(line.split(',')[1])
- return cls(fmt)
+ return cls(fmt, **kwargs)
- def __init__(self, format=1):
+ def __init__(self, format=1, **kwargs):
+ super(FormatStmt, self).__init__(**kwargs)
format = int(format)
if format not in [1, 2]:
raise ValueError('Valid formats are 1 or 2')
self.format = format
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return 'FMAT,%d' % self.format
+
+ @property
+ def format_tuple(self):
+ return (self.format, 6 - self.format)
class LinkToolStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
linked = [int(tool) for tool in line.split('/')]
- return cls(linked)
+ return cls(linked, **kwargs)
- def __init__(self, linked_tools):
+ def __init__(self, linked_tools, **kwargs):
+ super(LinkToolStmt, self).__init__(**kwargs)
self.linked_tools = [int(x) for x in linked_tools]
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return '/'.join([str(x) for x in self.linked_tools])
class MeasuringModeStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
+ def from_excellon(cls, line, **kwargs):
if not ('M71' in line or 'M72' in line):
raise ValueError('Not a measuring mode statement')
- return cls('inch') if 'M72' in line else cls('metric')
+ return cls('inch', **kwargs) if 'M72' in line else cls('metric', **kwargs)
- def __init__(self, units='inch'):
+ def __init__(self, units='inch', **kwargs):
+ super(MeasuringModeStmt, self).__init__(**kwargs)
units = units.lower()
if units not in ['inch', 'metric']:
raise ValueError('units must be "inch" or "metric"')
self.units = units
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return 'M72' if self.units == 'inch' else 'M71'
+ def to_inch(self):
+ self.units = 'inch'
+
+ def to_metric(self):
+ self.units = 'metric'
+
+
+class RouteModeStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(RouteModeStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G00'
+
+
+class LinearModeStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(LinearModeStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G01'
+
+
+class DrillModeStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(DrillModeStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G05'
+
+
+class AbsoluteModeStmt(ExcellonStatement):
+
+ def __init__(self, **kwargs):
+ super(AbsoluteModeStmt, self).__init__(**kwargs)
+
+ def to_excellon(self, settings=None):
+ return 'G90'
+
class UnknownStmt(ExcellonStatement):
@classmethod
- def from_excellon(cls, line):
- return cls(line)
+ def from_excellon(cls, line, **kwargs):
+ return cls(line, **kwargs)
- def __init__(self, stmt):
+ def __init__(self, stmt, **kwargs):
+ super(UnknownStmt, self).__init__(**kwargs)
self.stmt = stmt
- def to_excellon(self):
+ def to_excellon(self, settings=None):
return self.stmt
+ def __str__(self):
+ return "<Unknown Statement: %s>" % self.stmt
+
+
+class SlotStmt(ExcellonStatement):
+ """
+ G85 statement. Defines a slot created by multiple drills between two specified points.
+
+ Format is two coordinates, split by G85in the middle, for example, XnY0nG85XnYn
+ """
+
+ @classmethod
+ def from_points(cls, start, end):
+
+ return cls(start[0], start[1], end[0], end[1])
+
+ @classmethod
+ def from_excellon(cls, line, settings, **kwargs):
+ # Split the line based on the G85 separator
+ sub_coords = line.split('G85')
+ (x_start_coord, y_start_coord) = SlotStmt.parse_sub_coords(sub_coords[0], settings)
+ (x_end_coord, y_end_coord) = SlotStmt.parse_sub_coords(sub_coords[1], settings)
+
+ # Some files seem to specify only one of the coordinates
+ if x_end_coord == None:
+ x_end_coord = x_start_coord
+ if y_end_coord == None:
+ y_end_coord = y_start_coord
+
+ c = cls(x_start_coord, y_start_coord, x_end_coord, y_end_coord, **kwargs)
+ c.units = settings.units
+ return c
+
+ @staticmethod
+ def parse_sub_coords(line, settings):
+
+ x_coord = None
+ y_coord = None
+
+ if line[0] == 'X':
+ splitline = line.strip('X').split('Y')
+ x_coord = parse_gerber_value(splitline[0], settings.format,
+ settings.zero_suppression)
+ if len(splitline) == 2:
+ y_coord = parse_gerber_value(splitline[1], settings.format,
+ settings.zero_suppression)
+ else:
+ y_coord = parse_gerber_value(line.strip(' Y'), settings.format,
+ settings.zero_suppression)
+
+ return (x_coord, y_coord)
+
+
+ def __init__(self, x_start=None, y_start=None, x_end=None, y_end=None, **kwargs):
+ super(SlotStmt, self).__init__(**kwargs)
+ self.x_start = x_start
+ self.y_start = y_start
+ self.x_end = x_end
+ self.y_end = y_end
+ self.mode = None
+
+ def to_excellon(self, settings):
+ stmt = ''
+
+ if self.x_start is not None:
+ stmt += 'X%s' % write_gerber_value(self.x_start, settings.format,
+ settings.zero_suppression)
+ if self.y_start is not None:
+ stmt += 'Y%s' % write_gerber_value(self.y_start, settings.format,
+ settings.zero_suppression)
+
+ stmt += 'G85'
+
+ if self.x_end is not None:
+ stmt += 'X%s' % write_gerber_value(self.x_end, settings.format,
+ settings.zero_suppression)
+ if self.y_end is not None:
+ stmt += 'Y%s' % write_gerber_value(self.y_end, settings.format,
+ settings.zero_suppression)
+
+ return stmt
+
+ def to_inch(self):
+ if self.units == 'metric':
+ self.units = 'inch'
+ if self.x_start is not None:
+ self.x_start = inch(self.x_start)
+ if self.y_start is not None:
+ self.y_start = inch(self.y_start)
+ if self.x_end is not None:
+ self.x_end = inch(self.x_end)
+ if self.y_end is not None:
+ self.y_end = inch(self.y_end)
+
+ def to_metric(self):
+ if self.units == 'inch':
+ self.units = 'metric'
+ if self.x_start is not None:
+ self.x_start = metric(self.x_start)
+ if self.y_start is not None:
+ self.y_start = metric(self.y_start)
+ if self.x_end is not None:
+ self.x_end = metric(self.x_end)
+ if self.y_end is not None:
+ self.y_end = metric(self.y_end)
+
+ def offset(self, x_offset=0, y_offset=0):
+ if self.x_start is not None:
+ self.x_start += x_offset
+ if self.y_start is not None:
+ self.y_start += y_offset
+ if self.x_end is not None:
+ self.x_end += x_offset
+ if self.y_end is not None:
+ self.y_end += y_offset
+
+ def __str__(self):
+ start_str = ''
+ if self.x_start is not None:
+ start_str += 'X: %g ' % self.x_start
+ if self.y_start is not None:
+ start_str += 'Y: %g ' % self.y_start
+
+ end_str = ''
+ if self.x_end is not None:
+ end_str += 'X: %g ' % self.x_end
+ if self.y_end is not None:
+ end_str += 'Y: %g ' % self.y_end
+
+ return '<Slot Statement: %s to %s>' % (start_str, end_str)
def pairwise(iterator):
""" Iterate over list taking two elements at a time.
@@ -428,4 +975,4 @@ def pairwise(iterator):
"""
itr = iter(iterator)
while True:
- yield tuple([itr.next() for i in range(2)])
+ yield tuple([next(itr) for i in range(2)])