More PCB progress

This commit is contained in:
Bán Dénes 2020-07-14 23:03:44 +02:00
parent 05a33d00ec
commit 55d60ba599
9 changed files with 231 additions and 100 deletions

View file

@ -115,6 +115,7 @@ points:
`anchors` are used to, well, anchor the zone to something. `anchors` are used to, well, anchor the zone to something.
It's the `[0, 0]` origin with a 0 degree orientation by default, but it can be changed to any other pre-existing point.(Consequently, the first zone can't use a ref, because there isn't any yet.) It's the `[0, 0]` origin with a 0 degree orientation by default, but it can be changed to any other pre-existing point.(Consequently, the first zone can't use a ref, because there isn't any yet.)
The `ref` field can also be an array of references, in which case their average is used -- mostly useful for anchoring to the center, by averaging a key and its mirror; see later.
This initial position can then be changed with the `rotate` and `shift` options, adding extra rotation and translation, respectively. This initial position can then be changed with the `rotate` and `shift` options, adding extra rotation and translation, respectively.
Once we know _where_ to start, we can describe the `columns` of our layout. Once we know _where_ to start, we can describe the `columns` of our layout.
@ -184,6 +185,7 @@ rotate: num # default = 0
padding: num # default = 19 padding: num # default = 19
skip: boolean # default = false skip: boolean # default = false
asym: left | right | both # default = both asym: left | right | both # default = both
mirror: <arbitrary key-level data>
``` ```
`name` is the unique identifier of this specific key. `name` is the unique identifier of this specific key.
@ -192,7 +194,7 @@ It defaults to a `<row>_<column>` format, but can be overridden if necessary.
Then we leave `padding` amount of vertical space before moving on to the next key in the column. Then we leave `padding` amount of vertical space before moving on to the next key in the column.
`skip` signals that the point is just a "helper" and should not be included in the output. `skip` signals that the point is just a "helper" and should not be included in the output.
This can happen when a _real_ point is more easily calculable through a "stepping stone", but then we don't actually want the stepping stone to be a key itself. This can happen when a _real_ point is more easily calculable through a "stepping stone", but then we don't actually want the stepping stone to be a key itself.
Finally, `asym` relates to mirroring, which we'll cover in a second. Finally, `asym` and `mirror` relate to mirroring, which we'll cover in a second.
<hr /> <hr />
@ -221,6 +223,9 @@ If it's set as `left`, mirroring will simply skip this key.
If it's `right`, mirroring will "move" the point instead of copying it. If it's `right`, mirroring will "move" the point instead of copying it.
The default `both` assumes symmetry. The default `both` assumes symmetry.
Using the _key-level_ `mirror` key (not to be confused with the global `mirror` setting we just discussed above), we can set additional data for the mirrored version of the key.
It will use the same extension mechanism as it did for the 5 levels before.
And this concludes point definitions. And this concludes point definitions.
This should be generic enough to describe any ergo layout, yet easy enough so that you'll appreciate not having to work in raw CAD. This should be generic enough to describe any ergo layout, yet easy enough so that you'll appreciate not having to work in raw CAD.
@ -575,9 +580,8 @@ Footprints can be specified at the key-level (under the `points` section, like w
The differences between the two footprint types are: The differences between the two footprint types are:
- an omitted `ref` in the anchor means the current key for key-level declarations, while here it defaults to `[0, 0]` - an omitted `ref` in the anchor means the current key for key-level declarations, while here it defaults to `[0, 0]`
- a parameter starting with an exclamation point is an indirect reference to an eponymous key-level attribute -- so, for example, `from = !col_wire` would mean that the key's `col_wire` attribute is read there. - a parameter starting with an exclamation point is an indirect reference to an eponymous key-level attribute -- so, for example, `from = !column_net` would mean that the key's `column_net` attribute is read there.
Another alternative to `anchor` here (here being under the `pcb` declaration) is to use the `between` key and place the footprint at the average of multiple anchors -- mostly useful for anchoring to the center, by averaging a key and its mirror.
Additionally, the edge cut of the PCB can be specified using a previously defined outline name under the `edge` key. Additionally, the edge cut of the PCB can be specified using a previously defined outline name under the `edge` key.
```yaml ```yaml
@ -586,36 +590,44 @@ pcb:
footprints: footprints:
- type: <footprint type> - type: <footprint type>
anchor: <anchor declaration> anchor: <anchor declaration>
between: nets: <type-specific net params>
- <anchor declaration> params: <type-specific (non-net) footprint params>
- ...
params: <type-specific footprint params>
- ... - ...
``` ```
Currently, the following footprint types are supported: Currently, the following footprint types are supported:
- **`mx`**, **`alps`**, **`choc`**: mechanical switch footprints. Common parameters: - **`mx`**, **`alps`**, **`choc`**: mechanical switch footprints. Common nets:
- `from`, `to`: nets to connect - `from`, `to`: nets to connect
- **`diode`**: a combined THT+SMD diode footprint. Parameters: - **`diode`**: a combined THT+SMD diode footprint. Nets:
- `from`, `to`: nets to connect - `from`, `to`: nets to connect
- **`promicro`**: a controller to drive the keyboard. Available pins are `RAW`, `VCC`, `GND`, `RST`, and 18 GPIOs `P01` through `P18`. No parameters. - **`promicro`**: a controller to drive the keyboard. Available pins are `RAW`, `VCC`, `GND`, `RST`, and 18 GPIOs `P01` through `P18`. No Nets.
- **`slider`**: an SMD slider switch (part no. here), ideal for on/off operation. Parameters: - **`slider`**: an SMD slider switch (part no. here), ideal for on/off operation. Nets:
- `from`, `to`: nets to connect - `from`, `to`: nets to connect
- **`button`**: an SMD button (part no. here), ideal for momentary toggles (like a reset switch). Parameters: - **`button`**: an SMD button (part no. here), ideal for momentary toggles (like a reset switch). Nets:
- `from`, `to`: nets to connect - `from`, `to`: nets to connect
- **`rgb`**: an RGB led (part no. here), for per-key illumination, underglow, or feedback. Parameters: - **`rgb`**: an RGB led (part no. here), for per-key illumination, underglow, or feedback. Nets:
- `din`, `dout`: input and output nets of the data line - `din`, `dout`: input and output nets of the data line
- VCC and GND nets are assumed to be called `VCC` and `GND`... - VCC and GND nets are assumed to be called `VCC` and `GND`...
- **`jstph`**: a two-pin JST-PH battery header footprint. Parameters: - **`jstph`**: a two-pin JST-PH battery header footprint. Nets:
- `pos`, `neg`: nets to connect to the positive and negative terminals, respectively. - `pos`, `neg`: nets to connect to the positive and negative terminals, respectively.
- **`pin`**: a single pin.
- Nets:
- `net`: the net it should connect to
- Parameters:
- `diameter`: the larger diameter of the hole, including the copper ring
- `drill`: the smaller diameter of the actual hole
- **`hole`**: a simple circular hole. Parameters:
- `diameter`: the diameter of the (non-plated!) hole

View file

@ -52,17 +52,38 @@ exports.trbl = (raw, name) => {
exports.anchor = (raw, name, points={}, check_unexpected=true, default_point=new Point()) => { exports.anchor = (raw, name, points={}, check_unexpected=true, default_point=new Point()) => {
if (check_unexpected) detect_unexpected(raw, name, ['ref', 'shift', 'rotate']) if (check_unexpected) detect_unexpected(raw, name, ['ref', 'shift', 'rotate'])
let a = default_point.clone() let point = default_point.clone()
if (raw.ref !== undefined) { if (raw.ref !== undefined) {
assert(points[raw.ref], `Unknown point reference "${raw.ref}" in anchor "${name}"!`) if (type(raw.ref) == 'array') {
a = points[raw.ref].clone() // averaging multiple anchors
let x = 0, y = 0, r = 0
const len = raw.ref.length
for (const ref of raw.ref) {
assert(points[ref], `Unknown point reference "${ref}" in anchor "${name}"!`)
const resolved = points[ref]
x += resolved.x
y += resolved.y
r += resolved.r
}
point = new Point(x / len, y / len, r / len)
} else {
assert(points[raw.ref], `Unknown point reference "${raw.ref}" in anchor "${name}"!`)
point = points[raw.ref].clone()
}
} }
if (raw.shift !== undefined) { if (raw.shift !== undefined) {
let xyval = wh(raw.shift || [0, 0], name + '.shift') let xyval = wh(raw.shift || [0, 0], name + '.shift')
a.shift(xyval, true) if (point.meta.mirrored) {
xyval[0] = -xyval[0]
}
point.shift(xyval, true)
} }
if (raw.rotate !== undefined) { if (raw.rotate !== undefined) {
a.r += sane(raw.rotate || 0, name + '.rotate', 'number') let rot = sane(raw.rotate || 0, name + '.rotate', 'number')
if (point.meta.mirrored) {
rot = -rot
}
point.r += rot
} }
return a return point
} }

43
src/footprints/diode.js Normal file
View file

@ -0,0 +1,43 @@
module.exports = {
nets: ['from', 'to'],
body: `
(module ComboDiode (layer F.Cu) (tedit 5B24D78E)
${''/* parametric position */}
__AT
${''/* diode symbols */}
(fp_line (start 0.25 0) (end 0.75 0) (layer F.SilkS) (width 0.1))
(fp_line (start 0.25 0.4) (end -0.35 0) (layer F.SilkS) (width 0.1))
(fp_line (start 0.25 -0.4) (end 0.25 0.4) (layer F.SilkS) (width 0.1))
(fp_line (start -0.35 0) (end 0.25 -0.4) (layer F.SilkS) (width 0.1))
(fp_line (start -0.35 0) (end -0.35 0.55) (layer F.SilkS) (width 0.1))
(fp_line (start -0.35 0) (end -0.35 -0.55) (layer F.SilkS) (width 0.1))
(fp_line (start -0.75 0) (end -0.35 0) (layer F.SilkS) (width 0.1))
(fp_line (start 0.25 0) (end 0.75 0) (layer B.SilkS) (width 0.1))
(fp_line (start 0.25 0.4) (end -0.35 0) (layer B.SilkS) (width 0.1))
(fp_line (start 0.25 -0.4) (end 0.25 0.4) (layer B.SilkS) (width 0.1))
(fp_line (start -0.35 0) (end 0.25 -0.4) (layer B.SilkS) (width 0.1))
(fp_line (start -0.35 0) (end -0.35 0.55) (layer B.SilkS) (width 0.1))
(fp_line (start -0.35 0) (end -0.35 -0.55) (layer B.SilkS) (width 0.1))
(fp_line (start -0.75 0) (end -0.35 0) (layer B.SilkS) (width 0.1))
${''/* extra direction bars */}
(fp_line (start -2.5 -0.9) (end -2.5 0.9) (layer F.SilkS) (width 0.12))
(fp_line (start -2.5 -0.9) (end -2.5 0.9) (layer B.SilkS) (width 0.12))
${''/* SMD pads on both sides */}
(pad 1 smd rect (at -1.65 0 __ROT(0)) (size 0.9 1.2) (layers F.Cu F.Paste F.Mask) __NET_TO)
(pad 2 smd rect (at 1.65 0 __ROT(0)) (size 0.9 1.2) (layers B.Cu B.Paste B.Mask) __NET_FROM)
(pad 1 smd rect (at -1.65 0 __ROT(0)) (size 0.9 1.2) (layers B.Cu B.Paste B.Mask) __NET_TO)
(pad 2 smd rect (at 1.65 0 __ROT(0)) (size 0.9 1.2) (layers F.Cu F.Paste F.Mask) __NET_FROM)
${''/* THT terminals */}
(pad 1 thru_hole rect (at -3.81 0 __ROT(0)) (size 1.778 1.778) (drill 0.9906) (layers *.Cu *.Mask) __NET_TO)
(pad 2 thru_hole circle (at 3.81 0 __ROT(0)) (size 1.905 1.905) (drill 0.9906) (layers *.Cu *.Mask) __NET_FROM)
)
`
}

View file

@ -1,4 +1,5 @@
module.exports = { module.exports = {
mx: require('./mx'), mx: require('./mx'),
diode: require('./diode'),
promicro: require('./promicro') promicro: require('./promicro')
} }

View file

@ -1,5 +1,5 @@
module.exports = { module.exports = {
params: ['from', 'to'], nets: ['from', 'to'],
body: ` body: `
(module MX (layer F.Cu) (tedit 5DD4F656) (module MX (layer F.Cu) (tedit 5DD4F656)
@ -18,8 +18,8 @@ module.exports = {
(fp_line (start 7 -7) (end 7 -6) (layer F.SilkS) (width 0.15)) (fp_line (start 7 -7) (end 7 -6) (layer F.SilkS) (width 0.15))
${''/* pins */} ${''/* pins */}
(pad 1 thru_hole circle (at 2.54 -5.08) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) __PARAM_FROM) (pad 1 thru_hole circle (at 2.54 -5.08) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) __NET_FROM)
(pad 2 thru_hole circle (at -3.81 -2.54) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) __PARAM_TO) (pad 2 thru_hole circle (at -3.81 -2.54) (size 2.286 2.286) (drill 1.4986) (layers *.Cu *.Mask) __NET_TO)
${''/* middle shaft */} ${''/* middle shaft */}
(pad "" np_thru_hole circle (at 0 0) (size 3.9878 3.9878) (drill 3.9878) (layers *.Cu *.Mask)) (pad "" np_thru_hole circle (at 0 0) (size 3.9878 3.9878) (drill 3.9878) (layers *.Cu *.Mask))

View file

@ -1,5 +1,5 @@
module.exports = { module.exports = {
nets: [ static_nets: [
'RAW', 'GND', 'RST', 'VCC', 'RAW', 'GND', 'RST', 'VCC',
'P21', 'P20', 'P19', 'P18', 'P21', 'P20', 'P19', 'P18',
'P15', 'P14', 'P16', 'P10', 'P15', 'P14', 'P16', 'P10',
@ -31,34 +31,34 @@ module.exports = {
(fp_line (start -12.7 6.35) (end -12.7 8.89) (layer F.SilkS) (width 0.381)) (fp_line (start -12.7 6.35) (end -12.7 8.89) (layer F.SilkS) (width 0.381))
${''/* pin names */} ${''/* pin names */}
(fp_text user RAW (at -13.97 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user RAW (at -13.97 5.0 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -11.43 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user GND (at -11.43 5.0 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user RST (at -8.89 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user RST (at -8.89 5.0 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user VCC (at -6.35 5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user VCC (at -6.35 5.0 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 21 (at -3.81 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 21 (at -3.81 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 20 (at -1.27 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 20 (at -1.27 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 19 (at 1.27 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 19 (at 1.27 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 18 (at 3.81 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 18 (at 3.81 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 15 (at 6.35 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 15 (at 6.35 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 14 (at 8.89 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 14 (at 8.89 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 16 (at 11.43 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 16 (at 11.43 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 10 (at 13.97 5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 10 (at 13.97 5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 1 (at -13.97 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 1 (at -13.97 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 0 (at -11.43 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 0 (at -11.43 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -8.89 -5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user GND (at -8.89 -5.0 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -6.35 -5.0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user GND (at -6.35 -5.0 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 2 (at -3.81 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 2 (at -3.81 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 3 (at -1.27 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 3 (at -1.27 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 4 (at 1.27 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 4 (at 1.27 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 5 (at 3.81 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 5 (at 3.81 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 6 (at 6.35 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 6 (at 6.35 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 7 (at 8.89 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 7 (at 8.89 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 8 (at 11.43 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 8 (at 11.43 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user 9 (at 13.97 -5.461) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15)))) (fp_text user 9 (at 13.97 -5.461 __ROT(90)) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
${''/* and now the actual pins */} ${''/* and now the actual pins */}
(pad 1 thru_hole rect (at -13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_RAW) (pad 1 thru_hole rect (at -13.97 7.62 __ROT(0)) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_RAW)
(pad 2 thru_hole circle (at -11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_GND) (pad 2 thru_hole circle (at -11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_GND)
(pad 3 thru_hole circle (at -8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_RST) (pad 3 thru_hole circle (at -8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_RST)
(pad 4 thru_hole circle (at -6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_VCC) (pad 4 thru_hole circle (at -6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) __NET_VCC)

View file

@ -116,7 +116,7 @@ const kicad_netclass = `
const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => { const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => {
const grs = [] const grs = []
const xy = val => `${val[0]} ${val[1]}` const xy = val => `${val[0]} ${-val[1]}`
m.model.walk(model, { m.model.walk(model, {
onPath: wp => { onPath: wp => {
const p = wp.pathContext const p = wp.pathContext
@ -129,7 +129,7 @@ const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => {
const angle_start = p.startAngle > p.endAngle ? p.startAngle - 360 : p.startAngle const angle_start = p.startAngle > p.endAngle ? p.startAngle - 360 : p.startAngle
const angle_diff = Math.abs(p.endAngle - angle_start) const angle_diff = Math.abs(p.endAngle - angle_start)
const end = m.point.rotate(m.point.add(center, [p.radius, 0]), angle_start, center) const end = m.point.rotate(m.point.add(center, [p.radius, 0]), angle_start, center)
grs.push(`(gr_arc (start ${xy(center)}) (end ${xy(end)}) (angle ${angle_diff}) (layer ${layer}) (width 0.15))`) grs.push(`(gr_arc (start ${xy(center)}) (end ${xy(end)}) (angle ${-angle_diff}) (layer ${layer}) (width 0.15))`)
break break
case 'circle': case 'circle':
break break
@ -138,52 +138,63 @@ const makerjs2kicad = exports._makerjs2kicad = (model, layer='Edge.Cuts') => {
} }
} }
}) })
return grs return grs.join('\n')
} }
const footprint_types = require('./footprints') const footprint_types = require('./footprints')
const footprint = exports._footprint = (config, name, points, net_indexer, default_anchor) => { const footprint = exports._footprint = (config, name, points, net_indexer, point) => {
// config sanitization // config sanitization
a.detect_unexpected(config, name, ['type', 'anchor', 'between', 'params']) a.detect_unexpected(config, name, ['type', 'anchor', 'nets', 'params'])
const type = a.in(config.type, `${name}.type`, Object.keys(footprint_types)) const type = a.in(config.type, `${name}.type`, Object.keys(footprint_types))
let anchor = a.anchor(config.anchor, `${name}.anchor`, points, true, default_anchor) let anchor = a.anchor(config.anchor || {}, `${name}.anchor`, points, true, point)
const params = a.sane(config.params, `${name}.params`, 'object') const nets = a.sane(config.nets || {}, `${name}.nets`, 'object')
const params = a.sane(config.params || {}, `${name}.params`, 'object')
// averaging multiple anchors, if necessary
if (config.between) {
const between = a.sane(config.between, `${name}.between`, 'array')
let x = 0, y = 0, r = 0, bi = 0
const len = between.length
for (const b of between) {
ba = a.anchor(b, `${name}.between[${++bi}]`, points, true)
x += ba.x
y += ba.y
r += ba.r
}
anchor = new Point(x / len, y / len, r / len)
}
// basic setup // basic setup
const fp = footprint_types[type] const fp = footprint_types[type]
let result = fp.body let result = fp.body
// footprint positioning // footprint positioning
const at = `(at ${anchor.x} ${anchor.y} ${anchor.r})` const at = `(at ${anchor.x} ${-anchor.y} ${anchor.r})`
result = result.replace('__AT', at) result = result.replace(new RegExp('__AT', 'g'), at)
// fix rotations within footprints
const rot_regex = /__ROT\((\d+)\)/g
const matches = [...result.matchAll(rot_regex)]
for (const match of matches) {
const angle = parseFloat(match[1])
result = result.replace(match[0], (anchor.r + angle) + '')
}
// connecting static nets // connecting static nets
for (const net of (fp.nets || [])) { for (const net of (fp.static_nets || [])) {
const index = net_indexer(net) const index = net_indexer(net)
result = result.replace('__NET_' + net.toUpperCase(), `(net ${index} "${net}")`) result = result.replace(new RegExp('__NET_' + net.toUpperCase(), 'g'), `(net ${index} "${net}")`)
} }
// connecting parametric nets // connecting parametric nets
for (const param of (fp.params || [])) { for (const net_ref of (fp.nets || [])) {
const net = params[param] let net = nets[net_ref]
a.sane(net, `${name}.params.${param}`, 'string') a.sane(net, `${name}.nets.${net_ref}`, 'string')
if (net.startsWith('!') && point) {
const indirect = net.substring(1)
net = point.meta[indirect]
a.sane(net, `${name}.nets.${net_ref} --> ${point.meta.name}.${indirect}`, 'string')
}
const index = net_indexer(net) const index = net_indexer(net)
result = result.replace('__PARAM_' + net.toUpperCase(), `(net ${index} "${net}")`) result = result.replace(new RegExp('__NET_' + net_ref.toUpperCase(), 'g'), `(net ${index} "${net}")`)
}
// connecting other, non-net parameters
for (const param of (fp.params || [])) {
let value = params[param]
if (value === undefined) throw new Error(`Field "${name}.params.${param}" is missing!`)
if (a.type(value) == 'string' && value.startsWith('!') && point) {
const indirect = value.substring(1)
value = point.meta[indirect]
if (value === undefined) throw new Error(`Field "${name}.params.${param} --> ${point.meta.name}.${indirect}" is missing!`)
}
result = result.replace(new RegExp('__PARAM_' + param.toUpperCase(), 'g'), value)
} }
return result return result
@ -200,9 +211,9 @@ exports.parse = (config, points, outlines) => {
const kicad_edge = makerjs2kicad(edge) const kicad_edge = makerjs2kicad(edge)
// making a global net index registry // making a global net index registry
const nets = {} const nets = {"": 0}
const net_indexer = net => { const net_indexer = net => {
if (nets[net]) return nets[net] if (nets[net] !== undefined) return nets[net]
const index = Object.keys(nets).length const index = Object.keys(nets).length
return nets[net] = index return nets[net] = index
} }
@ -211,17 +222,15 @@ exports.parse = (config, points, outlines) => {
// key-level footprints // key-level footprints
for (const [pname, point] of Object.entries(points)) { for (const [pname, point] of Object.entries(points)) {
let f_index = 0 for (const [f_name, f] of Object.entries(point.meta.footprints || {})) {
for (const f of (point.meta.footprints || [])) { footprints.push(footprint(f, `${pname}.footprints.${f_name}`, points, net_indexer, point))
footprints.push(footprint(f, `${pname}.footprints[${++f_index}]`, points, net_indexer, point))
} }
} }
// global one-off footprints // global one-off footprints
const global_footprints = a.sane(config.footprints || [], 'pcb.footprints', 'array') const global_footprints = a.sane(config.footprints || {}, 'pcb.footprints', 'object')
let gf_index = 0 for (const [gf_name, gf] of Object.entries(global_footprints)) {
for (const gf of global_footprints) { footprints.push(footprint(gf, `pcb.footprints.${gf_name}`, points, net_indexer))
footprints.push(footprint(gf, `pcb.footprints[${++gf_index}]`, points, net_indexer))
} }
// finalizing nets // finalizing nets
@ -241,6 +250,7 @@ exports.parse = (config, points, outlines) => {
${nets_text} ${nets_text}
${netclass} ${netclass}
${footprint_text} ${footprint_text}
${kicad_edge}
${kicad_suffix} ${kicad_suffix}
` `

View file

@ -238,8 +238,8 @@ exports.parse = (config = {}) => {
for (const [name, p] of Object.entries(points)) { for (const [name, p] of Object.entries(points)) {
if (p.meta.asym == 'left') continue if (p.meta.asym == 'left') continue
const mp = p.clone().mirror(axis) const mp = p.clone().mirror(axis)
mp.meta = extend(mp.meta, mp.meta.mirror || {})
mp.meta.mirrored = true mp.meta.mirrored = true
delete mp.meta.asym
mirrored_points[`mirror_${name}`] = mp mirrored_points[`mirror_${name}`] = mp
if (p.meta.asym == 'right') { if (p.meta.asym == 'right') {
p.meta.skip = true p.meta.skip = true

View file

@ -13,6 +13,8 @@ points:
bind: [,10] bind: [,10]
top: top:
bind: [,10] bind: [,10]
key:
column_net: P1
ring: ring:
stagger: 12 stagger: 12
rows: rows:
@ -22,6 +24,8 @@ points:
bind: [,10] bind: [,10]
top: top:
bind: [,10] bind: [,10]
key:
column_net: P0
middle: middle:
stagger: 5 stagger: 5
rows: rows:
@ -30,6 +34,8 @@ points:
home: home:
bind: [,10,,10] bind: [,10,,10]
top: top:
key:
column_net: P2
index: index:
stagger: -6 stagger: -6
rows: rows:
@ -39,6 +45,8 @@ points:
bind: [,,,10] bind: [,,,10]
top: top:
bind: [,,,10] bind: [,,,10]
key:
column_net: P3
inner: inner:
stagger: -2 stagger: -2
rows: rows:
@ -48,16 +56,23 @@ points:
bind: [,,,10] bind: [,,,10]
top: top:
bind: [,,,10] bind: [,,,10]
key:
column_net: P4
rows: rows:
bottom: bottom:
bind: [10] bind: [10]
row_net: P7
mirror:
row_net: P16
home: home:
bind: [10] bind: [10]
row_net: P6
mirror:
row_net: P14
top: top:
key: row_net: P5
footprints: mirror:
- type: mx row_net: P15
thumbfan: thumbfan:
anchor: anchor:
ref: inner_bottom ref: inner_bottom
@ -70,6 +85,8 @@ points:
rows: rows:
thumb: thumb:
bind: [10,1,,] bind: [10,1,,]
key:
column_net: P2
home: home:
spread: 21.25 spread: 21.25
rotate: -28 rotate: -28
@ -77,12 +94,35 @@ points:
rows: rows:
thumb: thumb:
bind: [,10,,15] bind: [,10,,15]
key:
column_net: P3
far: far:
rows: rows:
thumb: thumb:
bind: [-1,,,5] bind: [-1,,,5]
key:
column_net: P4
rows:
thumb:
row_net: P8
mirror:
row_net: P10
key: key:
bind: [0, 0, 0, 0] bind: [0,0,0,0]
footprints:
mx:
type: mx
nets:
from: '!column_net'
to: '!name'
diode:
type: diode
anchor:
rotate: 90
shift: [-8, 0]
nets:
from: '!name'
to: '!row_net'
rotate: -20 rotate: -20
mirror: mirror:
ref: pinky_home ref: pinky_home
@ -103,7 +143,7 @@ outline:
rotate: 90 rotate: 90
right: right:
ref: mirror_far_thumb ref: mirror_far_thumb
shift: [-0.5, 0] shift: [0.5, 0]
rotate: 90 rotate: 90
waypoints: waypoints:
- percent: 50 - percent: 50
@ -134,11 +174,11 @@ outline:
- type: rectangle - type: rectangle
size: [25, 5] size: [25, 5]
ref: far_thumb ref: far_thumb
shift: [-25, 12] shift: [25, 12]
- type: rectangle - type: rectangle
size: [25, 5] size: [25, 5]
ref: mirror_home_thumb ref: mirror_home_thumb
shift: [-25, 12] shift: [25, 12]
- type: rectangle - type: rectangle
size: [25, 5] size: [25, 5]
ref: mirror_far_thumb ref: mirror_far_thumb
@ -158,7 +198,11 @@ outline:
pcb: pcb:
edge: outline edge: outline
footprints: footprints:
- type: promicro mcu:
between: type: promicro
- ref: inner_top anchor:
- ref: mirror_inner_top ref:
- inner_top
- mirror_inner_top
shift: [0, -20]
rotate: 270