Generalize part objects/arrays, add combination shorthands, update docs
This commit is contained in:
parent
5b1da540ac
commit
d60c9dbc94
5 changed files with 104 additions and 134 deletions
154
README.md
154
README.md
|
@ -46,7 +46,7 @@ The `outlines` section then uses these points to generate plate, case, and PCB o
|
||||||
The `cases` 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 `pcbs` section is used to configure KiCAD PCB templates.
|
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](https://github.com/mrzealot/absolem/blob/master/absolem.yaml)'s config was created.
|
In the following, we'll have an in-depth discussion about each of these.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,48 +236,7 @@ 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.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## A concrete points example
|
|
||||||
|
|
||||||
TODO: Absolem points here, with pics
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -415,15 +374,39 @@ Using these, we define exports as follows:
|
||||||
exports:
|
exports:
|
||||||
my_name:
|
my_name:
|
||||||
- operation: add | subtract | intersect | stack # default = add
|
- operation: add | subtract | intersect | stack # default = add
|
||||||
type: <one of the types>
|
type: <one of the types> # default = outline
|
||||||
<type-specific params>
|
<type-specific params>
|
||||||
- ...
|
- ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Individual parts can also be specified as an object instead of an array (which could be useful when YAML or built-in inheritance is used), like so:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
exports:
|
||||||
|
my_name:
|
||||||
|
first_phase:
|
||||||
|
operation: add | subtract | intersect | stack # default = add
|
||||||
|
type: <one of the types> # default = outline
|
||||||
|
<type-specific params>
|
||||||
|
second:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Operations are performed in order, and the resulting shape is exported as an output.
|
Operations are performed in order, and the resulting shape is exported as an output.
|
||||||
Additionally, it is going to be available for further export declarations to use (through the `ref` type) under the name specified (`my_name`, in this case).
|
Additionally, it is going to be available for further export declarations to use (through the `ref` type) under the name specified (`my_name`, in this case).
|
||||||
If we only want to use it as a building block for further exports, we can start the name with an underscore (e.g., `_my_name`) to prevent it from being actually exported.
|
If we only want to use it as a building block for further exports, we can start the name with an underscore (e.g., `_my_name`) to prevent it from being actually exported.
|
||||||
|
|
||||||
|
A shorthand version of a part can be given when the elements of the above arrays/objects are simple strings instead of further objects.
|
||||||
|
The syntax is a symbol from `[+, -, ~, ^]`, followed by a name, and is equivalent to adding/subtracting/intersecting/stacking an outline of that name, respectively.
|
||||||
|
More specifically, `~something` is equivalent to:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: outline
|
||||||
|
name: something
|
||||||
|
operation: intersect
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -443,36 +426,6 @@ If we only want to use it as a building block for further exports, we can start
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## A concrete outline example
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -497,7 +450,7 @@ Declarations might look like this:
|
||||||
```yaml
|
```yaml
|
||||||
cases:
|
cases:
|
||||||
case_name:
|
case_name:
|
||||||
- type: outline
|
- type: outline # default option
|
||||||
name: <outline ref>
|
name: <outline ref>
|
||||||
extrude: num # default = 1
|
extrude: num # default = 1
|
||||||
shift: [x, y, z] # default = [0, 0, 0]
|
shift: [x, y, z] # default = [0, 0, 0]
|
||||||
|
@ -518,7 +471,12 @@ When the `type` is `case`, `name` specifies which case to use.
|
||||||
After having established our base 3D object, it is (relatively!) `rotate`d, `shift`ed, and combined with what we have so far according to `operation`.
|
After having established our base 3D object, it is (relatively!) `rotate`d, `shift`ed, and combined with what we have so far according to `operation`.
|
||||||
If we only want to use an object as a building block for further objects, we can employ the same "start with an underscore" trick we learned at the outlines section to make it "private".
|
If we only want to use an object as a building block for further objects, we can employ the same "start with an underscore" trick we learned at the outlines section to make it "private".
|
||||||
|
|
||||||
|
Individual case parts can again be listed as an object instead of an array, if that's more comfortable for inheritance/reuse (just like for outlines).
|
||||||
|
And speaking of outline similarities, the `[+, -, ~]` plus name shorthand is available again.
|
||||||
|
First it will try to look up cases, and then outlines by the name given.
|
||||||
|
Stacking is omitted as it makes no sense here.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -537,43 +495,6 @@ If we only want to use an object as a building block for further objects, we can
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## A concrete case example
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -602,12 +523,16 @@ 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 = !column_net` would mean that the key's `column_net` 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.
|
||||||
|
|
||||||
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 (or other decorative outlines for the silkscreen) can be specified using a previously defined outline name under the `outlines` key.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
pcbs:
|
pcbs:
|
||||||
pcb_name:
|
pcb_name:
|
||||||
edge: <outline reference>
|
outlines:
|
||||||
|
pcb_outline_name:
|
||||||
|
outline: <outline reference>
|
||||||
|
layer: <kicad layer to export to> # default = Edge.Cuts
|
||||||
|
...
|
||||||
footprints:
|
footprints:
|
||||||
- type: <footprint type>
|
- type: <footprint type>
|
||||||
anchor: <anchor declaration>
|
anchor: <anchor declaration>
|
||||||
|
@ -617,6 +542,7 @@ pcbs:
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Defining both the `outlines` and the `footprints` can be done either as arrays or objects, whichever is more convenient.
|
||||||
Currently, the following footprint types are supported:
|
Currently, the following footprint types are supported:
|
||||||
|
|
||||||
- **`mx`**, **`alps`**, **`choc`**: mechanical switch footprints. Common nets:
|
- **`mx`**, **`alps`**, **`choc`**: mechanical switch footprints. Common nets:
|
||||||
|
|
|
@ -162,4 +162,24 @@ const inherit = exports.inherit = (name_prefix, name, set) => {
|
||||||
result = extend.apply(this, list)
|
result = extend.apply(this, list)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const op_prefix = exports.op_prefix = str => {
|
||||||
|
const suffix = str.slice(1)
|
||||||
|
if (str.startsWith('+')) return {name: suffix, operation: 'add'}
|
||||||
|
if (str.startsWith('-')) return {name: suffix, operation: 'subtract'}
|
||||||
|
if (str.startsWith('~')) return {name: suffix, operation: 'intersect'}
|
||||||
|
if (str.startsWith('^')) return {name: suffix, operation: 'stack'}
|
||||||
|
return {name: str, operation: 'add'}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.op_str = (str, choices={}, order=Object.keys(choices)) => {
|
||||||
|
let res = op_prefix(str)
|
||||||
|
for (const key of order) {
|
||||||
|
if (choices[key].includes(res.name)) {
|
||||||
|
res.type = key
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
47
src/cases.js
47
src/cases.js
|
@ -36,30 +36,40 @@ exports.parse = (config, outlines) => {
|
||||||
return result.join('')
|
return result.join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [case_name, case_config] of Object.entries(cases_config)) {
|
for (let [case_name, case_config] of Object.entries(cases_config)) {
|
||||||
|
|
||||||
// config sanitization
|
// config sanitization
|
||||||
const parts = a.sane(case_config, `cases.${case_name}`, 'array')
|
case_config = a.inherit('cases', case_name, cases_config)
|
||||||
|
if (a.type(case_config) == 'array') {
|
||||||
|
case_config = {...case_config}
|
||||||
|
}
|
||||||
|
const parts = a.sane(case_config, `cases.${case_name}`, 'object')
|
||||||
|
|
||||||
const body = []
|
const body = []
|
||||||
const case_dependencies = []
|
const case_dependencies = []
|
||||||
const outline_dependencies = []
|
const outline_dependencies = []
|
||||||
let part_index = 0
|
let first = true
|
||||||
for (const part of parts) {
|
for (let [part_name, part] of Object.entries(parts)) {
|
||||||
const part_name = `cases.${case_name}[${++part_index}]`
|
if (a.type(part) == 'string') {
|
||||||
const part_var = `${case_name}__part_${part_index}`
|
part = a.op_str(part, {
|
||||||
a.detect_unexpected(part, part_name, ['type', 'name', 'extrude', 'shift', 'rotate', 'operation'])
|
outline: Object.keys(outlines),
|
||||||
const type = a.in(part.type, `${part_name}.type`, ['outline', 'case'])
|
case: Object.keys(cases)
|
||||||
const name = a.sane(part.name, `${part_name}.name`, 'string')
|
}, ['case', 'outline'])
|
||||||
const shift = a.numarr(part.shift || [0, 0, 0], `${part_name}.shift`, 3)
|
}
|
||||||
const rotate = a.numarr(part.rotate || [0, 0, 0], `${part_name}.rotate`, 3)
|
const part_qname = `cases.${case_name}.${part_name}`
|
||||||
const operation = a.in(part.operation || 'add', `${part_name}.operation`, ['add', 'subtract', 'intersect'])
|
const part_var = `${case_name}__part_${part_name}`
|
||||||
|
a.detect_unexpected(part, part_qname, ['type', 'name', 'extrude', 'shift', 'rotate', 'operation'])
|
||||||
|
const type = a.in(part.type || 'outline', `${part_qname}.type`, ['outline', 'case'])
|
||||||
|
const name = a.sane(part.name, `${part_qname}.name`, 'string')
|
||||||
|
const shift = a.numarr(part.shift || [0, 0, 0], `${part_qname}.shift`, 3)
|
||||||
|
const rotate = a.numarr(part.rotate || [0, 0, 0], `${part_qname}.rotate`, 3)
|
||||||
|
const operation = a.in(part.operation || 'add', `${part_qname}.operation`, ['add', 'subtract', 'intersect'])
|
||||||
|
|
||||||
let base
|
let base
|
||||||
if (type == 'outline') {
|
if (type == 'outline') {
|
||||||
const extrude = a.sane(part.extrude || 1, `${part_name}.extrude`, 'number')
|
const extrude = a.sane(part.extrude || 1, `${part_qname}.extrude`, 'number')
|
||||||
const outline = outlines[name]
|
const outline = outlines[name]
|
||||||
a.assert(outline, `Field "${part_name}.name" does not name a valid outline!`)
|
a.assert(outline, `Field "${part_qname}.name" does not name a valid outline!`)
|
||||||
if (!scripts[name]) {
|
if (!scripts[name]) {
|
||||||
scripts[name] = m.exporter.toJscadScript(outline, {
|
scripts[name] = m.exporter.toJscadScript(outline, {
|
||||||
functionName: `${name}_outline_fn`,
|
functionName: `${name}_outline_fn`,
|
||||||
|
@ -70,8 +80,8 @@ exports.parse = (config, outlines) => {
|
||||||
outline_dependencies.push(name)
|
outline_dependencies.push(name)
|
||||||
base = `${name}_outline_fn()`
|
base = `${name}_outline_fn()`
|
||||||
} else {
|
} else {
|
||||||
a.assert(part.extrude === undefined, `Field "${part_name}.extrude" should not be used when type=case!`)
|
a.assert(part.extrude === undefined, `Field "${part_qname}.extrude" should not be used when type=case!`)
|
||||||
a.in(name, `${part_name}.name`, Object.keys(cases))
|
a.in(name, `${part_qname}.name`, Object.keys(cases))
|
||||||
case_dependencies.push(name)
|
case_dependencies.push(name)
|
||||||
base = `${name}_case_fn()`
|
base = `${name}_case_fn()`
|
||||||
}
|
}
|
||||||
|
@ -81,13 +91,14 @@ exports.parse = (config, outlines) => {
|
||||||
else if (operation == 'intersect') op = 'intersect'
|
else if (operation == 'intersect') op = 'intersect'
|
||||||
|
|
||||||
let op_statement = `let result = ${part_var};`
|
let op_statement = `let result = ${part_var};`
|
||||||
if (part_index > 1) {
|
if (!first) {
|
||||||
op_statement = `result = result.${op}(${part_var});`
|
op_statement = `result = result.${op}(${part_var});`
|
||||||
}
|
}
|
||||||
|
first = false
|
||||||
|
|
||||||
body.push(`
|
body.push(`
|
||||||
|
|
||||||
// creating part ${part_index} of case ${case_name}
|
// creating part ${part_name} of case ${case_name}
|
||||||
let ${part_var} = ${base};
|
let ${part_var} = ${base};
|
||||||
|
|
||||||
// make sure that rotations are relative
|
// make sure that rotations are relative
|
||||||
|
|
|
@ -212,11 +212,18 @@ exports.parse = (config = {}, points = {}) => {
|
||||||
const ex = a.sane(config.exports || {}, 'outlines.exports', 'object')
|
const ex = a.sane(config.exports || {}, 'outlines.exports', 'object')
|
||||||
for (let [key, parts] of Object.entries(ex)) {
|
for (let [key, parts] of Object.entries(ex)) {
|
||||||
parts = a.inherit('outlines.exports', key, ex)
|
parts = a.inherit('outlines.exports', key, ex)
|
||||||
|
if (a.type(parts) == 'array') {
|
||||||
|
parts = {...parts}
|
||||||
|
}
|
||||||
|
parts = a.sane(parts, `outlines.exports.${key}`, 'object')
|
||||||
let result = {models: {}}
|
let result = {models: {}}
|
||||||
for (const [part_name, part] of Object.entries(parts)) {
|
for (let [part_name, part] of Object.entries(parts)) {
|
||||||
const name = `outlines.exports.${key}.${part_name}`
|
const name = `outlines.exports.${key}.${part_name}`
|
||||||
|
if (a.type(part) == 'string') {
|
||||||
|
part = a.op_str(part, {outline: Object.keys(outlines)})
|
||||||
|
}
|
||||||
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 || 'outline', `${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'])
|
||||||
|
|
||||||
let op = u.union
|
let op = u.union
|
||||||
|
|
|
@ -215,6 +215,9 @@ exports.parse = (config, points, outlines) => {
|
||||||
a.detect_unexpected(pcb_config, `pcbs.${pcb_name}`, ['outlines', 'footprints'])
|
a.detect_unexpected(pcb_config, `pcbs.${pcb_name}`, ['outlines', 'footprints'])
|
||||||
|
|
||||||
// outline conversion
|
// outline conversion
|
||||||
|
if (a.type(pcb_config.outlines) == 'array') {
|
||||||
|
pcb_config.outlines = {...pcb_config.outlines}
|
||||||
|
}
|
||||||
const config_outlines = a.sane(pcb_config.outlines || {}, `pcbs.${pcb_name}.outlines`, 'object')
|
const config_outlines = a.sane(pcb_config.outlines || {}, `pcbs.${pcb_name}.outlines`, 'object')
|
||||||
const kicad_outlines = {}
|
const kicad_outlines = {}
|
||||||
for (const [outline_name, outline] of Object.entries(config_outlines)) {
|
for (const [outline_name, outline] of Object.entries(config_outlines)) {
|
||||||
|
@ -250,6 +253,9 @@ exports.parse = (config, points, outlines) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// global one-off footprints
|
// global one-off footprints
|
||||||
|
if (a.type(pcb_config.footprints) == 'array') {
|
||||||
|
pcb_config.footprints = {...pcb_config.footprints}
|
||||||
|
}
|
||||||
const global_footprints = a.sane(pcb_config.footprints || {}, `pcbs.${pcb_name}.footprints`, 'object')
|
const global_footprints = a.sane(pcb_config.footprints || {}, `pcbs.${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, `pcbs.${pcb_name}.footprints.${gf_name}`, points, undefined, net_indexer, component_indexer))
|
footprints.push(footprint(gf, `pcbs.${pcb_name}.footprints.${gf_name}`, points, undefined, net_indexer, component_indexer))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue