Mixed progress, vol. 2
This commit is contained in:
parent
c3d7643371
commit
5a380fa58c
7 changed files with 192 additions and 157 deletions
38
README.md
38
README.md
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
22
src/cli.js
22
src/cli.js
|
@ -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
|
||||
|
||||
|
|
|
@ -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'])
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
|
|
137
test/fixtures/absolem.yaml
vendored
137
test/fixtures/absolem.yaml
vendored
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue