Generalize case combinations, relativize rotations
This commit is contained in:
parent
23ed8989ce
commit
5b1da540ac
2 changed files with 95 additions and 34 deletions
15
README.md
15
README.md
|
@ -497,17 +497,25 @@ Declarations might look like this:
|
||||||
```yaml
|
```yaml
|
||||||
cases:
|
cases:
|
||||||
case_name:
|
case_name:
|
||||||
- outline: <outline ref>
|
- type: outline
|
||||||
|
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]
|
||||||
rotate: [ax, ay, az] # default = [0, 0, 0]
|
rotate: [ax, ay, az] # default = [0, 0, 0]
|
||||||
operation: add | subtract | intersect # default = add
|
operation: add | subtract | intersect # default = add
|
||||||
|
- type: case
|
||||||
|
name: <case_ref>
|
||||||
|
# extrude makes no sense here...
|
||||||
|
shift: # same as above
|
||||||
|
rotate: # same as above
|
||||||
|
operation: # same as above
|
||||||
- ...
|
- ...
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
`outline` specifies which outline to import onto the xy plane, while `extrude` specifies how much it should be extruded along the z axis.
|
When the `type` is `outline`, `name` specifies which outline to import onto the xy plane, while `extrude` specifies how much it should be extruded along the z axis.
|
||||||
After that, the object is `shift`d, `rotate`d, and combined with what we have so far according to `operation`.
|
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`.
|
||||||
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".
|
||||||
|
|
||||||
|
|
||||||
|
@ -543,6 +551,7 @@ If we only want to use an object as a building block for further objects, we can
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## A concrete case example
|
## A concrete case example
|
||||||
|
|
114
src/cases.js
114
src/cases.js
|
@ -4,68 +4,120 @@ const a = require('./assert')
|
||||||
|
|
||||||
exports.parse = (config, outlines) => {
|
exports.parse = (config, outlines) => {
|
||||||
|
|
||||||
const cases = a.sane(config, 'cases', 'object')
|
const cases_config = a.sane(config, 'cases', 'object')
|
||||||
|
|
||||||
|
const scripts = {}
|
||||||
|
const cases = {}
|
||||||
const results = {}
|
const results = {}
|
||||||
|
|
||||||
for (const [case_name, case_config] of Object.entries(cases)) {
|
const resolve = (case_name, resolved_scripts=new Set(), resolved_cases=new Set()) => {
|
||||||
|
for (const o of Object.values(cases[case_name].outline_dependencies)) {
|
||||||
|
resolved_scripts.add(o)
|
||||||
|
}
|
||||||
|
for (const c of Object.values(cases[case_name].case_dependencies)) {
|
||||||
|
resolved_cases.add(c)
|
||||||
|
resolve(c, resolved_scripts, resolved_cases)
|
||||||
|
}
|
||||||
|
result = []
|
||||||
|
for (const o of resolved_scripts) {
|
||||||
|
result.push(scripts[o] + '\n\n')
|
||||||
|
}
|
||||||
|
for (const c of resolved_cases) {
|
||||||
|
result.push(cases[c].body)
|
||||||
|
}
|
||||||
|
result.push(cases[case_name].body)
|
||||||
|
result.push(`
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
return ${case_name}_case_fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
`)
|
||||||
|
return result.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [case_name, case_config] of Object.entries(cases_config)) {
|
||||||
|
|
||||||
// config sanitization
|
// config sanitization
|
||||||
const parts = a.sane(case_config, `cases.${case_name}`, 'array')
|
const parts = a.sane(case_config, `cases.${case_name}`, 'array')
|
||||||
|
|
||||||
const scripts = []
|
const body = []
|
||||||
const main = []
|
const case_dependencies = []
|
||||||
|
const outline_dependencies = []
|
||||||
let part_index = 0
|
let part_index = 0
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
const part_name = `cases.${case_name}[${++part_index}]`
|
const part_name = `cases.${case_name}[${++part_index}]`
|
||||||
a.detect_unexpected(part, part_name, ['outline', 'extrude', 'shift', 'rotate', 'operation'])
|
const part_var = `${case_name}__part_${part_index}`
|
||||||
const outline = outlines[part.outline]
|
a.detect_unexpected(part, part_name, ['type', 'name', 'extrude', 'shift', 'rotate', 'operation'])
|
||||||
a.assert(outline, `Field ${part_name}.outline does not name a valid outline!`)
|
const type = a.in(part.type, `${part_name}.type`, ['outline', 'case'])
|
||||||
const extrude = a.sane(part.extrude || 1, `${part_name}.extrude`, 'number')
|
const name = a.sane(part.name, `${part_name}.name`, 'string')
|
||||||
const shift = a.numarr(part.shift || [0, 0, 0], `${part_name}.shift`, 3)
|
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 rotate = a.numarr(part.rotate || [0, 0, 0], `${part_name}.rotate`, 3)
|
||||||
const operation = a.in(part.operation || 'add', `${part_name}.operation`, ['add', 'subtract', 'intersect'])
|
const operation = a.in(part.operation || 'add', `${part_name}.operation`, ['add', 'subtract', 'intersect'])
|
||||||
|
|
||||||
|
let base
|
||||||
|
if (type == 'outline') {
|
||||||
|
const extrude = a.sane(part.extrude || 1, `${part_name}.extrude`, 'number')
|
||||||
|
const outline = outlines[name]
|
||||||
|
a.assert(outline, `Field "${part_name}.name" does not name a valid outline!`)
|
||||||
|
if (!scripts[name]) {
|
||||||
|
scripts[name] = m.exporter.toJscadScript(outline, {
|
||||||
|
functionName: `${name}_outline_fn`,
|
||||||
|
extrude: extrude,
|
||||||
|
indent: 4
|
||||||
|
})
|
||||||
|
}
|
||||||
|
outline_dependencies.push(name)
|
||||||
|
base = `${name}_outline_fn()`
|
||||||
|
} else {
|
||||||
|
a.assert(part.extrude === undefined, `Field "${part_name}.extrude" should not be used when type=case!`)
|
||||||
|
a.in(name, `${part_name}.name`, Object.keys(cases))
|
||||||
|
case_dependencies.push(name)
|
||||||
|
base = `${name}_case_fn()`
|
||||||
|
}
|
||||||
|
|
||||||
let op = 'union'
|
let op = 'union'
|
||||||
if (operation == 'subtract') op = 'subtract'
|
if (operation == 'subtract') op = 'subtract'
|
||||||
else if (operation == 'intersect') op = 'intersect'
|
else if (operation == 'intersect') op = 'intersect'
|
||||||
|
|
||||||
const part_fn = `${part.outline}_fn`
|
let op_statement = `let result = ${part_var};`
|
||||||
const part_var = `${part.outline}_var`
|
|
||||||
|
|
||||||
scripts.push(m.exporter.toJscadScript(outline, {
|
|
||||||
functionName: part_fn,
|
|
||||||
extrude: extrude
|
|
||||||
}))
|
|
||||||
|
|
||||||
let op_statement = `let ${case_name} = ${part_var};`
|
|
||||||
if (part_index > 1) {
|
if (part_index > 1) {
|
||||||
op_statement = `${case_name} = ${case_name}.${op}(${part_var});`
|
op_statement = `result = result.${op}(${part_var});`
|
||||||
}
|
}
|
||||||
|
|
||||||
main.push(`
|
body.push(`
|
||||||
|
|
||||||
// creating part ${part_index} of case ${case_name}
|
// creating part ${part_index} of case ${case_name}
|
||||||
let ${part_var} = ${part_fn}();
|
let ${part_var} = ${base};
|
||||||
|
|
||||||
|
// make sure that rotations are relative
|
||||||
|
let ${part_var}_bounds = ${part_var}.getBounds();
|
||||||
|
let ${part_var}_x = ${part_var}_bounds[0].x + (${part_var}_bounds[1].x - ${part_var}_bounds[0].x) / 2
|
||||||
|
let ${part_var}_y = ${part_var}_bounds[0].y + (${part_var}_bounds[1].y - ${part_var}_bounds[0].y) / 2
|
||||||
|
${part_var} = translate([-${part_var}_x, -${part_var}_y, 0], ${part_var});
|
||||||
${part_var} = rotate(${JSON.stringify(rotate)}, ${part_var});
|
${part_var} = rotate(${JSON.stringify(rotate)}, ${part_var});
|
||||||
|
${part_var} = translate([${part_var}_x, ${part_var}_y, 0], ${part_var});
|
||||||
|
|
||||||
${part_var} = translate(${JSON.stringify(shift)}, ${part_var});
|
${part_var} = translate(${JSON.stringify(shift)}, ${part_var});
|
||||||
${op_statement}
|
${op_statement}
|
||||||
|
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
results[case_name] = `
|
cases[case_name] = {
|
||||||
|
body: `
|
||||||
|
|
||||||
// individual makerjs exports
|
function ${case_name}_case_fn() {
|
||||||
${scripts.join('\n\n')}
|
${body.join('')}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
`,
|
||||||
|
case_dependencies,
|
||||||
|
outline_dependencies
|
||||||
|
}
|
||||||
|
|
||||||
// combination of parts
|
results[case_name] = resolve(case_name)
|
||||||
function main() {
|
|
||||||
${main.join('')}
|
|
||||||
return ${case_name};
|
|
||||||
}
|
|
||||||
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue