diff options
author | Hamilton Kibbe <hamilton.kibbe@gmail.com> | 2016-11-05 21:11:09 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-05 21:11:09 -0400 |
commit | d2fe4441662435e55f2dc481bf94a2729b9d6a48 (patch) | |
tree | dd60a0b21e1d1ca7258b9f978ce973354d96062c /gerber/excellon_statements.py | |
parent | 318a81382e074a5897489299a58e029815d23492 (diff) | |
parent | 5af19af190c1fb0f0c5be029d46d63e657dde4d9 (diff) | |
download | gerbonara-d2fe4441662435e55f2dc481bf94a2729b9d6a48.tar.gz gerbonara-d2fe4441662435e55f2dc481bf94a2729b9d6a48.tar.bz2 gerbonara-d2fe4441662435e55f2dc481bf94a2729b9d6a48.zip |
Merge pull request #3 from garretfick/merge-curtacircuitos
Merge curtacircuitos
Diffstat (limited to 'gerber/excellon_statements.py')
-rw-r--r-- | gerber/excellon_statements.py | 703 |
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)]) |