@@ -67,19 +67,25 @@ def extra_data_file_filter(self):
67
67
68
68
@staticmethod
69
69
def normalize (point ):
70
- return [point [ 0 ] * 1e-6 , point [ 1 ] * 1e-6 ]
70
+ return [point . x * 1e-6 , point . y * 1e-6 ]
71
71
72
72
@staticmethod
73
- def get_arc_angles (d ):
73
+ def normalize_angle (angle ):
74
+ if isinstance (angle , int ) or isinstance (angle , float ):
75
+ return angle * 0.1
76
+ else :
77
+ return angle .AsDegrees ()
78
+
79
+ def get_arc_angles (self , d ):
74
80
# type: (pcbnew.PCB_SHAPE) -> tuple
75
- a1 = d .GetArcAngleStart ()
81
+ a1 = self . normalize_angle ( d .GetArcAngleStart () )
76
82
if hasattr (d , "GetAngle" ):
77
- a2 = a1 + d .GetAngle ()
83
+ a2 = a1 + self . normalize_angle ( d .GetAngle () )
78
84
else :
79
- a2 = a1 + d .GetArcAngle ()
85
+ a2 = a1 + self . normalize_angle ( d .GetArcAngle () )
80
86
if a2 < a1 :
81
87
a1 , a2 = a2 , a1
82
- return round (a1 * 0.1 , 2 ), round (a2 * 0.1 , 2 )
88
+ return round (a1 , 2 ), round (a2 , 2 )
83
89
84
90
def parse_shape (self , d ):
85
91
# type: (pcbnew.PCB_SHAPE) -> dict or None
@@ -151,7 +157,7 @@ def parse_shape(self, d):
151
157
else :
152
158
parent_footprint = d .GetParentFootprint ()
153
159
if parent_footprint is not None :
154
- angle = parent_footprint .GetOrientation () * 0.1 ,
160
+ angle = self . normalize_angle ( parent_footprint .GetOrientation ())
155
161
shape_dict = {
156
162
"type" : shape ,
157
163
"pos" : start ,
@@ -178,26 +184,34 @@ def parse_shape(self, d):
178
184
"width" : d .GetWidth () * 1e-6
179
185
}
180
186
181
- def parse_poly_set (self , polygon_set ):
187
+ def parse_line_chain (self , shape ):
188
+ # type: (pcbnew.SHAPE_LINE_CHAIN) -> list
189
+ result = []
190
+ if not hasattr (shape , "PointCount" ):
191
+ self .logger .warn ("No PointCount method on outline object. "
192
+ "Unpatched kicad version?" )
193
+ return result
194
+
195
+ for point_index in range (shape .PointCount ()):
196
+ result .append (
197
+ self .normalize (shape .CPoint (point_index )))
198
+
199
+ return result
200
+
201
+ def parse_poly_set (self , poly ):
202
+ # type: (pcbnew.SHAPE_POLY_SET) -> list
182
203
result = []
183
- for polygon_index in range (polygon_set .OutlineCount ()):
184
- outline = polygon_set .Outline (polygon_index )
185
- if not hasattr (outline , "PointCount" ):
186
- self .logger .warn ("No PointCount method on outline object. "
187
- "Unpatched kicad version?" )
188
- return result
189
- parsed_outline = []
190
- for point_index in range (outline .PointCount ()):
191
- point = outline .CPoint (point_index )
192
- parsed_outline .append (self .normalize ([point .x , point .y ]))
193
- result .append (parsed_outline )
204
+
205
+ for i in range (poly .OutlineCount ()):
206
+ result .append (self .parse_line_chain (poly .Outline (i )))
194
207
195
208
return result
196
209
197
210
def parse_text (self , d ):
198
- pos = self . normalize ( d . GetPosition ())
199
- if not d .IsVisible () and d .GetClass () != "PTEXT" :
211
+ # type: (pcbnew.PCB_TEXT) -> dict
212
+ if not d .IsVisible () and d .GetClass () not in [ "PTEXT" , "PCB_TEXT" ] :
200
213
return None
214
+ pos = self .normalize (d .GetPosition ())
201
215
if hasattr (d , "GetTextThickness" ):
202
216
thickness = d .GetTextThickness () * 1e-6
203
217
else :
@@ -213,13 +227,38 @@ def parse_text(self, d):
213
227
"thickness" : thickness ,
214
228
"svgpath" : create_path (lines )
215
229
}
230
+ elif hasattr (d , 'GetEffectiveTextShape' ):
231
+ shape = d .GetEffectiveTextShape (
232
+ aTriangulate = False ) # type: pcbnew.SHAPE_COMPOUND
233
+ segments = []
234
+ polygons = []
235
+ for s in shape .GetSubshapes ():
236
+ if s .Type () == pcbnew .SH_LINE_CHAIN :
237
+ polygons .append (self .parse_line_chain (s ))
238
+ elif s .Type () == pcbnew .SH_SEGMENT :
239
+ seg = s .GetSeg ()
240
+ segments .append (
241
+ [self .normalize (seg .A ), self .normalize (seg .B )])
242
+ else :
243
+ self .logger .warn (
244
+ "Unsupported subshape in text: %s" % s .Type ())
245
+ if segments :
246
+ return {
247
+ "thickness" : thickness ,
248
+ "svgpath" : create_path (segments )
249
+ }
250
+ else :
251
+ return {
252
+ "polygons" : polygons
253
+ }
254
+
216
255
if d .GetClass () == "MTEXT" :
217
- angle = d .GetDrawRotation () * 0.1
256
+ angle = self . normalize_angle ( d .GetDrawRotation ())
218
257
else :
219
258
if hasattr (d , "GetTextAngle" ):
220
- angle = d .GetTextAngle () * 0.1
259
+ angle = self . normalize_angle ( d .GetTextAngle ())
221
260
else :
222
- angle = d .GetOrientation () * 0.1
261
+ angle = self . normalize_angle ( d .GetOrientation ())
223
262
if hasattr (d , "GetTextHeight" ):
224
263
height = d .GetTextHeight () * 1e-6
225
264
width = d .GetTextWidth () * 1e-6
@@ -250,15 +289,47 @@ def parse_text(self, d):
250
289
"angle" : angle
251
290
}
252
291
292
+ def parse_dimension (self , d ):
293
+ # type: (pcbnew.PCB_DIMENSION_BASE) -> dict
294
+ segments = []
295
+ circles = []
296
+ for s in d .GetShapes ():
297
+ s = s .Cast ()
298
+ if s .Type () == pcbnew .SH_SEGMENT :
299
+ seg = s .GetSeg ()
300
+ segments .append (
301
+ [self .normalize (seg .A ), self .normalize (seg .B )])
302
+ elif s .Type () == pcbnew .SH_CIRCLE :
303
+ circles .append (
304
+ [self .normalize (s .GetCenter ()), s .GetRadius () * 1e-6 ])
305
+ else :
306
+ self .logger .info (
307
+ "Unsupported shape type in dimension object: %s" , s .Type ())
308
+
309
+ svgpath = create_path (segments , circles = circles )
310
+
311
+ return {
312
+ "thickness" : d .GetLineThickness () * 1e-6 ,
313
+ "svgpath" : svgpath
314
+ }
315
+
253
316
def parse_drawing (self , d ):
317
+ # type: (pcbnew.BOARD_ITEM) -> list
318
+ result = []
319
+ s = None
254
320
if d .GetClass () in ["DRAWSEGMENT" , "MGRAPHIC" , "PCB_SHAPE" ]:
255
- return self .parse_shape (d )
256
- elif d .GetClass () in ["PTEXT" , "MTEXT" ]:
257
- return self .parse_text (d )
321
+ s = self .parse_shape (d )
322
+ elif d .GetClass () in ["PTEXT" , "MTEXT" , "FP_TEXT" , "PCB_TEXT" ]:
323
+ s = self .parse_text (d )
324
+ elif d .GetClass ().startswith ("PCB_DIM" ):
325
+ result .append (self .parse_dimension (d ))
326
+ s = self .parse_text (d .Text ())
258
327
else :
259
328
self .logger .info ("Unsupported drawing class %s, skipping" ,
260
329
d .GetClass ())
261
- return None
330
+ if s :
331
+ result .append (s )
332
+ return result
262
333
263
334
def parse_edges (self , pcb ):
264
335
edges = []
@@ -269,8 +340,7 @@ def parse_edges(self, pcb):
269
340
drawings .append (g )
270
341
for d in drawings :
271
342
if d .GetLayer () == pcbnew .Edge_Cuts :
272
- parsed_drawing = self .parse_drawing (d )
273
- if parsed_drawing :
343
+ for parsed_drawing in self .parse_drawing (d ):
274
344
edges .append (parsed_drawing )
275
345
if bbox is None :
276
346
bbox = d .GetBoundingBox ()
@@ -287,15 +357,13 @@ def parse_drawings_on_layers(self, drawings, f_layer, b_layer):
287
357
for d in drawings :
288
358
if d [1 ].GetLayer () not in [f_layer , b_layer ]:
289
359
continue
290
- drawing = self .parse_drawing (d [1 ])
291
- if not drawing :
292
- continue
293
- if d [0 ] in ["ref" , "val" ]:
294
- drawing [d [0 ]] = 1
295
- if d [1 ].GetLayer () == f_layer :
296
- front .append (drawing )
297
- else :
298
- back .append (drawing )
360
+ for drawing in self .parse_drawing (d [1 ]):
361
+ if d [0 ] in ["ref" , "val" ]:
362
+ drawing [d [0 ]] = 1
363
+ if d [1 ].GetLayer () == f_layer :
364
+ front .append (drawing )
365
+ else :
366
+ back .append (drawing )
299
367
300
368
return {
301
369
"F" : front ,
@@ -321,7 +389,7 @@ def parse_pad(self, pad):
321
389
layers .append ("B" )
322
390
pos = self .normalize (pad .GetPosition ())
323
391
size = self .normalize (pad .GetSize ())
324
- angle = pad .GetOrientation () * - 0.1
392
+ angle = self . normalize_angle ( pad .GetOrientation ())
325
393
shape_lookup = {
326
394
pcbnew .PAD_SHAPE_RECT : "rect" ,
327
395
pcbnew .PAD_SHAPE_OVAL : "oval" ,
@@ -401,8 +469,14 @@ def parse_footprints(self):
401
469
f_copy = pcbnew .MODULE (f )
402
470
else :
403
471
f_copy = pcbnew .FOOTPRINT (f )
404
- f_copy .SetOrientation (0 )
405
- f_copy .SetPosition (pcbnew .wxPoint (0 , 0 ))
472
+ try :
473
+ f_copy .SetOrientation (0 )
474
+ except TypeError :
475
+ f_copy .SetOrientation (
476
+ pcbnew .EDA_ANGLE (0 , pcbnew .TENTHS_OF_A_DEGREE_T ))
477
+ pos = f_copy .GetPosition ()
478
+ pos .x = pos .y = 0
479
+ f_copy .SetPosition (pos )
406
480
if hasattr (f_copy , 'GetFootprintRect' ):
407
481
footprint_rect = f_copy .GetFootprintRect ()
408
482
else :
@@ -411,7 +485,7 @@ def parse_footprints(self):
411
485
"pos" : self .normalize (f .GetPosition ()),
412
486
"relpos" : self .normalize (footprint_rect .GetPosition ()),
413
487
"size" : self .normalize (footprint_rect .GetSize ()),
414
- "angle" : f .GetOrientation () * 0.1 ,
488
+ "angle" : self . normalize_angle ( f .GetOrientation ()) ,
415
489
}
416
490
417
491
# graphical drawings
@@ -420,13 +494,11 @@ def parse_footprints(self):
420
494
# we only care about copper ones, silkscreen is taken care of
421
495
if d .GetLayer () not in [pcbnew .F_Cu , pcbnew .B_Cu ]:
422
496
continue
423
- drawing = self .parse_drawing (d )
424
- if not drawing :
425
- continue
426
- drawings .append ({
427
- "layer" : "F" if d .GetLayer () == pcbnew .F_Cu else "B" ,
428
- "drawing" : drawing ,
429
- })
497
+ for drawing in self .parse_drawing (d ):
498
+ drawings .append ({
499
+ "layer" : "F" if d .GetLayer () == pcbnew .F_Cu else "B" ,
500
+ "drawing" : drawing ,
501
+ })
430
502
431
503
# footprint pads
432
504
pads = []
0 commit comments