Generalize case combinations, relativize rotations

This commit is contained in:
Bán Dénes 2020-10-09 19:18:53 +02:00
parent 23ed8989ce
commit 5b1da540ac
2 changed files with 95 additions and 34 deletions

View file

@ -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

View file

@ -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