Skip to content

Commit 50c790c

Browse files
committed
Update compatibility with nightly, support dimensions
1 parent a329307 commit 50c790c

File tree

7 files changed

+199
-82
lines changed

7 files changed

+199
-82
lines changed

DATAFORMAT.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ attribute.
240240
// will not attempt to read character data from newstroke font and
241241
// will draw the path as is. "thickness" will be used as stroke width.
242242
"svgpath": svgpath,
243+
// If polygons are specified then remaining attributes are ignored
244+
"polygons": [
245+
// Polygons are described as set of outlines.
246+
[
247+
[point1x, point1y], [point2x, point2y], ...
248+
],
249+
...
250+
],
243251
"height": height,
244252
"width": width,
245253
// -1: justify left/top

InteractiveHtmlBom/dialog/dialog_base.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
###########################################################################
4-
## Python code generated with wxFormBuilder (version Oct 26 2018)
4+
## Python code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
55
## http://www.wxformbuilder.org/
66
##
77
## PLEASE DO *NOT* EDIT THIS FILE!
@@ -50,7 +50,7 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
5050
bSizer39.Add( self.m_button41, 0, wx.ALL, 5 )
5151

5252

53-
bSizer39.Add( ( 50, 0), 0, wx.EXPAND, 5 )
53+
bSizer39.Add( ( 50, 0), 1, wx.EXPAND, 5 )
5454

5555
self.m_button42 = wx.Button( self, wx.ID_ANY, u"Generate BOM", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
5656

@@ -61,7 +61,7 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
6161
bSizer39.Add( self.m_button43, 0, wx.ALL, 5 )
6262

6363

64-
bSizer20.Add( bSizer39, 0, wx.ALIGN_CENTER, 5 )
64+
bSizer20.Add( bSizer39, 0, wx.EXPAND, 5 )
6565

6666

6767
self.SetSizer( bSizer20 )
@@ -76,7 +76,7 @@ def __del__( self ):
7676
pass
7777

7878

79-
# Virtual event handlers, overide them in your derived class
79+
# Virtual event handlers, override them in your derived class
8080
def OnSaveSettings( self, event ):
8181
event.Skip()
8282

@@ -191,7 +191,7 @@ def __del__( self ):
191191
pass
192192

193193

194-
# Virtual event handlers, overide them in your derived class
194+
# Virtual event handlers, override them in your derived class
195195
def OnBoardRotationSlider( self, event ):
196196
event.Skip()
197197

@@ -370,7 +370,7 @@ def __del__( self ):
370370
pass
371371

372372

373-
# Virtual event handlers, overide them in your derived class
373+
# Virtual event handlers, override them in your derived class
374374
def OnSize( self, event ):
375375
event.Skip()
376376

@@ -432,10 +432,10 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
432432
self.fieldsGrid.AutoSizeColumns()
433433
self.fieldsGrid.EnableDragColMove( False )
434434
self.fieldsGrid.EnableDragColSize( True )
435-
self.fieldsGrid.SetColLabelSize( 30 )
436435
self.fieldsGrid.SetColLabelValue( 0, u"Show" )
437436
self.fieldsGrid.SetColLabelValue( 1, u"Group" )
438437
self.fieldsGrid.SetColLabelValue( 2, u"Name" )
438+
self.fieldsGrid.SetColLabelSize( 30 )
439439
self.fieldsGrid.SetColLabelAlignment( wx.ALIGN_CENTER, wx.ALIGN_CENTER )
440440

441441
# Rows
@@ -552,7 +552,7 @@ def __del__( self ):
552552
pass
553553

554554

555-
# Virtual event handlers, overide them in your derived class
555+
# Virtual event handlers, override them in your derived class
556556
def OnSize( self, event ):
557557
event.Skip()
558558

InteractiveHtmlBom/ecad/easyeda.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def parse_pad(self, shape):
222222
"layers": pad_layers,
223223
"pos": [x, y],
224224
"size": [width, height],
225-
"angle": -angle,
225+
"angle": angle,
226226
"shape": pad_shape,
227227
"type": pad_type,
228228
}

InteractiveHtmlBom/ecad/kicad.py

Lines changed: 122 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,25 @@ def extra_data_file_filter(self):
6767

6868
@staticmethod
6969
def normalize(point):
70-
return [point[0] * 1e-6, point[1] * 1e-6]
70+
return [point.x * 1e-6, point.y * 1e-6]
7171

7272
@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):
7480
# type: (pcbnew.PCB_SHAPE) -> tuple
75-
a1 = d.GetArcAngleStart()
81+
a1 = self.normalize_angle(d.GetArcAngleStart())
7682
if hasattr(d, "GetAngle"):
77-
a2 = a1 + d.GetAngle()
83+
a2 = a1 + self.normalize_angle(d.GetAngle())
7884
else:
79-
a2 = a1 + d.GetArcAngle()
85+
a2 = a1 + self.normalize_angle(d.GetArcAngle())
8086
if a2 < a1:
8187
a1, a2 = a2, a1
82-
return round(a1 * 0.1, 2), round(a2 * 0.1, 2)
88+
return round(a1, 2), round(a2, 2)
8389

8490
def parse_shape(self, d):
8591
# type: (pcbnew.PCB_SHAPE) -> dict or None
@@ -151,7 +157,7 @@ def parse_shape(self, d):
151157
else:
152158
parent_footprint = d.GetParentFootprint()
153159
if parent_footprint is not None:
154-
angle = parent_footprint.GetOrientation() * 0.1,
160+
angle = self.normalize_angle(parent_footprint.GetOrientation())
155161
shape_dict = {
156162
"type": shape,
157163
"pos": start,
@@ -178,26 +184,34 @@ def parse_shape(self, d):
178184
"width": d.GetWidth() * 1e-6
179185
}
180186

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
182203
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)))
194207

195208
return result
196209

197210
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"]:
200213
return None
214+
pos = self.normalize(d.GetPosition())
201215
if hasattr(d, "GetTextThickness"):
202216
thickness = d.GetTextThickness() * 1e-6
203217
else:
@@ -213,13 +227,38 @@ def parse_text(self, d):
213227
"thickness": thickness,
214228
"svgpath": create_path(lines)
215229
}
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+
216255
if d.GetClass() == "MTEXT":
217-
angle = d.GetDrawRotation() * 0.1
256+
angle = self.normalize_angle(d.GetDrawRotation())
218257
else:
219258
if hasattr(d, "GetTextAngle"):
220-
angle = d.GetTextAngle() * 0.1
259+
angle = self.normalize_angle(d.GetTextAngle())
221260
else:
222-
angle = d.GetOrientation() * 0.1
261+
angle = self.normalize_angle(d.GetOrientation())
223262
if hasattr(d, "GetTextHeight"):
224263
height = d.GetTextHeight() * 1e-6
225264
width = d.GetTextWidth() * 1e-6
@@ -250,15 +289,47 @@ def parse_text(self, d):
250289
"angle": angle
251290
}
252291

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+
253316
def parse_drawing(self, d):
317+
# type: (pcbnew.BOARD_ITEM) -> list
318+
result = []
319+
s = None
254320
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())
258327
else:
259328
self.logger.info("Unsupported drawing class %s, skipping",
260329
d.GetClass())
261-
return None
330+
if s:
331+
result.append(s)
332+
return result
262333

263334
def parse_edges(self, pcb):
264335
edges = []
@@ -269,8 +340,7 @@ def parse_edges(self, pcb):
269340
drawings.append(g)
270341
for d in drawings:
271342
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):
274344
edges.append(parsed_drawing)
275345
if bbox is None:
276346
bbox = d.GetBoundingBox()
@@ -287,15 +357,13 @@ def parse_drawings_on_layers(self, drawings, f_layer, b_layer):
287357
for d in drawings:
288358
if d[1].GetLayer() not in [f_layer, b_layer]:
289359
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)
299367

300368
return {
301369
"F": front,
@@ -321,7 +389,7 @@ def parse_pad(self, pad):
321389
layers.append("B")
322390
pos = self.normalize(pad.GetPosition())
323391
size = self.normalize(pad.GetSize())
324-
angle = pad.GetOrientation() * -0.1
392+
angle = self.normalize_angle(pad.GetOrientation())
325393
shape_lookup = {
326394
pcbnew.PAD_SHAPE_RECT: "rect",
327395
pcbnew.PAD_SHAPE_OVAL: "oval",
@@ -401,8 +469,14 @@ def parse_footprints(self):
401469
f_copy = pcbnew.MODULE(f)
402470
else:
403471
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)
406480
if hasattr(f_copy, 'GetFootprintRect'):
407481
footprint_rect = f_copy.GetFootprintRect()
408482
else:
@@ -411,7 +485,7 @@ def parse_footprints(self):
411485
"pos": self.normalize(f.GetPosition()),
412486
"relpos": self.normalize(footprint_rect.GetPosition()),
413487
"size": self.normalize(footprint_rect.GetSize()),
414-
"angle": f.GetOrientation() * 0.1,
488+
"angle": self.normalize_angle(f.GetOrientation()),
415489
}
416490

417491
# graphical drawings
@@ -420,13 +494,11 @@ def parse_footprints(self):
420494
# we only care about copper ones, silkscreen is taken care of
421495
if d.GetLayer() not in [pcbnew.F_Cu, pcbnew.B_Cu]:
422496
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+
})
430502

431503
# footprint pads
432504
pads = []

0 commit comments

Comments
 (0)