Mixed progress, vol. 2

This commit is contained in:
Bán Dénes 2020-07-17 23:20:49 +02:00
parent c3d7643371
commit 5a380fa58c
7 changed files with 192 additions and 157 deletions

View file

@ -36,15 +36,15 @@ The important thing is that the data should contain the following keys:
```yaml ```yaml
points: <points config...> points: <points config...>
outline: <outline config...> outlines: <outline config...>
case: <case config...> cases: <case config...>
pcb: <pcb config...> pcbs: <pcb config...>
``` ```
The `points` section describes the core of the layout: the positions of the keys. The `points` section describes the core of the layout: the positions of the keys.
The `outline` section then uses these points to generate plate, case, and PCB outlines. The `outlines` section then uses these points to generate plate, case, and PCB outlines.
The `case` section details how the case outlines are to be 3D-ized to form a 3D-printable object. The `cases` section details how the case outlines are to be 3D-ized to form a 3D-printable object.
Finally, the `pcb` section is used to configure a KiCAD PCB template. Finally, the `pcbs` section is used to configure KiCAD PCB templates.
In the following, we'll have an in-depth discussion about each of these, with an additional running example of how the [Absolem](#TODO-link-to-config-yaml)'s config was created. In the following, we'll have an in-depth discussion about each of these, with an additional running example of how the [Absolem](#TODO-link-to-config-yaml)'s config was created.
@ -305,7 +305,7 @@ TODO: Absolem points here, with pics
## Outline ## Outlines
Once the raw points are available, we want to turn them into solid, continuous outlines. Once the raw points are available, we want to turn them into solid, continuous outlines.
The points are enough to create properly positioned and rotated rectangles (with parametric side lengths), but they won't combine since there won't be any overlap. The points are enough to create properly positioned and rotated rectangles (with parametric side lengths), but they won't combine since there won't be any overlap.
@ -480,7 +480,7 @@ If we only want to use it as a building block for further exports, we can start
## Case ## Cases
Cases add a pretty basic and minimal 3D aspect to the generation process. Cases add a pretty basic and minimal 3D aspect to the generation process.
In this phase, we take different outlines (exported from the above section, even the "private" ones), extrude and position them in space, and combine them into one 3D-printable object. In this phase, we take different outlines (exported from the above section, even the "private" ones), extrude and position them in space, and combine them into one 3D-printable object.
@ -488,7 +488,7 @@ That's it.
Declarations might look like this: Declarations might look like this:
```yaml ```yaml
case: cases:
case_name: case_name:
- outline: <outline ref> - outline: <outline ref>
extrude: num # default = 1 extrude: num # default = 1
@ -575,7 +575,7 @@ If we only want to use an object as a building block for further objects, we can
## PCB ## PCBs
Everything should be ready for a handwire, but if you'd like the design to be more accessible and easily replicable, you probably want a PCB as well. Everything should be ready for a handwire, but if you'd like the design to be more accessible and easily replicable, you probably want a PCB as well.
To help you get started, the necessary footprints and an edge cut can be automatically positioned so that all you need to do manually is the routing. To help you get started, the necessary footprints and an edge cut can be automatically positioned so that all you need to do manually is the routing.
@ -589,7 +589,8 @@ The differences between the two footprint types are:
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
pcb: pcbs:
pcb_name:
edge: <outline reference> edge: <outline reference>
footprints: footprints:
- type: <footprint type> - type: <footprint type>
@ -597,6 +598,7 @@ pcb:
nets: <type-specific net params> nets: <type-specific net params>
params: <type-specific (non-net) footprint params> params: <type-specific (non-net) footprint params>
- ... - ...
...
``` ```
Currently, the following footprint types are supported: Currently, the following footprint types are supported:

View file

@ -106,7 +106,7 @@ const extend_pair = exports.extend_pair = (to, from) => {
} else return from } else return from
} }
exports.extend = (...args) => { const extend = exports.extend = (...args) => {
let res = args[0] let res = args[0]
for (const arg of args) { for (const arg of args) {
if (res == arg) continue if (res == arg) continue
@ -115,18 +115,22 @@ exports.extend = (...args) => {
return res return res
} }
const inherit = exports.inherit = (config, name_prefix, name, set) => { const inherit = exports.inherit = (name_prefix, name, set) => {
let result = u.deepcopy(config) let result = u.deepcopy(set[name])
if (config.extends !== undefined) { if (result.extends !== undefined) {
let list = config.extends let candidates = [name]
if (type(list) !== 'array') list = [list] const list = []
for (const item of list) { while (candidates.length) {
const other = set[item] const item = candidates.shift()
assert(other, `Field "${name_prefix}.${name}" does not name a valid target!`) const other = u.deepcopy(set[item])
result = extend_pair(inherit(other, name_prefix, config.extends, set), result) assert(other, `"${item}" (reached from "${name_prefix}.${name}.extends") does not name a valid target!`)
let parents = other.extends || []
if (type(parents) !== 'array') parents = [parents]
candidates = candidates.concat(parents)
delete other.extends
list.unshift(other)
} }
delete result.extends result = extend.apply(this, list)
} }
return result return result
} }

View file

@ -12,8 +12,8 @@ const yargs = require('yargs')
const u = require('./utils') const u = require('./utils')
const io = require('./io') const io = require('./io')
const points_lib = require('./points') const points_lib = require('./points')
const outline_lib = require('./outline') const outlines_lib = require('./outlines')
const pcb_lib = require('./pcb') const pcbs_lib = require('./pcbs')
// command line args // command line args
@ -68,19 +68,21 @@ if (args.debug) {
// outlines // outlines
console.log('Generating outlines...') console.log('Generating outlines...')
const outlines = outline_lib.parse(config.outline, points) const outlines = outlines_lib.parse(config.outlines, points)
for (const [name, outline] of Object.entries(outlines)) { for (const [name, outline] of Object.entries(outlines)) {
if (!args.debug && name.startsWith('_')) continue if (!args.debug && name.startsWith('_')) continue
io.dump_model(outline, path.join(args.o, `outline/${name}`), args.debug) io.dump_model(outline, path.join(args.o, `outlines/${name}`), args.debug)
} }
// pcb // pcbs
console.log('Scaffolding PCB...') console.log('Scaffolding PCBs...')
const pcb = pcb_lib.parse(config.pcb, points, outlines) const pcbs = pcbs_lib.parse(config.pcbs, points, outlines)
const pcb_file = path.join(args.o, `pcb/pcb.kicad_pcb`) for (const [pcb_name, pcb_text] of Object.entries(pcbs)) {
const pcb_file = path.join(args.o, `pcbs/${pcb_name}.kicad_pcb`)
fs.mkdirpSync(path.dirname(pcb_file)) fs.mkdirpSync(path.dirname(pcb_file))
fs.writeFileSync(pcb_file, pcb) fs.writeFileSync(pcb_file, pcb_text)
}
// goodbye // goodbye

View file

@ -35,23 +35,23 @@ const layout = exports._layout = (config = {}, points = {}) => {
// Glue config sanitization // Glue config sanitization
const parsed_glue = u.deepcopy(a.sane(config, 'outline.glue', 'object')) const parsed_glue = u.deepcopy(a.sane(config, 'outlines.glue', 'object'))
for (let [gkey, gval] of Object.entries(parsed_glue)) { for (let [gkey, gval] of Object.entries(parsed_glue)) {
gval = a.inherit(gval, 'outline.glue', gkey, config) gval = a.inherit('outlines.glue', gkey, config)
a.detect_unexpected(gval, `outline.glue.${gkey}`, ['top', 'bottom', 'waypoints', 'extra']) a.detect_unexpected(gval, `outlines.glue.${gkey}`, ['top', 'bottom', 'waypoints', 'extra'])
for (const y of ['top', 'bottom']) { for (const y of ['top', 'bottom']) {
a.detect_unexpected(gval[y], `outline.glue.${gkey}.${y}`, ['left', 'right']) a.detect_unexpected(gval[y], `outlines.glue.${gkey}.${y}`, ['left', 'right'])
gval[y].left = relative_anchor(gval[y].left, `outline.glue.${gkey}.${y}.left`, points) gval[y].left = relative_anchor(gval[y].left, `outlines.glue.${gkey}.${y}.left`, points)
if (a.type(gval[y].right) != 'number') { if (a.type(gval[y].right) != 'number') {
gval[y].right = relative_anchor(gval[y].right, `outline.glue.${gkey}.${y}.right`, points) gval[y].right = relative_anchor(gval[y].right, `outlines.glue.${gkey}.${y}.right`, points)
} }
} }
gval.waypoints = a.sane(gval.waypoints || [], `outline.glue.${gkey}.waypoints`, 'array') gval.waypoints = a.sane(gval.waypoints || [], `outlines.glue.${gkey}.waypoints`, 'array')
let wi = 0 let wi = 0
gval.waypoints = gval.waypoints.map(w => { gval.waypoints = gval.waypoints.map(w => {
const name = `outline.glue.${gkey}.waypoints[${++wi}]` const name = `outlines.glue.${gkey}.waypoints[${++wi}]`
a.detect_unexpected(w, name, ['percent', 'width']) a.detect_unexpected(w, name, ['percent', 'width'])
w.percent = a.sane(w.percent, name + '.percent', 'number') w.percent = a.sane(w.percent, name + '.percent', 'number')
w.width = a.wh(w.width, name + '.width') w.width = a.wh(w.width, name + '.width')
@ -208,12 +208,12 @@ exports.parse = (config = {}, points = {}) => {
const outlines = {} const outlines = {}
const ex = a.sane(config.exports, 'outline.exports', 'object') const ex = a.sane(config.exports, 'outlines.exports', 'object')
for (const [key, parts] of Object.entries(ex)) { for (let [key, parts] of Object.entries(ex)) {
let index = 0 parts = a.inherit('outlines.exports', key, ex)
let result = {models: {}} let result = {models: {}}
for (const part of parts) { for (const [part_name, part] of Object.entries(parts)) {
const name = `outline.exports.${key}[${++index}]` const name = `outlines.exports.${key}.${part_name}`
const expected = ['type', 'operation'] const expected = ['type', 'operation']
part.type = a.in(part.type, `${name}.type`, ['keys', 'rectangle', 'circle', 'polygon', 'outline']) part.type = a.in(part.type, `${name}.type`, ['keys', 'rectangle', 'circle', 'polygon', 'outline'])
part.operation = a.in(part.operation || 'add', `${name}.operation`, ['add', 'subtract', 'intersect', 'stack']) part.operation = a.in(part.operation || 'add', `${name}.operation`, ['add', 'subtract', 'intersect', 'stack'])

View file

@ -204,10 +204,15 @@ const footprint = exports._footprint = (config, name, points, net_indexer, point
exports.parse = (config, points, outlines) => { exports.parse = (config, points, outlines) => {
const pcbs = a.sane(config, 'pcb', 'object')
const results = {}
for (const [pcb_name, pcb_config] of Object.entries(pcbs)) {
// config sanitization // config sanitization
a.detect_unexpected(config, 'pcb', ['edge', 'footprints']) a.detect_unexpected(pcb_config, `pcb.${pcb_name}`, ['edge', 'footprints'])
const edge = outlines[config.edge] const edge = outlines[pcb_config.edge]
if (!edge) throw new Error(`Field "pcb.edge" doesn't name a valid outline!`) if (!edge) throw new Error(`Field "pcb.${pcb_name}.edge" doesn't name a valid outline!`)
// Edge.Cuts conversion // Edge.Cuts conversion
const kicad_edge = makerjs2kicad(edge) const kicad_edge = makerjs2kicad(edge)
@ -223,16 +228,16 @@ exports.parse = (config, points, outlines) => {
const footprints = [] const footprints = []
// key-level footprints // key-level footprints
for (const [pname, point] of Object.entries(points)) { for (const [p_name, point] of Object.entries(points)) {
for (const [f_name, f] of Object.entries(point.meta.footprints || {})) { for (const [f_name, f] of Object.entries(point.meta.footprints || {})) {
footprints.push(footprint(f, `${pname}.footprints.${f_name}`, points, net_indexer, point)) footprints.push(footprint(f, `${p_name}.footprints.${f_name}`, points, net_indexer, point))
} }
} }
// global one-off footprints // global one-off footprints
const global_footprints = a.sane(config.footprints || {}, 'pcb.footprints', 'object') const global_footprints = a.sane(pcb_config.footprints || {}, `pcb.${pcb_name}.footprints`, 'object')
for (const [gf_name, gf] of Object.entries(global_footprints)) { for (const [gf_name, gf] of Object.entries(global_footprints)) {
footprints.push(footprint(gf, `pcb.footprints.${gf_name}`, points, net_indexer)) footprints.push(footprint(gf, `pcb.${pcb_name}.footprints.${gf_name}`, points, net_indexer))
} }
// finalizing nets // finalizing nets
@ -246,14 +251,15 @@ exports.parse = (config, points, outlines) => {
const netclass = kicad_netclass.replace('__ADD_NET', add_nets_arr.join('\n')) const netclass = kicad_netclass.replace('__ADD_NET', add_nets_arr.join('\n'))
const nets_text = nets_arr.join('\n') const nets_text = nets_arr.join('\n')
const footprint_text = footprints.join('\n') const footprint_text = footprints.join('\n')
return ` results[pcb_name] = `
${kicad_prefix} ${kicad_prefix}
${nets_text} ${nets_text}
${netclass} ${netclass}
${footprint_text} ${footprint_text}
${kicad_edge} ${kicad_edge}
${kicad_suffix} ${kicad_suffix}
` `
} }
return results
}

View file

@ -180,7 +180,7 @@ exports.parse = (config = {}) => {
for (let [zone_name, zone] of Object.entries(zones)) { for (let [zone_name, zone] of Object.entries(zones)) {
// handle zone-level `extends` clauses // handle zone-level `extends` clauses
zone = a.inherit(zone, 'points.zones', zone_name, zones) zone = a.inherit('points.zones', zone_name, zones)
const anchor = a.anchor(zone.anchor || {}, `points.zones.${zone_name}.anchor`, points) const anchor = a.anchor(zone.anchor || {}, `points.zones.${zone_name}.anchor`, points)
points = Object.assign(points, render_zone(zone_name, zone, anchor, global_key)) points = Object.assign(points, render_zone(zone_name, zone, anchor, global_key))

View file

@ -10,9 +10,9 @@ points:
rows: rows:
bottom: bottom:
home: home:
bind: [,15] bind: [,15,-1]
top: top:
bind: [,15] bind: [,15,-1]
key: key:
column_net: P1 column_net: P1
ring: ring:
@ -70,6 +70,10 @@ points:
mirror: mirror:
row_net: P6 row_net: P6
top: top:
footprints:
mx:
anchor:
rotate: 180
row_net: P15 row_net: P15
mirror: mirror:
row_net: P5 row_net: P5
@ -87,6 +91,8 @@ points:
tags: tags:
s19: false s19: false
s18: true s18: true
footprints:
diode: '!!unset'
thumbfan: thumbfan:
anchor: anchor:
ref: matrix_inner_bottom ref: matrix_inner_bottom
@ -203,9 +209,9 @@ points:
mirror: mirror:
ref: matrix_pinky_home ref: matrix_pinky_home
distance: 223.7529778 distance: 223.7529778
outline: outlines:
glue: glue:
classic: classic_s19:
top: top:
left: left:
ref: matrix_inner_top ref: matrix_inner_top
@ -227,95 +233,110 @@ outline:
width: 50 width: 50
- percent: 90 - percent: 90
width: 25 width: 25
uniform: uniform_s19:
extends: classic extends: classic_s19
bottom: bottom:
left: left:
ref: unifar_far1u_thumb ref: unifar_far1u_thumb
right: right:
ref: mirror_unifar_far1u_thumb ref: mirror_unifar_far1u_thumb
choc: classic_s18:
extends: classic extends: classic_s19
top: top:
left: left:
ref: choc_inner_top ref: choc_inner_top
right: right:
ref: mirror_choc_inner_top ref: mirror_choc_inner_top
uniform_choc: uniform_s18:
extends: extends:
- uniform - uniform_s19
- choc - classic_s18
exports: exports:
classic_outline: classic_s19_outline:
- type: keys main:
type: keys
side: both side: both
tags: tags:
- s19 - s19
- classic - classic
glue: classic glue: classic_s19
size: 13.5 size: 13.5
corner: .5 corner: .5
uniform_outline: uniform_s19_outline:
- type: keys extends: classic_s19_outline
side: both main:
tags: tags:
- s19 - s19
- uniform - uniform
glue: uniform glue: uniform_s19
size: 13.5 uniform_s18_outline:
corner: .5 extends: uniform_s19_outline
main:
tags:
- s18
- uniform
glue: uniform_s18
intersected_outline: intersected_outline:
- type: outline one:
name: classic_outline type: outline
- type: outline name: classic_s19_outline
name: uniform_outline two:
type: outline
name: uniform_s18_outline
operation: intersect operation: intersect
classic_holes: classic_s19_switches:
- type: keys main:
type: keys
side: both side: both
tags: tags:
- s19
- classic - classic
glue: classic glue: classic_s19
size: 14 size: 14
bound: false bound: false
classic_middle: uniform_s19_switches:
- type: keys main:
type: keys
side: both
tags:
- uniform
glue: uniform_s19
size: 14
bound: false
classic_s19_middle:
raw:
type: keys
side: middle side: middle
tags: tags:
- s19 - s19
- classic - classic
glue: classic glue: classic_s19
size: 24 size: 24
- type: rectangle helper1:
type: rectangle
size: [25, 5] size: [25, 5]
ref: thumbfan_home_thumb ref: thumbfan_home_thumb
shift: [0, 12] shift: [0, 12]
- type: rectangle helper2:
type: rectangle
size: [25, 5] size: [25, 5]
ref: thumbfan_far_thumb ref: thumbfan_far_thumb
shift: [25, 12] shift: [-25, 12]
- type: rectangle helper3:
type: rectangle
size: [25, 5] size: [25, 5]
ref: mirror_thumbfan_home_thumb ref: mirror_thumbfan_home_thumb
shift: [25, 12] shift: [25, 12]
- type: rectangle helper4:
type: rectangle
size: [25, 5] size: [25, 5]
ref: mirror_thumbfan_far_thumb ref: mirror_thumbfan_far_thumb
shift: [0, 12] shift: [0, 12]
- type: outline outer_bounds:
name: classic_outline type: outline
name: classic_s19_outline
operation: intersect operation: intersect
complex: pcbs:
- type: outline main:
name: classic_outline
- type: outline
name: classic_holes
operation: stack
- type: outline
name: classic_middle
operation: stack
pcb:
edge: intersected_outline edge: intersected_outline
footprints: footprints:
mcu: mcu: