Skip to content

Commit 7c64336

Browse files
committed
Kicad nightly features
Add support for trapezoid pads and non-filled shapes
1 parent 3bd6617 commit 7c64336

File tree

4 files changed

+66
-16
lines changed

4 files changed

+66
-16
lines changed

DATAFORMAT.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ attribute.
161161
"type": "circle",
162162
"start": [x, y],
163163
"radius": radius,
164+
// Optional boolean, defaults to 0
165+
"filled": 0,
166+
// Line width (only has effect for non-filled shapes)
164167
"width": width,
165168
}
166169
```
@@ -199,6 +202,10 @@ attribute.
199202
```js
200203
{
201204
"type": "polygon",
205+
// Optional, defaults to 1
206+
"filled": 1,
207+
// Line width (only has effect for non-filled shapes)
208+
"width": width
202209
// SVG path of the polygon given as 'd' attribute of svg spec.
203210
// If this parameter is specified everything below it is ignored.
204211
"svgpath": svgpath,

InteractiveHtmlBom/ecad/kicad.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,35 @@ def parse_shape(self, d):
8484
return None
8585
start = self.normalize(d.GetStart())
8686
end = self.normalize(d.GetEnd())
87-
if shape in ["segment", "rect"]:
87+
if shape == "segment" or shape == "rect" and not d.IsFilled():
8888
return {
8989
"type": shape,
9090
"start": start,
9191
"end": end,
9292
"width": d.GetWidth() * 1e-6
9393
}
94-
if shape == "circle":
94+
if shape == "rect" and d.IsFilled():
9595
return {
96+
"type": "polygon",
97+
"pos": start,
98+
"angle": 0,
99+
"polygons": [[
100+
[0, 0],
101+
[end[0] - start[0], 0],
102+
[end[0] - start[0], end[1] - start[1]],
103+
[0, end[1] - start[1]]
104+
]]
105+
}
106+
if shape == "circle":
107+
shape_dict = {
96108
"type": shape,
97109
"start": start,
98110
"radius": d.GetRadius() * 1e-6,
99111
"width": d.GetWidth() * 1e-6
100112
}
113+
if d.IsFilled():
114+
shape_dict["filled"] = 1
115+
return shape_dict
101116
if shape == "arc":
102117
a1 = round(d.GetArcAngleStart() * 0.1, 2)
103118
a2 = round((d.GetArcAngleStart() + d.GetAngle()) * 0.1, 2)
@@ -125,12 +140,16 @@ def parse_shape(self, d):
125140
parent_footprint = d.GetParentFootprint()
126141
if parent_footprint is not None:
127142
angle = parent_footprint.GetOrientation() * 0.1,
128-
return {
143+
shape_dict = {
129144
"type": shape,
130145
"pos": start,
131146
"angle": angle,
132147
"polygons": polygons
133148
}
149+
if not d.IsFilled():
150+
shape_dict["filled"] = 0
151+
shape_dict["width"] = d.GetWidth() * 1e-6
152+
return shape_dict
134153
if shape == "curve":
135154
return {
136155
"type": shape,
@@ -290,6 +309,8 @@ def parse_pad(self, pad):
290309
pcbnew.PAD_SHAPE_OVAL: "oval",
291310
pcbnew.PAD_SHAPE_CIRCLE: "circle",
292311
}
312+
if hasattr(pcbnew, "PAD_SHAPE_TRAPEZOID"):
313+
shape_lookup[pcbnew.PAD_SHAPE_TRAPEZOID] = "trapezoid"
293314
if hasattr(pcbnew, "PAD_SHAPE_ROUNDRECT"):
294315
shape_lookup[pcbnew.PAD_SHAPE_ROUNDRECT] = "roundrect"
295316
if hasattr(pcbnew, "PAD_SHAPE_CUSTOM"):
@@ -312,10 +333,18 @@ def parse_pad(self, pad):
312333
polygon_set = pad.GetCustomShapeAsPolygon()
313334
if polygon_set.HasHoles():
314335
self.logger.warn('Detected holes in custom pad polygons')
315-
if polygon_set.IsSelfIntersecting():
316-
self.logger.warn(
317-
'Detected self intersecting polygons in custom pad')
318336
pad_dict["polygons"] = self.parse_poly_set(polygon_set)
337+
if shape == "trapezoid":
338+
# treat trapezoid as custom shape
339+
pad_dict["shape"] = "custom"
340+
delta = self.normalize(pad.GetDelta())
341+
pad_dict["polygons"] = [[
342+
[size[0] / 2 + delta[1] / 2, size[1] / 2 - delta[0] / 2],
343+
[-size[0] / 2 - delta[1] / 2, size[1] / 2 + delta[0] / 2],
344+
[-size[0] / 2 + delta[1] / 2, -size[1] / 2 - delta[0] / 2],
345+
[size[0] / 2 - delta[1] / 2, -size[1] / 2 + delta[0] / 2],
346+
]]
347+
319348
if shape in ["roundrect", "chamfrect"]:
320349
pad_dict["radius"] = pad.GetRoundRectCornerRadius() * 1e-6
321350
if shape == "chamfrect":
@@ -498,8 +527,7 @@ def parse_zones(self, zones):
498527
def parse_netlist(net_info):
499528
# type: (pcbnew.NETINFO_LIST) -> list
500529
nets = net_info.NetsByName().asdict().keys()
501-
nets = [str(s) for s in nets]
502-
nets.sort()
530+
nets = sorted([str(s) for s in nets])
503531
return nets
504532

505533
@staticmethod

InteractiveHtmlBom/ecad/schema/genericjsonpcbdata_v1.schema

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@
367367
"type": { "const": "circle" },
368368
"start": { "$ref": "#/definitions/Coordinates" },
369369
"radius": { "type": "number" },
370+
"filled": { "type": "integer" },
370371
"width": { "type": "number" }
371372
},
372373
"required": ["type", "start", "radius", "width"],
@@ -413,6 +414,8 @@
413414
"additionalProperties": false,
414415
"properties": {
415416
"type": { "const": "polygon" },
417+
"filled": { "type": "integer" },
418+
"width": { "type": "number" },
416419
"svgpath": { "type": "string" },
417420
"pos": { "$ref": "#/definitions/Coordinates" },
418421
"angle": { "type": "number" },

InteractiveHtmlBom/web/render.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ function drawText(ctx, text, color) {
111111

112112
function drawedge(ctx, scalefactor, edge, color) {
113113
ctx.strokeStyle = color;
114+
ctx.fillStyle = color;
114115
ctx.lineWidth = Math.max(1 / scalefactor, edge.width);
115116
ctx.lineCap = "round";
116117
ctx.lineJoin = "round";
@@ -147,7 +148,10 @@ function drawedge(ctx, scalefactor, edge, color) {
147148
ctx.moveTo(...edge.start);
148149
ctx.bezierCurveTo(...edge.cpa, ...edge.cpb, ...edge.end);
149150
}
150-
ctx.stroke();
151+
if("filled" in edge && edge.filled)
152+
ctx.fill();
153+
else
154+
ctx.stroke();
151155
}
152156
}
153157

@@ -216,22 +220,30 @@ function getPolygonsPath(shape) {
216220
return shape.path2d;
217221
}
218222

219-
function drawPolygonShape(ctx, shape, color) {
223+
function drawPolygonShape(ctx, scalefactor, shape, color) {
220224
ctx.save();
221-
ctx.fillStyle = color;
222225
if (!("svgpath" in shape)) {
223226
ctx.translate(...shape.pos);
224227
ctx.rotate(deg2rad(-shape.angle));
225228
}
226-
ctx.fill(getPolygonsPath(shape));
229+
if("filled" in shape && !shape.filled) {
230+
ctx.strokeStyle = color;
231+
ctx.lineWidth = Math.max(1 / scalefactor, shape.width);
232+
ctx.lineCap = "round";
233+
ctx.lineJoin = "round";
234+
ctx.stroke(getPolygonsPath(shape));
235+
} else {
236+
ctx.fillStyle = color;
237+
ctx.fill(getPolygonsPath(shape));
238+
}
227239
ctx.restore();
228240
}
229241

230242
function drawDrawing(ctx, scalefactor, drawing, color) {
231243
if (["segment", "arc", "circle", "curve", "rect"].includes(drawing.type)) {
232244
drawedge(ctx, scalefactor, drawing, color);
233245
} else if (drawing.type == "polygon") {
234-
drawPolygonShape(ctx, drawing, color);
246+
drawPolygonShape(ctx, scalefactor, drawing, color);
235247
} else {
236248
drawText(ctx, drawing, color);
237249
}
@@ -340,7 +352,7 @@ function drawEdgeCuts(canvas, scalefactor) {
340352
var ctx = canvas.getContext("2d");
341353
var edgecolor = getComputedStyle(topmostdiv).getPropertyValue('--pcb-edge-color');
342354
for (var edge of pcbdata.edges) {
343-
drawedge(ctx, scalefactor, edge, edgecolor);
355+
drawDrawing(ctx, scalefactor, edge, edgecolor);
344356
}
345357
}
346358

@@ -384,7 +396,7 @@ function drawBgLayer(layername, canvas, layer, scalefactor, edgeColor, polygonCo
384396
if (["segment", "arc", "circle", "curve", "rect"].includes(d.type)) {
385397
drawedge(ctx, scalefactor, d, edgeColor);
386398
} else if (d.type == "polygon") {
387-
drawPolygonShape(ctx, d, polygonColor);
399+
drawPolygonShape(ctx, scalefactor, d, polygonColor);
388400
} else {
389401
drawText(ctx, d, textColor);
390402
}
@@ -508,7 +520,7 @@ function drawBackground(canvasdict, clear = true) {
508520
drawFootprints(canvasdict.bg, canvasdict.layer,
509521
canvasdict.transform.s * canvasdict.transform.zoom, false);
510522

511-
drawEdgeCuts(canvasdict.bg, canvasdict.transform.s);
523+
drawEdgeCuts(canvasdict.bg, canvasdict.transform.s * canvasdict.transform.zoom);
512524

513525
var style = getComputedStyle(topmostdiv);
514526
var edgeColor = style.getPropertyValue('--silkscreen-edge-color');

0 commit comments

Comments
 (0)