summaryrefslogtreecommitdiff
path: root/gerber/render/excellon_backend.py
blob: 765d68c972e1f3997f8852719e4e44c52df0bf66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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()]

        # 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

        self._start_header()
        self._start_comments()

    def _start_header(self):
        """Create the header from the settings"""

        self.header.append(UnitStmt.from_settings(self.settings))

        if self.settings.notation == 'incremental':
            raise NotImplementedError('Incremental mode is not implemented')
        else:
            self.body.append(AbsoluteModeStmt())

    def _start_comments(self):

        # 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._get_end()

    def set_bounds(self, bounds, *args, **kwargs):
        pass

    def paint_background(self):
        pass

    def _render_line(self, line, color):
        raise ValueError('Invalid Excellon object')
    def _render_arc(self, arc, color):
        raise ValueError('Invalid Excellon object')

    def _render_region(self, region, color):
        raise ValueError('Invalid Excellon object')

    def _render_level_polarity(self, region):
        raise ValueError('Invalid Excellon object')

    def _render_circle(self, circle, color):
        raise ValueError('Invalid Excellon object')

    def _render_rectangle(self, rectangle, color):
        raise ValueError('Invalid Excellon object')

    def _render_obround(self, obround, color):
        raise ValueError('Invalid Excellon object')

    def _render_polygon(self, polygon, color):
        raise ValueError('Invalid Excellon object')

    def _simplify_point(self, point):
        return (point[0] if point[0] != self._pos[0] else None, point[1] if point[1] != self._pos[1] else None)

    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)
            self.header.append(ExcellonTool.from_tool(tool))

        if tool != self.cur_tool:
            self.body.append(ToolSelectionStmt(tool.number))
            self.cur_tool = tool

        point = self._simplify_point(drill.position)
        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

        # 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_mode = 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