From 288f49955ecc1a811752aa4b1e713f9954e3033b Mon Sep 17 00:00:00 2001 From: Garret Fick Date: Sun, 27 Mar 2016 14:24:11 +0800 Subject: Actually fix the rout rendering to be correct --- gerber/excellon.py | 10 ++-- gerber/excellon_statements.py | 8 +++- gerber/render/excellon_backend.py | 98 +++++++++++++++++++++++++++++++++++---- 3 files changed, 102 insertions(+), 14 deletions(-) diff --git a/gerber/excellon.py b/gerber/excellon.py index 02709fd..72cf75c 100755 --- a/gerber/excellon.py +++ b/gerber/excellon.py @@ -111,10 +111,14 @@ class DrillSlot(object): A slot is created between two points. The way the slot is created depends on the statement used to create it """ - def __init__(self, tool, start, end): + TYPE_ROUT = 1 + TYPE_G85 = 2 + + def __init__(self, tool, start, end, slot_type): self.tool = tool self.start = start self.end = end + self.slot_type = slot_type def to_inch(self): if self.tool.units == 'metric': @@ -520,7 +524,7 @@ class ExcellonParser(object): if not self.active_tool: self.active_tool = self._get_tool(1) - self.hits.append(DrillSlot(self.active_tool, start, end)) + self.hits.append(DrillSlot(self.active_tool, start, end, DrillSlot.TYPE_ROUT)) self.active_tool._hit() elif line[:3] == 'G05': @@ -641,7 +645,7 @@ class ExcellonParser(object): if not self.active_tool: self.active_tool = self._get_tool(1) - self.hits.append(DrillSlot(self.active_tool, (stmt.x_start, stmt.y_start), (stmt.x_end, stmt.y_end))) + self.hits.append(DrillSlot(self.active_tool, (stmt.x_start, stmt.y_start), (stmt.x_end, stmt.y_end), DrillSlot.TYPE_G85)) self.active_tool._hit() else: stmt = CoordinateStmt.from_excellon(line, self._settings()) diff --git a/gerber/excellon_statements.py b/gerber/excellon_statements.py index a6a5a5e..c9367b4 100644 --- a/gerber/excellon_statements.py +++ b/gerber/excellon_statements.py @@ -367,8 +367,12 @@ class ZAxisInfeedRateStmt(ExcellonStatement): class CoordinateStmt(ExcellonStatement): @classmethod - def from_point(cls, point): - return cls(point[0], point[1]) + 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): diff --git a/gerber/render/excellon_backend.py b/gerber/render/excellon_backend.py index eb79f1b..b2c213f 100644 --- a/gerber/render/excellon_backend.py +++ b/gerber/render/excellon_backend.py @@ -1,21 +1,29 @@ from .render import GerberContext +from ..excellon import DrillSlot from ..excellon_statements import * class ExcellonContext(GerberContext): + MODE_DRILL = 1 + MODE_SLOT =2 + def __init__(self, settings): GerberContext.__init__(self) + + # Statements that we write self.comments = [] self.header = [] self.tool_def = [] self.body_start = [RewindStopStmt()] self.body = [] self.start = [HeaderBeginStmt()] - self.end = [EndOfProgramStmt()] + # Current tool and position self.handled_tools = set() self.cur_tool = None + self.drill_mode = ExcellonContext.MODE_DRILL + self.drill_down = False self._pos = (None, None) self.settings = settings @@ -33,9 +41,22 @@ class ExcellonContext(GerberContext): # Write the digits used - this isn't valid Excellon statement, so we write as a comment self.comments.append(CommentStmt('FILE_FORMAT=%d:%d' % (self.settings.format[0], self.settings.format[1]))) + def _get_end(self): + """How we end depends on our mode""" + + end = [] + + if self.drill_down: + end.append(RetractWithClampingStmt()) + end.append(RetractWithoutClampingStmt()) + + end.append(EndOfProgramStmt()) + + return end + @property def statements(self): - return self.start + self.comments + self.header + self.body_start + self.body + self.end + return self.start + self.comments + self.header + self.body_start + self.body + self._get_end() def set_bounds(self, bounds): pass @@ -71,6 +92,9 @@ class ExcellonContext(GerberContext): def _render_drill(self, drill, color): + if self.drill_mode != ExcellonContext.MODE_DRILL: + self._start_drill_mode() + tool = drill.hit.tool if not tool in self.handled_tools: self.handled_tools.add(tool) @@ -84,20 +108,76 @@ class ExcellonContext(GerberContext): self._pos = drill.position self.body.append(CoordinateStmt.from_point(point)) + def _start_drill_mode(self): + """ + If we are not in drill mode, then end the ROUT so we can do basic drilling + """ + + if self.drill_mode == ExcellonContext.MODE_SLOT: + + # Make sure we are retracted before changing modes + last_cmd = self.body[-1] + if self.drill_down: + self.body.append(RetractWithClampingStmt()) + self.body.append(RetractWithoutClampingStmt()) + self.drill_down = False + + # Switch to drill mode + self.body.append(DrillModeStmt()) + self.drill_mode = ExcellonContext.MODE_DRILL + + else: + raise ValueError('Should be in slot mode') + def _render_slot(self, slot, color): + # Set the tool first, before we might go into drill mode tool = slot.hit.tool if not tool in self.handled_tools: self.handled_tools.add(tool) self.header.append(ExcellonTool.from_tool(tool)) - - if tool != self.cur_tool: - self.body.append(ToolSelectionStmt(tool.number)) - self.cur_tool = tool - # Slots don't use simplified points - self._pos = slot.end - self.body.append(SlotStmt.from_points(slot.start, slot.end)) + if tool != self.cur_tool: + self.body.append(ToolSelectionStmt(tool.number)) + self.cur_tool = tool + + # Two types of drilling - normal drill and slots + if slot.hit.slot_type == DrillSlot.TYPE_ROUT: + + # For ROUT, setting the mode is part of the actual command. + + # Are we in the right position? + if slot.start != self._pos: + if self.drill_down: + # We need to move into the right position, so retract + self.body.append(RetractWithClampingStmt()) + self.drill_down = False + + # Move to the right spot + point = self._simplify_point(slot.start) + self._pos = slot.start + self.body.append(CoordinateStmt.from_point(point, mode="ROUT")) + + # Now we are in the right spot, so drill down + if not self.drill_down: + self.body.append(ZAxisRoutPositionStmt()) + self.drill_down = True + + # Do a linear move from our current position to the end position + point = self._simplify_point(slot.end) + self._pos = slot.end + self.body.append(CoordinateStmt.from_point(point, mode="LINEAR")) + + self.drill_down = ExcellonContext.MODE_SLOT + + else: + # This is a G85 slot, so do this in normally drilling mode + if self.drill_mode != ExcellonContext.MODE_DRILL: + self._start_drill_mode() + + # Slots don't use simplified points + self._pos = slot.end + self.body.append(SlotStmt.from_points(slot.start, slot.end)) def _render_inverted_layer(self): pass -- cgit