Generate stencils using KiKit

This commit is contained in:
Luca 2020-11-03 15:16:22 +01:00
parent 59236dea8a
commit cfeb057280
8 changed files with 272754 additions and 0 deletions

View File

@ -14,6 +14,16 @@ The following command was used to generate the 2x1 panel of anykeys in `panels/a
kikit panelize grid --tolerance 10 --space 3 --gridsize 1 2 --tabwidth 3 --tabheight 3 --htabs 0 --vtabs 1 --mousebites 0.3 0.6 -0.1 --radius 0.8 --railsTb 5 --fiducials 10 2.5 1 2 --tooling 5 2.5 1.5 --copperfill anykey.kicad_pcb panels/anykey-x2.kicad_pcb kikit panelize grid --tolerance 10 --space 3 --gridsize 1 2 --tabwidth 3 --tabheight 3 --htabs 0 --vtabs 1 --mousebites 0.3 0.6 -0.1 --radius 0.8 --railsTb 5 --fiducials 10 2.5 1 2 --tooling 5 2.5 1.5 --copperfill anykey.kicad_pcb panels/anykey-x2.kicad_pcb
``` ```
## Stencil
[KiKit](https://github.com/yaqwsx/KiKit) was used to generate the 3d-printable stencils in `stencil/`:
```sh
kikit stencil createprinted --pcbthickness 1.6 --thickness 0.15 --cutout 'C1,C2,C3,C4,C5,C6,C7,U1,D1,R1' panels/anykey-x2.kicad_pcb stencil/
```
Note that currently (2020-11-03) the `--cutout` option is only available on `master`.
## Documents ## Documents
### STM32 reference manual ### STM32 reference manual

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

12762
stencil/anykey-x2-PasteTop.dxf Normal file

File diff suppressed because it is too large Load Diff

559
stencil/bottomStencil.scad Normal file
View File

@ -0,0 +1,559 @@
// Generated by SolidPython 1.0.1 on 2020-11-03 14:20:42
$fa = 0.4; $fs = 0.4;
rotate(a = 0, v = [1, 0, 0]) {
difference() {
scale(v = [1, 1, 1]) {
difference() {
linear_extrude(height = 1.4500000000) {
offset(r = 1.0000000000) {
import(file = "/home/luca/projects/anykey/stencil/anykey-x2-EdgeCuts.dxf", origin = [0, 0]);
}
}
translate(v = [0, 0, 0.1500000000]) {
linear_extrude(height = 1.4500000000) {
translate(v = [0, 0, 0]) {
import(file = "/home/luca/projects/anykey/stencil/anykey-x2-EdgeCuts.dxf", origin = [0, 0]);
}
}
}
}
}
linear_extrude(center = true, height = 0.6000000000) {
translate(v = [0, 0, 0]) {
import(file = "/home/luca/projects/anykey/stencil/anykey-x2-PasteBottom.dxf", origin = [0, 0]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[69020000.0000000000, 66270000.0000000000], [69020000.0000000000, 67730000.0000000000], [71980000.0000000000, 67730000.0000000000], [71980000.0000000000, 66270000.0000000000], [69020000.0000000000, 66270000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[66874700.0000000000, 82510100.0000000000], [66210100.0000000000, 83174700.0000000000], [67525300.0000000000, 84490000.0000000000], [68190000.0000000000, 83825300.0000000000], [66874700.0000000000, 82510100.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[68825300.0000000000, 72410100.0000000000], [67510100.0000000000, 73725300.0000000000], [68174700.0000000000, 74390000.0000000000], [69490000.0000000000, 73074700.0000000000], [68825300.0000000000, 72410100.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[73174700.0000000000, 85490000.0000000000], [74490000.0000000000, 84174700.0000000000], [73825300.0000000000, 83510100.0000000000], [72510100.0000000000, 84825300.0000000000], [73174700.0000000000, 85490000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[71670000.0000000000, 64930000.0000000000], [71670000.0000000000, 63070000.0000000000], [70730000.0000000000, 63070000.0000000000], [70730000.0000000000, 64930000.0000000000], [71670000.0000000000, 64930000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[74070000.0000000000, 62530000.0000000000], [74070000.0000000000, 63470000.0000000000], [75930000.0000000000, 63470000.0000000000], [75930000.0000000000, 62530000.0000000000], [74070000.0000000000, 62530000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[67300000.0000000000, 62250000.0000000000], [67300000.0000000000, 65750000.0000000000], [70700000.0000000000, 65750000.0000000000], [70700000.0000000000, 62250000.0000000000], [67300000.0000000000, 62250000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[65470000.0000000000, 62330000.0000000000], [65470000.0000000000, 63270000.0000000000], [67330000.0000000000, 63270000.0000000000], [67330000.0000000000, 62330000.0000000000], [65470000.0000000000, 62330000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[76930000.0000000000, 74470000.0000000000], [76930000.0000000000, 73530000.0000000000], [75070000.0000000000, 73530000.0000000000], [75070000.0000000000, 74470000.0000000000], [76930000.0000000000, 74470000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[70000000.0000000000, 85000000.0000000000], [70000000.0000000000, 87000000.0000000000], [72000000.0000000000, 87000000.0000000000], [72000000.0000000000, 85000000.0000000000], [70000000.0000000000, 85000000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[54020000.0000000000, 66270000.0000000000], [54020000.0000000000, 67730000.0000000000], [56980000.0000000000, 67730000.0000000000], [56980000.0000000000, 66270000.0000000000], [54020000.0000000000, 66270000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[51874700.0000000000, 82510100.0000000000], [51210100.0000000000, 83174700.0000000000], [52525300.0000000000, 84490000.0000000000], [53190000.0000000000, 83825300.0000000000], [51874700.0000000000, 82510100.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[53825300.0000000000, 72410100.0000000000], [52510100.0000000000, 73725300.0000000000], [53174700.0000000000, 74390000.0000000000], [54490000.0000000000, 73074700.0000000000], [53825300.0000000000, 72410100.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[58174700.0000000000, 85490000.0000000000], [59490000.0000000000, 84174700.0000000000], [58825300.0000000000, 83510100.0000000000], [57510100.0000000000, 84825300.0000000000], [58174700.0000000000, 85490000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[56670000.0000000000, 64930000.0000000000], [56670000.0000000000, 63070000.0000000000], [55730000.0000000000, 63070000.0000000000], [55730000.0000000000, 64930000.0000000000], [56670000.0000000000, 64930000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[59070000.0000000000, 62530000.0000000000], [59070000.0000000000, 63470000.0000000000], [60930000.0000000000, 63470000.0000000000], [60930000.0000000000, 62530000.0000000000], [59070000.0000000000, 62530000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[52300000.0000000000, 62250000.0000000000], [52300000.0000000000, 65750000.0000000000], [55700000.0000000000, 65750000.0000000000], [55700000.0000000000, 62250000.0000000000], [52300000.0000000000, 62250000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[50470000.0000000000, 62330000.0000000000], [50470000.0000000000, 63270000.0000000000], [52330000.0000000000, 63270000.0000000000], [52330000.0000000000, 62330000.0000000000], [50470000.0000000000, 62330000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[61930000.0000000000, 74470000.0000000000], [61930000.0000000000, 73530000.0000000000], [60070000.0000000000, 73530000.0000000000], [60070000.0000000000, 74470000.0000000000], [61930000.0000000000, 74470000.0000000000]]);
}
}
scale(v = [0.0000010000, -0.0000010000, 1]) {
linear_extrude(center = true, height = 0.6000000000) {
polygon(paths = [[0, 1, 2, 3, 4]], points = [[55000000.0000000000, 85000000.0000000000], [55000000.0000000000, 87000000.0000000000], [57000000.0000000000, 87000000.0000000000], [57000000.0000000000, 85000000.0000000000], [55000000.0000000000, 85000000.0000000000]]);
}
}
}
}
/***********************************************
********* SolidPython code: **********
************************************************
import pcbnew
from pcbnew import wxPoint
import numpy as np
from kikit.common import *
from kikit.defs import *
from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad
from kikit.export import gerberImpl, pasteDxfExport
import solid
import solid.utils
import subprocess
from kikit.common import removeComponents, parseReferences
from shapely.geometry import Point
from kikit import pcbnew_compatibility
OUTER_BORDER = fromMm(7.5)
INNER_BORDER = fromMm(5)
MOUNTING_HOLES_COUNT = 3
MOUNTING_HOLE_R = fromMm(1)
HOLE_SPACING = fromMm(20)
def addBottomCounterpart(board, item):
item = item.Duplicate()
item.SetLayer(Layer.B_Paste)
board.Add(item)
def addRoundedCorner(board, center, start, end, thickness):
corner = pcbnew.PCB_SHAPE()
corner.SetShape(STROKE_T.S_ARC)
corner.SetCenter(wxPoint(center[0], center[1]))
corner.SetArcStart(wxPoint(start[0], start[1]))
if np.cross(start - center, end - center) > 0:
corner.SetAngle(fromDegrees(90))
else:
corner.SetAngle(fromDegrees(-90))
corner.SetWidth(thickness)
corner.SetLayer(Layer.F_Paste)
board.Add(corner)
addBottomCounterpart(board, corner)
def addLine(board, start, end, thickness):
line = pcbnew.PCB_SHAPE()
line.SetShape(STROKE_T.S_SEGMENT)
line.SetStart(wxPoint(start[0], start[1]))
line.SetEnd(wxPoint(end[0], end[1]))
line.SetWidth(thickness)
line.SetLayer(Layer.F_Paste)
board.Add(line)
addBottomCounterpart(board, line)
def addBite(board, origin, direction, normal, thickness):
"""
Adds a bite to the stencil, direction points to the bridge, normal points
inside the stencil
"""
direction = normalize(direction) * thickness
normal = normalize(normal) * thickness
center = wxPoint(origin[0], origin[1]) + wxPoint(normal[0], normal[1])
start = origin
end = center + wxPoint(direction[0], direction[1])
# addLine(board, end, end + normal / 2, thickness)
addRoundedCorner(board, center, start, end, thickness)
def numberOfCuts(length, bridgeWidth, bridgeSpacing):
"""
Return number of bridges which fit inside the length and cut length
"""
count = int(np.floor((length + bridgeWidth) / (bridgeWidth + bridgeSpacing)))
cutLength = (length - (count - 1) * bridgeWidth) / count
return count, cutLength
def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance):
"""
Add rectangular frame to the board
"""
R=fromMm(1)
corners = [
(tl(rect), wxPoint(R, 0), wxPoint(0, R)), # TL
(tr(rect), wxPoint(0, R), wxPoint(-R, 0)), # TR
(br(rect), wxPoint(-R, 0), wxPoint(0, -R)), # BR
(bl(rect), wxPoint(0, -R), wxPoint(R, 0)) # BL
]
for c, sOffset, eOffset in corners:
addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance)
count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing)
for i in range(count):
start = rect.GetX() + R + i * bridgeWidth + i * cutLength
end = start + cutLength
y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight()
addLine(board, wxPoint(start, y1), wxPoint(end, y1), clearance)
if i != 0:
addBite(board, wxPoint(start, y1), wxPoint(-1, 0), wxPoint(0, 1), clearance)
if i != count - 1:
addBite(board, wxPoint(end, y1), wxPoint(1, 0), wxPoint(0, 1), clearance)
addLine(board, wxPoint(start, y2), wxPoint(end, y2), clearance)
if i != 0:
addBite(board, wxPoint(start, y2), wxPoint(-1, 0), wxPoint(0, -1), clearance)
if i != count - 1:
addBite(board, wxPoint(end, y2), wxPoint(1, 0), wxPoint(0, -1), clearance)
count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing)
for i in range(count):
start = rect.GetY() + R + i * bridgeWidth + i * cutLength
end = start + cutLength
x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth()
addLine(board, wxPoint(x1, start), wxPoint(x1, end), clearance)
if i != 0:
addBite(board, wxPoint(x1, start), wxPoint(0, -1), wxPoint(1, 0), clearance)
if i != count - 1:
addBite(board, wxPoint(x1, end), wxPoint(0, 1), wxPoint(1, 0), clearance)
addLine(board, wxPoint(x2, start), wxPoint(x2, end), clearance)
if i != 0:
addBite(board, wxPoint(x2, start), wxPoint(0, -1), wxPoint(-1, 0), clearance)
if i != count - 1:
addBite(board, wxPoint(x2, end), wxPoint(0, 1), wxPoint(-1, 0), clearance)
def addHole(board, position, radius):
circle = pcbnew.PCB_SHAPE()
circle.SetShape(STROKE_T.S_CIRCLE)
circle.SetCenter(wxPoint(position[0], position[1]))
circle.SetArcStart(wxPoint(position[0], position[1]) + wxPoint(radius/2, 0))
circle.SetWidth(radius)
circle.SetLayer(Layer.F_Paste)
board.Add(circle)
addBottomCounterpart(board, circle)
def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2),
bridgeSpacing=fromMm(10), clearance=fromMm(0.5)):
"""
Given a Pcbnew board finds the board outline and creates a stencil for
KiKit's stencil jig.
Mainly, adds mounting holes and mouse bites to define the panel outline.
jigFrameSize is a tuple (width, height).
"""
bBox = findBoardBoundingBox(board)
frameSize = rectByCenter(rectCenter(bBox),
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER),
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER))
cutSize = rectByCenter(rectCenter(bBox),
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1),
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1))
addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance)
for i in range(MOUNTING_HOLES_COUNT):
x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
addHole(board, wxPoint(x, OUTER_BORDER / 2 + frameSize.GetY()), MOUNTING_HOLE_R)
addHole(board, wxPoint(x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight()), MOUNTING_HOLE_R)
for i in range(MOUNTING_HOLES_COUNT):
y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
addHole(board, wxPoint(OUTER_BORDER / 2 + frameSize.GetX(), y), MOUNTING_HOLE_R)
addHole(board, wxPoint(- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y), MOUNTING_HOLE_R)
PIN_TOLERANCE = fromMm(0.05)
addHole(board, tl(frameSize) + wxPoint(OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
addHole(board, tr(frameSize) + wxPoint(-OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
addHole(board, br(frameSize) + wxPoint(-OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
addHole(board, bl(frameSize) + wxPoint(OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
def jigMountingHoles(jigFrameSize, origin=wxPoint(0, 0)):
""" Get list of all mounting holes in a jig of given size """
w, h = jigFrameSize
holes = [
wxPoint(0, (w + INNER_BORDER) / 2),
wxPoint(0, -(w + INNER_BORDER) / 2),
wxPoint((h + INNER_BORDER) / 2, 0),
wxPoint(-(h + INNER_BORDER) / 2, 0),
]
return [x + origin for x in holes]
def createOuterPolygon(board, jigFrameSize, outerBorder):
bBox = findBoardBoundingBox(board)
centerpoint = rectCenter(bBox)
holes = jigMountingHoles(jigFrameSize, centerpoint)
outerSubstrate = Substrate(collectEdges(board, "Edge.Cuts"))
outerSubstrate.substrates = outerSubstrate.substrates.buffer(outerBorder)
tabs = []
for hole in holes:
tab, _ = outerSubstrate.tab(hole, centerpoint - hole, INNER_BORDER, fromMm(1000))
tabs.append(tab)
outerSubstrate.union(tabs)
outerSubstrate.union([Point(x).buffer(INNER_BORDER / 2) for x in holes])
outerSubstrate.millFillets(fromMm(3))
return outerSubstrate.exterior(), holes
def createOffsetPolygon(board, offset):
outerSubstrate = Substrate(collectEdges(board, "Edge.Cuts"))
outerSubstrate.substrates = outerSubstrate.substrates.buffer(offset)
return outerSubstrate.exterior()
def m2countersink():
HEAD_DIA = fromMm(4.5)
HOLE_LEN = fromMm(10)
SINK_EXTRA = fromMm(0.3)
sinkH = np.sqrt(HEAD_DIA**2 / 4)
sink = solid.cylinder(d1=0, d2=HEAD_DIA, h=sinkH)
sinkE = solid.cylinder(d=HEAD_DIA, h=SINK_EXTRA)
hole = solid.cylinder(h=HOLE_LEN, d=fromMm(2))
return sinkE + solid.utils.down(sinkH)(sink) + solid.utils.down(HOLE_LEN)(hole)
def mirrorX(linestring, origin):
return [(2 * origin - x, y) for x, y in linestring]
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder, innerBorder, tolerance, topSide):
bBox = findBoardBoundingBox(board)
centerpoint = rectCenter(bBox)
top = jigThickness - fromMm(0.15)
pcbBottom = jigThickness - pcbThickness
outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
outerRing = outerPolygon.exterior.coords
if topSide:
outerRing = mirrorX(outerRing, centerpoint[0])
body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(
outerRing))
innerRing = createOffsetPolygon(board, - innerBorder).exterior.coords
if topSide:
innerRing = mirrorX(innerRing, centerpoint[0])
innerCutout = solid.utils.down(jigThickness)(
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
registerRing = createOffsetPolygon(board, tolerance).exterior.coords
if topSide:
registerRing = mirrorX(registerRing, centerpoint[0])
registerCutout = solid.utils.up(jigThickness - pcbThickness)(
solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing)))
register = body - innerCutout - registerCutout
for hole in holes:
register = register - solid.translate([hole[0], hole[1], top])(m2countersink())
return solid.scale(toMm(1))(
solid.translate([-centerpoint[0], -centerpoint[1], 0])(register))
def makeTopRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder=fromMm(3), innerBorder=fromMm(1),
tolerance=fromMm(0.05)):
"""
Create a SolidPython representation of the top register
"""
print("Top")
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder, innerBorder, tolerance, True)
def makeBottomRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder=fromMm(3), innerBorder=fromMm(1),
tolerance=fromMm(0.05)):
"""
Create a SolidPython representation of the top register
"""
print("Bottom")
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder, innerBorder, tolerance, False)
def renderScad(infile, outfile):
infile = os.path.abspath(infile)
outfile = os.path.abspath(outfile)
subprocess.check_call(["openscad", "-o", outfile, infile])
def shapelyToSHAPE_POLY_SET(polygon):
p = pcbnew.SHAPE_POLY_SET()
print(polygon.exterior)
p.AddOutline(linestringToKicad(polygon.exterior))
return p
def cutoutComponents(board, components):
topCutout = extractComponentPolygons(components, "F.CrtYd")
for polygon in topCutout:
zone = pcbnew.PCB_SHAPE()
zone.SetShape(STROKE_T.S_POLYGON)
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
zone.SetLayer(Layer.F_Paste)
board.Add(zone)
bottomCutout = extractComponentPolygons(components, "B.CrtYd")
for polygon in bottomCutout:
zone = pcbnew.PCB_SHAPE()
zone.SetShape(STROKE_T.S_POLYGON)
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
zone.SetLayer(Layer.B_Paste)
board.Add(zone)
from pathlib import Path
import os
def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness,
registerborder, tolerance, ignore, cutout):
board = pcbnew.LoadBoard(inputboard)
refs = parseReferences(ignore)
removeComponents(board, refs)
Path(outputdir).mkdir(parents=True, exist_ok=True)
jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1]))
addJigFrame(board, jigsize)
cutoutComponents(board, getComponents(board, parseReferences(cutout)))
stencilFile = os.path.join(outputdir, "stencil.kicad_pcb")
board.Save(stencilFile)
plotPlan = [
# name, id, comment
("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
("PasteTop", pcbnew.F_Paste, "Paste top"),
]
gerberDir = os.path.join(outputdir, "gerber")
gerberImpl(stencilFile, gerberDir, plotPlan, False)
gerbers = [os.path.join(gerberDir, x) for x in os.listdir(gerberDir)]
subprocess.check_call(["zip", "-j",
os.path.join(outputdir, "gerbers.zip")] + gerbers)
jigthickness = fromMm(jigthickness)
pcbthickness = fromMm(pcbthickness)
outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1])
tolerance = fromMm(tolerance)
topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness,
outerBorder, innerBorder, tolerance)
bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness,
outerBorder, innerBorder, tolerance)
topRegisterFile = os.path.join(outputdir, "topRegister.scad")
solid.scad_render_to_file(topRegister, topRegisterFile)
renderScad(topRegisterFile, os.path.join(outputdir, "topRegister.stl"))
bottomRegisterFile = os.path.join(outputdir, "bottomRegister.scad")
solid.scad_render_to_file(bottomRegister, bottomRegisterFile)
renderScad(bottomRegisterFile, os.path.join(outputdir, "bottomRegister.stl"))
def printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance):
bodyOffset = solid.utils.up(0) if frameWidth + frameClearance == 0 else solid.offset(r=frameWidth + frameClearance)
body = solid.linear_extrude(height=thickness + frameHeight)(
bodyOffset(solid.import_dxf(outlineDxf)))
boardOffset = solid.utils.up(0) if frameClearance == 0 else solid.offset(r=frameClearance)
board = solid.utils.up(thickness)(
solid.linear_extrude(height=thickness + frameHeight)(
boardOffset(solid.import_dxf(outlineDxf))))
return body - board
def getComponents(board, references):
"""
Return a list of components based on designator
"""
return [m for m in board.GetModules() if m.GetReference() in references]
def collectModuleEdges(module, layerName):
"""
Return all edges on given layer in given module
"""
return [e for e in module.GraphicalItems() if e.GetLayerName() == layerName]
def extractComponentPolygons(modules, srcLayer):
"""
Return a list of shapely polygons with holes for already placed components.
The source layer defines the geometry on which the cutout is computed.
Usually it a font or back courtyard
"""
polygons = []
for m in modules:
edges = collectModuleEdges(m, srcLayer)
for ring in extractRings(edges):
polygons.append(toShapely(ring, edges))
return polygons
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
frameClearance, enlargeHoles, front):
zScale = -1 if front else 1
xRotate = 180 if front else 0
substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
thickness, frameHeight, frameWidth, frameClearance))
holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
holes = solid.linear_extrude(height=4*thickness, center=True)(
holesOffset(solid.import_dxf(holesDxf)))
substrate -= holes
for h in extraHoles:
substrate -= solid.scale([toMm(1), -toMm(1), 1])(
solid.linear_extrude(height=4*thickness, center=True)(
solid.polygon(h.exterior.coords)))
return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth,
ignore, cutout, frameclearance, enlargeholes):
"""
Create a 3D printed self-registering stencil.
"""
board = pcbnew.LoadBoard(inputboard)
refs = parseReferences(ignore)
cutoutComponents = getComponents(board, parseReferences(cutout))
removeComponents(board, refs)
Path(outputdir).mkdir(parents=True, exist_ok=True)
# We create the stencil based on DXF export. Using it avoids the necessity
# to interpret KiCAD PAD shapes which constantly change with newer and newer
# versions.
height = min(pcbthickness, max(0.5, pcbthickness - 0.3))
bottomPaste, topPaste, outline = pasteDxfExport(board, outputdir)
topCutout = extractComponentPolygons(cutoutComponents, "F.CrtYd")
bottomCutout = extractComponentPolygons(cutoutComponents, "B.CrtYd")
topStencil = printedStencil(outline, topPaste, topCutout, thickness, height,
framewidth, frameclearance, enlargeholes, True)
bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness,
height, framewidth, frameclearance, enlargeholes, False)
bottomStencilFile = os.path.join(outputdir, "bottomStencil.scad")
solid.scad_render_to_file(bottomStencil, bottomStencilFile,
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
renderScad(bottomStencilFile, os.path.join(outputdir, "bottomStencil.stl"))
topStencilFile = os.path.join(outputdir, "topStencil.scad")
solid.scad_render_to_file(topStencil, topStencilFile,
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
renderScad(topStencilFile, os.path.join(outputdir, "topStencil.stl"))
************************************************/

130482
stencil/bottomStencil.stl Normal file

File diff suppressed because it is too large Load Diff

459
stencil/topStencil.scad Normal file
View File

@ -0,0 +1,459 @@
// Generated by SolidPython 1.0.1 on 2020-11-03 14:22:51
$fa = 0.4; $fs = 0.4;
rotate(a = 180, v = [1, 0, 0]) {
difference() {
scale(v = [1, 1, -1]) {
difference() {
linear_extrude(height = 1.4500000000) {
offset(r = 1.0000000000) {
import(file = "/home/luca/projects/anykey/stencil/anykey-x2-EdgeCuts.dxf", origin = [0, 0]);
}
}
translate(v = [0, 0, 0.1500000000]) {
linear_extrude(height = 1.4500000000) {
translate(v = [0, 0, 0]) {
import(file = "/home/luca/projects/anykey/stencil/anykey-x2-EdgeCuts.dxf", origin = [0, 0]);
}
}
}
}
}
linear_extrude(center = true, height = 0.6000000000) {
translate(v = [0, 0, 0]) {
import(file = "/home/luca/projects/anykey/stencil/anykey-x2-PasteTop.dxf", origin = [0, 0]);
}
}
}
}
/***********************************************
********* SolidPython code: **********
************************************************
import pcbnew
from pcbnew import wxPoint
import numpy as np
from kikit.common import *
from kikit.defs import *
from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad
from kikit.export import gerberImpl, pasteDxfExport
import solid
import solid.utils
import subprocess
from kikit.common import removeComponents, parseReferences
from shapely.geometry import Point
from kikit import pcbnew_compatibility
OUTER_BORDER = fromMm(7.5)
INNER_BORDER = fromMm(5)
MOUNTING_HOLES_COUNT = 3
MOUNTING_HOLE_R = fromMm(1)
HOLE_SPACING = fromMm(20)
def addBottomCounterpart(board, item):
item = item.Duplicate()
item.SetLayer(Layer.B_Paste)
board.Add(item)
def addRoundedCorner(board, center, start, end, thickness):
corner = pcbnew.PCB_SHAPE()
corner.SetShape(STROKE_T.S_ARC)
corner.SetCenter(wxPoint(center[0], center[1]))
corner.SetArcStart(wxPoint(start[0], start[1]))
if np.cross(start - center, end - center) > 0:
corner.SetAngle(fromDegrees(90))
else:
corner.SetAngle(fromDegrees(-90))
corner.SetWidth(thickness)
corner.SetLayer(Layer.F_Paste)
board.Add(corner)
addBottomCounterpart(board, corner)
def addLine(board, start, end, thickness):
line = pcbnew.PCB_SHAPE()
line.SetShape(STROKE_T.S_SEGMENT)
line.SetStart(wxPoint(start[0], start[1]))
line.SetEnd(wxPoint(end[0], end[1]))
line.SetWidth(thickness)
line.SetLayer(Layer.F_Paste)
board.Add(line)
addBottomCounterpart(board, line)
def addBite(board, origin, direction, normal, thickness):
"""
Adds a bite to the stencil, direction points to the bridge, normal points
inside the stencil
"""
direction = normalize(direction) * thickness
normal = normalize(normal) * thickness
center = wxPoint(origin[0], origin[1]) + wxPoint(normal[0], normal[1])
start = origin
end = center + wxPoint(direction[0], direction[1])
# addLine(board, end, end + normal / 2, thickness)
addRoundedCorner(board, center, start, end, thickness)
def numberOfCuts(length, bridgeWidth, bridgeSpacing):
"""
Return number of bridges which fit inside the length and cut length
"""
count = int(np.floor((length + bridgeWidth) / (bridgeWidth + bridgeSpacing)))
cutLength = (length - (count - 1) * bridgeWidth) / count
return count, cutLength
def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance):
"""
Add rectangular frame to the board
"""
R=fromMm(1)
corners = [
(tl(rect), wxPoint(R, 0), wxPoint(0, R)), # TL
(tr(rect), wxPoint(0, R), wxPoint(-R, 0)), # TR
(br(rect), wxPoint(-R, 0), wxPoint(0, -R)), # BR
(bl(rect), wxPoint(0, -R), wxPoint(R, 0)) # BL
]
for c, sOffset, eOffset in corners:
addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance)
count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing)
for i in range(count):
start = rect.GetX() + R + i * bridgeWidth + i * cutLength
end = start + cutLength
y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight()
addLine(board, wxPoint(start, y1), wxPoint(end, y1), clearance)
if i != 0:
addBite(board, wxPoint(start, y1), wxPoint(-1, 0), wxPoint(0, 1), clearance)
if i != count - 1:
addBite(board, wxPoint(end, y1), wxPoint(1, 0), wxPoint(0, 1), clearance)
addLine(board, wxPoint(start, y2), wxPoint(end, y2), clearance)
if i != 0:
addBite(board, wxPoint(start, y2), wxPoint(-1, 0), wxPoint(0, -1), clearance)
if i != count - 1:
addBite(board, wxPoint(end, y2), wxPoint(1, 0), wxPoint(0, -1), clearance)
count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing)
for i in range(count):
start = rect.GetY() + R + i * bridgeWidth + i * cutLength
end = start + cutLength
x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth()
addLine(board, wxPoint(x1, start), wxPoint(x1, end), clearance)
if i != 0:
addBite(board, wxPoint(x1, start), wxPoint(0, -1), wxPoint(1, 0), clearance)
if i != count - 1:
addBite(board, wxPoint(x1, end), wxPoint(0, 1), wxPoint(1, 0), clearance)
addLine(board, wxPoint(x2, start), wxPoint(x2, end), clearance)
if i != 0:
addBite(board, wxPoint(x2, start), wxPoint(0, -1), wxPoint(-1, 0), clearance)
if i != count - 1:
addBite(board, wxPoint(x2, end), wxPoint(0, 1), wxPoint(-1, 0), clearance)
def addHole(board, position, radius):
circle = pcbnew.PCB_SHAPE()
circle.SetShape(STROKE_T.S_CIRCLE)
circle.SetCenter(wxPoint(position[0], position[1]))
circle.SetArcStart(wxPoint(position[0], position[1]) + wxPoint(radius/2, 0))
circle.SetWidth(radius)
circle.SetLayer(Layer.F_Paste)
board.Add(circle)
addBottomCounterpart(board, circle)
def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2),
bridgeSpacing=fromMm(10), clearance=fromMm(0.5)):
"""
Given a Pcbnew board finds the board outline and creates a stencil for
KiKit's stencil jig.
Mainly, adds mounting holes and mouse bites to define the panel outline.
jigFrameSize is a tuple (width, height).
"""
bBox = findBoardBoundingBox(board)
frameSize = rectByCenter(rectCenter(bBox),
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER),
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER))
cutSize = rectByCenter(rectCenter(bBox),
jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1),
jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1))
addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance)
for i in range(MOUNTING_HOLES_COUNT):
x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
addHole(board, wxPoint(x, OUTER_BORDER / 2 + frameSize.GetY()), MOUNTING_HOLE_R)
addHole(board, wxPoint(x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight()), MOUNTING_HOLE_R)
for i in range(MOUNTING_HOLES_COUNT):
y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1)
addHole(board, wxPoint(OUTER_BORDER / 2 + frameSize.GetX(), y), MOUNTING_HOLE_R)
addHole(board, wxPoint(- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y), MOUNTING_HOLE_R)
PIN_TOLERANCE = fromMm(0.05)
addHole(board, tl(frameSize) + wxPoint(OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
addHole(board, tr(frameSize) + wxPoint(-OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
addHole(board, br(frameSize) + wxPoint(-OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
addHole(board, bl(frameSize) + wxPoint(OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE)
def jigMountingHoles(jigFrameSize, origin=wxPoint(0, 0)):
""" Get list of all mounting holes in a jig of given size """
w, h = jigFrameSize
holes = [
wxPoint(0, (w + INNER_BORDER) / 2),
wxPoint(0, -(w + INNER_BORDER) / 2),
wxPoint((h + INNER_BORDER) / 2, 0),
wxPoint(-(h + INNER_BORDER) / 2, 0),
]
return [x + origin for x in holes]
def createOuterPolygon(board, jigFrameSize, outerBorder):
bBox = findBoardBoundingBox(board)
centerpoint = rectCenter(bBox)
holes = jigMountingHoles(jigFrameSize, centerpoint)
outerSubstrate = Substrate(collectEdges(board, "Edge.Cuts"))
outerSubstrate.substrates = outerSubstrate.substrates.buffer(outerBorder)
tabs = []
for hole in holes:
tab, _ = outerSubstrate.tab(hole, centerpoint - hole, INNER_BORDER, fromMm(1000))
tabs.append(tab)
outerSubstrate.union(tabs)
outerSubstrate.union([Point(x).buffer(INNER_BORDER / 2) for x in holes])
outerSubstrate.millFillets(fromMm(3))
return outerSubstrate.exterior(), holes
def createOffsetPolygon(board, offset):
outerSubstrate = Substrate(collectEdges(board, "Edge.Cuts"))
outerSubstrate.substrates = outerSubstrate.substrates.buffer(offset)
return outerSubstrate.exterior()
def m2countersink():
HEAD_DIA = fromMm(4.5)
HOLE_LEN = fromMm(10)
SINK_EXTRA = fromMm(0.3)
sinkH = np.sqrt(HEAD_DIA**2 / 4)
sink = solid.cylinder(d1=0, d2=HEAD_DIA, h=sinkH)
sinkE = solid.cylinder(d=HEAD_DIA, h=SINK_EXTRA)
hole = solid.cylinder(h=HOLE_LEN, d=fromMm(2))
return sinkE + solid.utils.down(sinkH)(sink) + solid.utils.down(HOLE_LEN)(hole)
def mirrorX(linestring, origin):
return [(2 * origin - x, y) for x, y in linestring]
def makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder, innerBorder, tolerance, topSide):
bBox = findBoardBoundingBox(board)
centerpoint = rectCenter(bBox)
top = jigThickness - fromMm(0.15)
pcbBottom = jigThickness - pcbThickness
outerPolygon, holes = createOuterPolygon(board, jigFrameSize, outerBorder)
outerRing = outerPolygon.exterior.coords
if topSide:
outerRing = mirrorX(outerRing, centerpoint[0])
body = solid.linear_extrude(height=top, convexity=10)(solid.polygon(
outerRing))
innerRing = createOffsetPolygon(board, - innerBorder).exterior.coords
if topSide:
innerRing = mirrorX(innerRing, centerpoint[0])
innerCutout = solid.utils.down(jigThickness)(
solid.linear_extrude(height=3 * jigThickness, convexity=10)(solid.polygon(innerRing)))
registerRing = createOffsetPolygon(board, tolerance).exterior.coords
if topSide:
registerRing = mirrorX(registerRing, centerpoint[0])
registerCutout = solid.utils.up(jigThickness - pcbThickness)(
solid.linear_extrude(height=jigThickness, convexity=10)(solid.polygon(registerRing)))
register = body - innerCutout - registerCutout
for hole in holes:
register = register - solid.translate([hole[0], hole[1], top])(m2countersink())
return solid.scale(toMm(1))(
solid.translate([-centerpoint[0], -centerpoint[1], 0])(register))
def makeTopRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder=fromMm(3), innerBorder=fromMm(1),
tolerance=fromMm(0.05)):
"""
Create a SolidPython representation of the top register
"""
print("Top")
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder, innerBorder, tolerance, True)
def makeBottomRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder=fromMm(3), innerBorder=fromMm(1),
tolerance=fromMm(0.05)):
"""
Create a SolidPython representation of the top register
"""
print("Bottom")
return makeRegister(board, jigFrameSize, jigThickness, pcbThickness,
outerBorder, innerBorder, tolerance, False)
def renderScad(infile, outfile):
infile = os.path.abspath(infile)
outfile = os.path.abspath(outfile)
subprocess.check_call(["openscad", "-o", outfile, infile])
def shapelyToSHAPE_POLY_SET(polygon):
p = pcbnew.SHAPE_POLY_SET()
print(polygon.exterior)
p.AddOutline(linestringToKicad(polygon.exterior))
return p
def cutoutComponents(board, components):
topCutout = extractComponentPolygons(components, "F.CrtYd")
for polygon in topCutout:
zone = pcbnew.PCB_SHAPE()
zone.SetShape(STROKE_T.S_POLYGON)
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
zone.SetLayer(Layer.F_Paste)
board.Add(zone)
bottomCutout = extractComponentPolygons(components, "B.CrtYd")
for polygon in bottomCutout:
zone = pcbnew.PCB_SHAPE()
zone.SetShape(STROKE_T.S_POLYGON)
zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon))
zone.SetLayer(Layer.B_Paste)
board.Add(zone)
from pathlib import Path
import os
def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness,
registerborder, tolerance, ignore, cutout):
board = pcbnew.LoadBoard(inputboard)
refs = parseReferences(ignore)
removeComponents(board, refs)
Path(outputdir).mkdir(parents=True, exist_ok=True)
jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1]))
addJigFrame(board, jigsize)
cutoutComponents(board, getComponents(board, parseReferences(cutout)))
stencilFile = os.path.join(outputdir, "stencil.kicad_pcb")
board.Save(stencilFile)
plotPlan = [
# name, id, comment
("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
("PasteTop", pcbnew.F_Paste, "Paste top"),
]
gerberDir = os.path.join(outputdir, "gerber")
gerberImpl(stencilFile, gerberDir, plotPlan, False)
gerbers = [os.path.join(gerberDir, x) for x in os.listdir(gerberDir)]
subprocess.check_call(["zip", "-j",
os.path.join(outputdir, "gerbers.zip")] + gerbers)
jigthickness = fromMm(jigthickness)
pcbthickness = fromMm(pcbthickness)
outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1])
tolerance = fromMm(tolerance)
topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness,
outerBorder, innerBorder, tolerance)
bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness,
outerBorder, innerBorder, tolerance)
topRegisterFile = os.path.join(outputdir, "topRegister.scad")
solid.scad_render_to_file(topRegister, topRegisterFile)
renderScad(topRegisterFile, os.path.join(outputdir, "topRegister.stl"))
bottomRegisterFile = os.path.join(outputdir, "bottomRegister.scad")
solid.scad_render_to_file(bottomRegister, bottomRegisterFile)
renderScad(bottomRegisterFile, os.path.join(outputdir, "bottomRegister.stl"))
def printedStencilSubstrate(outlineDxf, thickness, frameHeight, frameWidth, frameClearance):
bodyOffset = solid.utils.up(0) if frameWidth + frameClearance == 0 else solid.offset(r=frameWidth + frameClearance)
body = solid.linear_extrude(height=thickness + frameHeight)(
bodyOffset(solid.import_dxf(outlineDxf)))
boardOffset = solid.utils.up(0) if frameClearance == 0 else solid.offset(r=frameClearance)
board = solid.utils.up(thickness)(
solid.linear_extrude(height=thickness + frameHeight)(
boardOffset(solid.import_dxf(outlineDxf))))
return body - board
def getComponents(board, references):
"""
Return a list of components based on designator
"""
return [m for m in board.GetModules() if m.GetReference() in references]
def collectModuleEdges(module, layerName):
"""
Return all edges on given layer in given module
"""
return [e for e in module.GraphicalItems() if e.GetLayerName() == layerName]
def extractComponentPolygons(modules, srcLayer):
"""
Return a list of shapely polygons with holes for already placed components.
The source layer defines the geometry on which the cutout is computed.
Usually it a font or back courtyard
"""
polygons = []
for m in modules:
edges = collectModuleEdges(m, srcLayer)
for ring in extractRings(edges):
polygons.append(toShapely(ring, edges))
return polygons
def printedStencil(outlineDxf, holesDxf, extraHoles, thickness, frameHeight, frameWidth,
frameClearance, enlargeHoles, front):
zScale = -1 if front else 1
xRotate = 180 if front else 0
substrate = solid.scale([1, 1, zScale])(printedStencilSubstrate(outlineDxf,
thickness, frameHeight, frameWidth, frameClearance))
holesOffset = solid.utils.up(0) if enlargeHoles == 0 else solid.offset(delta=enlargeHoles)
holes = solid.linear_extrude(height=4*thickness, center=True)(
holesOffset(solid.import_dxf(holesDxf)))
substrate -= holes
for h in extraHoles:
substrate -= solid.scale([toMm(1), -toMm(1), 1])(
solid.linear_extrude(height=4*thickness, center=True)(
solid.polygon(h.exterior.coords)))
return solid.rotate(a=xRotate, v=[1, 0, 0])(substrate)
def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth,
ignore, cutout, frameclearance, enlargeholes):
"""
Create a 3D printed self-registering stencil.
"""
board = pcbnew.LoadBoard(inputboard)
refs = parseReferences(ignore)
cutoutComponents = getComponents(board, parseReferences(cutout))
removeComponents(board, refs)
Path(outputdir).mkdir(parents=True, exist_ok=True)
# We create the stencil based on DXF export. Using it avoids the necessity
# to interpret KiCAD PAD shapes which constantly change with newer and newer
# versions.
height = min(pcbthickness, max(0.5, pcbthickness - 0.3))
bottomPaste, topPaste, outline = pasteDxfExport(board, outputdir)
topCutout = extractComponentPolygons(cutoutComponents, "F.CrtYd")
bottomCutout = extractComponentPolygons(cutoutComponents, "B.CrtYd")
topStencil = printedStencil(outline, topPaste, topCutout, thickness, height,
framewidth, frameclearance, enlargeholes, True)
bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness,
height, framewidth, frameclearance, enlargeholes, False)
bottomStencilFile = os.path.join(outputdir, "bottomStencil.scad")
solid.scad_render_to_file(bottomStencil, bottomStencilFile,
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
renderScad(bottomStencilFile, os.path.join(outputdir, "bottomStencil.stl"))
topStencilFile = os.path.join(outputdir, "topStencil.scad")
solid.scad_render_to_file(topStencil, topStencilFile,
file_header=f'$fa = 0.4; $fs = 0.4;', include_orig_code=True)
renderScad(topStencilFile, os.path.join(outputdir, "topStencil.stl"))
************************************************/

54266
stencil/topStencil.stl Normal file

File diff suppressed because it is too large Load Diff