2b01c98ab1b7e1e1e6421d20851ef019ebdcc6f6
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

1) """
2) Exporting of GNU PCB files to DXF.
3) """
4) 
5) import ezdxf
6) import sys
7) 
8) import pcb_types
9) 
10) 
11) class DxfFootprintWriter():
12)     """
13)     Writer for DXF files from GNU PCB footprints.
14)     """
15) 
16)     def __init__(self):
17)         self._doc = ezdxf.new('R12')
18)         self._msp = self._doc.modelspace()
19)         self._layers = {}
20)         self._addLayer('clearance', 8)  # gray
21)         self._addLayer('hole_drill', 3)  # green
22)         self._addLayer('mask', 16)  # dark red
23)         self._addLayer('name', 7)  # white
24)         self._addLayer('number', 7)  # white
Stefan Schuermans begin DXF export of pads

Stefan Schuermans authored 3 years ago

25)         self._addLayer('pad_component', 1)  # red
26)         self._addLayer('pad_solder', 1)  # red
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

27)         self._addLayer('pin_copper', 1)  # red
28)         self._addLayer('pin_drill', 5)  # blue
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

29)         self._addLayer('silk_center', 2)  # yellow
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

30) 
31)     def _addCircle(self, x: pcb_types.Coordinate, y: pcb_types.Coordinate,
32)                    d: pcb_types.Coordinate, layer_name: str):
33)         self._msp.add_circle((x.mm, -y.mm),
34)                              radius=d.mm / 2,
35)                              dxfattribs={'layer': layer_name})
36) 
37)     def _addOctagon(self, cx: pcb_types.Coordinate, cy: pcb_types.Coordinate,
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

38)                     size: pcb_types.Coordinate, layer_name: str):
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

39)         l = size.mm / 2
40)         s = l / (1 + 2**.5)
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

41)         points = [(cx.mm - s, -cy.mm - l), (cx.mm + s, -cy.mm - l),
42)                   (cx.mm + l, -cy.mm - s), (cx.mm + l, -cy.mm + s),
43)                   (cx.mm + s, -cy.mm + l), (cx.mm - s, -cy.mm + l),
44)                   (cx.mm - l, -cy.mm + s), (cx.mm - l, -cy.mm - s),
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

45)                   (cx.mm - s, -cy.mm - l)]
46)         self._msp.add_polyline2d(points, dxfattribs={'layer': layer_name})
47) 
Stefan Schuermans begin DXF export of pads

Stefan Schuermans authored 3 years ago

48)     def _addRect(self, x1: pcb_types.Coordinate, y1: pcb_types.Coordinate,
49)                  x2: pcb_types.Coordinate, y2: pcb_types.Coordinate,
50)                  width: pcb_types.Coordinate, layer_name: str):
51)         v1 = pcb_types.Vector(x1, y1)
52)         v2 = pcb_types.Vector(x2, y2)
53)         i = (v2 - v1).resized(width / 2)  # half width in direction
54)         o = i.rotatedCW()  # half width orthogonal to direction
55)         p1 = v1 - i - o
56)         p2 = v1 - i + o
57)         p3 = v2 + i + o
58)         p4 = v2 + i - o
59)         points = [(p1.x.mm, -p1.y.mm), (p2.x.mm, -p2.y.mm),
60)                   (p3.x.mm, -p3.y.mm), (p4.x.mm, -p4.y.mm),
61)                   (p1.x.mm, -p1.y.mm)]
62)         self._msp.add_polyline2d(points, dxfattribs={'layer': layer_name})
63) 
64)     def _addRoundRect(self, x1: pcb_types.Coordinate, y1: pcb_types.Coordinate,
65)                       x2: pcb_types.Coordinate, y2: pcb_types.Coordinate,
66)                       width: pcb_types.Coordinate, layer_name: str):
67)         # FIXME: approximated rounding used for now, should be really round
68)         v1 = pcb_types.Vector(x1, y1)
69)         v2 = pcb_types.Vector(x2, y2)
70)         i = (v2 - v1).resized(width / 2)  # half width in direction
71)         steps = range(-90, 91, 15)
72)         points = []
73)         for deg in steps:
74)             p = v1 - i.rotated(deg)
75)             points.append((p.x.mm, -p.y.mm))
76)         for deg in steps:
77)             p = v2 + i.rotated(deg)
78)             points.append((p.x.mm, -p.y.mm))
79)         points.append(points[0])
80)         self._msp.add_polyline2d(points, dxfattribs={'layer': layer_name})
81) 
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

82)     def _addSquare(self, cx: pcb_types.Coordinate, cy: pcb_types.Coordinate,
83)                    size: pcb_types.Coordinate, layer_name: str):
84)         points = [(cx.mm - size.mm / 2, -cy.mm - size.mm / 2),
85)                   (cx.mm + size.mm / 2, -cy.mm - size.mm / 2),
86)                   (cx.mm + size.mm / 2, -cy.mm + size.mm / 2),
87)                   (cx.mm - size.mm / 2, -cy.mm + size.mm / 2),
88)                   (cx.mm - size.mm / 2, -cy.mm - size.mm / 2)]
89)         self._msp.add_polyline2d(points, dxfattribs={'layer': layer_name})
90) 
91)     def _addLayer(self, name: str, color: int = 7):
92)         if name in self._layers:
93)             return
94)         self._layers[name] = self._doc.layers.new(name=name,
95)                                                   dxfattribs={
96)                                                       'linetype': 'Continuous',
97)                                                       'color': color
98)                                                   })
99) 
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

100)     def _addLine(self, x1: pcb_types.Coordinate, y1: pcb_types.Coordinate,
101)                  x2: pcb_types.Coordinate, y2: pcb_types.Coordinate,
102)                  layer_name: str):
103)         self._msp.add_line((x1.mm, -y1.mm), (x2.mm, -y2.mm),
104)                            dxfattribs={'layer': layer_name})
105) 
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

106)     def _addText(self,
107)                  x: pcb_types.Coordinate,
108)                  y: pcb_types.Coordinate,
109)                  t: str,
110)                  layer_name: str,
111)                  height: pcb_types.Coordinate = None,
112)                  align: str = None):
113)         dxfattribs = {'layer': layer_name}
114)         if height is not None:
115)             dxfattribs['height'] = height.mm
116)         text = self._msp.add_text(t, dxfattribs=dxfattribs)
117)         kwargs = {}
118)         if align is not None:
119)             kwargs['align'] = align
120)         text.set_pos((x.mm, -y.mm), **kwargs)
121) 
122)     def _drawHole(self, pin: pcb_types.Pin):
123)         self._addCircle(pin.r_x, pin.r_y, pin.drill, 'hole_drill')
124)         self._addCircle(pin.r_x, pin.r_y, pin.thickness + pin.clearance,
125)                         'clearance')
126)         self._addCircle(pin.r_x, pin.r_y, pin.mask, 'mask')
127)         self._drawNameNumber(pin.r_x,
128)                              pin.r_y,
129)                              pin.name,
130)                              pin.number,
131)                              height=pin.drill / 2)
132) 
133)     def _drawNameNumber(self,
134)                         x: pcb_types.Coordinate,
135)                         y: pcb_types.Coordinate,
136)                         name: str,
137)                         number: str,
138)                         height: pcb_types.Coordinate = None):
139)         self._addText(x, y, name, 'name', height=height, align='BOTTOM_CENTER')
140)         self._addText(x,
141)                       y,
142)                       number,
143)                       'number',
144)                       height=height,
145)                       align='TOP_CENTER')
146) 
Stefan Schuermans begin DXF export of pads

Stefan Schuermans authored 3 years ago

147)     def _drawPadRound(self, pad: pcb_types.Pad, pad_layer: str):
148)         self._addRoundRect(pad.r_x1, pad.r_y1, pad.r_x2, pad.r_y2,
149)                            pad.thickness, pad_layer)
150)         self._addRoundRect(pad.r_x1, pad.r_y1, pad.r_x2, pad.r_y2,
151)                            pad.thickness + pad.clearance, 'clearance')
152)         self._addRoundRect(pad.r_x1, pad.r_y1, pad.r_x2, pad.r_y2, pad.mask,
153)                            'mask')
154)         self._drawNameNumber((pad.r_x1 + pad.r_x2) / 2,
155)                              (pad.r_y1 + pad.r_y2) / 2,
156)                              pad.name,
157)                              pad.number,
158)                              height=pad.thickness / 3)
159) 
160)     def _drawPadSquare(self, pad: pcb_types.Pad, pad_layer: str):
161)         self._addRect(pad.r_x1, pad.r_y1, pad.r_x2, pad.r_y2, pad.thickness,
162)                       pad_layer)
163)         self._addRect(pad.r_x1, pad.r_y1, pad.r_x2, pad.r_y2,
164)                       pad.thickness + pad.clearance, 'clearance')
165)         self._addRect(pad.r_x1, pad.r_y1, pad.r_x2, pad.r_y2, pad.mask, 'mask')
166)         self._drawNameNumber((pad.r_x1 + pad.r_x2) / 2,
167)                              (pad.r_y1 + pad.r_y2) / 2,
168)                              pad.name,
169)                              pad.number,
170)                              height=pad.thickness / 3)
171) 
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

172)     def _drawPinOctagon(self, pin: pcb_types.Pin):
173)         self._addOctagon(pin.r_x, pin.r_y, pin.thickness, 'pin_copper')
174)         self._addCircle(pin.r_x, pin.r_y, pin.drill, 'pin_drill')
175)         self._addOctagon(pin.r_x, pin.r_y, pin.thickness + pin.clearance,
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

176)                          'clearance')
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

177)         self._addOctagon(pin.r_x, pin.r_y, pin.mask, 'mask')
178)         self._drawNameNumber(pin.r_x,
179)                              pin.r_y,
180)                              pin.name,
181)                              pin.number,
182)                              height=pin.drill / 2)
183) 
184)     def _drawPinRound(self, pin: pcb_types.Pin):
185)         self._addCircle(pin.r_x, pin.r_y, pin.thickness, 'pin_copper')
186)         self._addCircle(pin.r_x, pin.r_y, pin.drill, 'pin_drill')
187)         self._addCircle(pin.r_x, pin.r_y, pin.thickness + pin.clearance,
188)                         'clearance')
189)         self._addCircle(pin.r_x, pin.r_y, pin.mask, 'mask')
190)         self._drawNameNumber(pin.r_x,
191)                              pin.r_y,
192)                              pin.name,
193)                              pin.number,
194)                              height=pin.drill / 2)
195) 
196)     def _drawPinSquare(self, pin: pcb_types.Pin):
197)         self._addSquare(pin.r_x, pin.r_y, pin.thickness, 'pin_copper')
198)         self._addCircle(pin.r_x, pin.r_y, pin.drill, 'pin_drill')
199)         self._addSquare(pin.r_x, pin.r_y, pin.thickness + pin.clearance,
200)                         'clearance')
201)         self._addSquare(pin.r_x, pin.r_y, pin.mask, 'mask')
202)         self._drawNameNumber(pin.r_x,
203)                              pin.r_y,
204)                              pin.name,
205)                              pin.number,
206)                              height=pin.drill / 2)
207) 
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

208)     def drawElementLine(self, element_line: pcb_types.ElementLine):
209)         """
210)         Draw element line to DXF.
211)         """
212)         self._addLine(element_line.r_x1, element_line.r_y1, element_line.r_x2,
213)                       element_line.r_y2, 'silk_center')
214)         # TODO element_line.thickness
215)         # TODO element_line.s_flags onsolder
216) 
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

217)     def drawFootprint(self, fp: pcb_types.Element):
218)         """
219)         Draw footprint to DXF.
220)         """
221)         # draw entities in header
222)         # TODO
223)         # draw entities in body
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

224)         type2method = {
225)             pcb_types.ElementLine: self.drawElementLine,
Stefan Schuermans begin DXF export of pads

Stefan Schuermans authored 3 years ago

226)             pcb_types.Pin: self.drawPin,
227)             pcb_types.Pad: self.drawPad
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

228)         }
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

229)         for entity in fp.body:
230)             try:
231)                 method = type2method[type(entity)]
232)                 method(entity)
233)             except KeyError:
234)                 # TODO
235)                 print(
236)                     'warning: ignoring unknown entity of'
237)                     f' type {type(entity).__name__:s}',
238)                     file=sys.stderr)
239) 
Stefan Schuermans begin DXF export of pads

Stefan Schuermans authored 3 years ago

240)     def drawPad(self, pad: pcb_types.Pad):
241)         """
242)         Draw pad to DXF.
243)         """
244)         if 'onsolder' in pad.s_flags:
245)             pad_layer = 'pad_solder'
246)         else:
247)             pad_layer = 'pad_component'
248)         if 'square' in pad.s_flags:
249)             return self._drawPadSquare(pad, pad_layer)
250)         return self._drawPadRound(pad, pad_layer)
251) 
Stefan Schuermans DXF export of pins + holes

Stefan Schuermans authored 3 years ago

252)     def drawPin(self, pin: pcb_types.Pin):
Stefan Schuermans begin DXF export of element...

Stefan Schuermans authored 3 years ago

253)         """
254)         Draw pin to DXF.
255)         """