1
0

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
points: <points config...>
outline: <outline config...>
case: <case config...>
pcb: <pcb config...>
outlines: <outline config...>
cases: <case config...>
pcbs: <pcb config...>
```
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 `case` 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.
The `outlines` section then uses these points to generate plate, case, and PCB outlines.
The `cases` section details how the case outlines are to be 3D-ized to form a 3D-printable object.
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.
@ -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.
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.
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:
```yaml
case:
cases:
case_name:
- outline: <outline ref>
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.
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,14 +589,16 @@ 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.
```yaml
pcb:
edge: <outline reference>
footprints:
- type: <footprint type>
anchor: <anchor declaration>
nets: <type-specific net params>
params: <type-specific (non-net) footprint params>
- ...
pcbs:
pcb_name:
edge: <outline reference>
footprints:
- type: <footprint type>
anchor: <anchor declaration>
nets: <type-specific net params>
params: <type-specific (non-net) footprint params>
- ...
...
```
Currently, the following footprint types are supported:

View File

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

View File

@ -12,8 +12,8 @@ const yargs = require('yargs')
const u = require('./utils')
const io = require('./io')
const points_lib = require('./points')
const outline_lib = require('./outline')
const pcb_lib = require('./pcb')
const outlines_lib = require('./outlines')
const pcbs_lib = require('./pcbs')
// command line args
@ -68,19 +68,21 @@ if (args.debug) {
// 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)) {
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...')
const pcb = pcb_lib.parse(config.pcb, points, outlines)
const pcb_file = path.join(args.o, `pcb/pcb.kicad_pcb`)
fs.mkdirpSync(path.dirname(pcb_file))
fs.writeFileSync(pcb_file, pcb)
console.log('Scaffolding PCBs...')
const pcbs = pcbs_lib.parse(config.pcbs, points, outlines)
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.writeFileSync(pcb_file, pcb_text)
}
// goodbye

View File

@ -35,23 +35,23 @@ const layout = exports._layout = (config = {}, points = {}) => {
// 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)) {
gval = a.inherit(gval, 'outline.glue', gkey, config)
a.detect_unexpected(gval, `outline.glue.${gkey}`, ['top', 'bottom', 'waypoints', 'extra'])
gval = a.inherit('outlines.glue', gkey, config)
a.detect_unexpected(gval, `outlines.glue.${gkey}`, ['top', 'bottom', 'waypoints', 'extra'])
for (const y of ['top', 'bottom']) {
a.detect_unexpected(gval[y], `outline.glue.${gkey}.${y}`, ['left', 'right'])
gval[y].left = relative_anchor(gval[y].left, `outline.glue.${gkey}.${y}.left`, points)
a.detect_unexpected(gval[y], `outlines.glue.${gkey}.${y}`, ['left', 'right'])
gval[y].left = relative_anchor(gval[y].left, `outlines.glue.${gkey}.${y}.left`, points)
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
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'])
w.percent = a.sane(w.percent, name + '.percent', 'number')
w.width = a.wh(w.width, name + '.width')
@ -208,12 +208,12 @@ exports.parse = (config = {}, points = {}) => {
const outlines = {}
const ex = a.sane(config.exports, 'outline.exports', 'object')
for (const [key, parts] of Object.entries(ex)) {
let index = 0
const ex = a.sane(config.exports, 'outlines.exports', 'object')
for (let [key, parts] of Object.entries(ex)) {
parts = a.inherit('outlines.exports', key, ex)
let result = {models: {}}
for (const part of parts) {
const name = `outline.exports.${key}[${++index}]`
for (const [part_name, part] of Object.entries(parts)) {
const name = `outlines.exports.${key}.${part_name}`
const expected = ['type', 'operation']
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'])

View File

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

View File

@ -180,7 +180,7 @@ exports.parse = (config = {}) => {
for (let [zone_name, zone] of Object.entries(zones)) {
// 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)
points = Object.assign(points, render_zone(zone_name, zone, anchor, global_key))

View File

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