Various progress
This commit is contained in:
parent
8d6be0ea00
commit
fad615045a
6 changed files with 135 additions and 65 deletions
29
README.md
29
README.md
|
@ -310,7 +310,9 @@ neighbors: [dir_x, dir_y]
|
||||||
bind: num | [num_x, num_y] | [num_t, num_r, num_b, num_l] # default = 10
|
bind: num | [num_x, num_y] | [num_t, num_r, num_b, num_l] # default = 10
|
||||||
```
|
```
|
||||||
|
|
||||||
The former declares the directions we want to bind in, where `dir_x` can be one of `left`, `right`, or `both`; and `dir_y` can be one of `up`, `down`, or `both`.
|
Again, key-level declaration means that both of these should be specified in the `points` section, benefiting from the same extension process every key-level setting does.
|
||||||
|
The former declares the directions we want to bind in, where `dir_x` can be one of `left`, `right`, `both` or `neither`; and `dir_y` can be one of `up`, `down`, `both` or `neither`.
|
||||||
|
(If left empty, `neither` will be assumed.)
|
||||||
The latter declares how much we want to bind, i.e., the amount of overlap we want in that direction to make sure that we can reach the neighbor (`num` applies to all directions, `num_x` horizontally, `num_y` vertically, and the t/r/b/l versions to top/right/bottom/left, respectively).
|
The latter declares how much we want to bind, i.e., the amount of overlap we want in that direction to make sure that we can reach the neighbor (`num` applies to all directions, `num_x` horizontally, `num_y` vertically, and the t/r/b/l versions to top/right/bottom/left, respectively).
|
||||||
|
|
||||||
If it's a one-piece design, we also need to "glue" the halves together (or we might want to leave some extra space for the controller on the inner side for splits).
|
If it's a one-piece design, we also need to "glue" the halves together (or we might want to leave some extra space for the controller on the inner side for splits).
|
||||||
|
@ -319,11 +321,11 @@ This is where the following section comes into play:
|
||||||
```yaml
|
```yaml
|
||||||
glue:
|
glue:
|
||||||
top:
|
top:
|
||||||
left: <line def>
|
left: <anchor>
|
||||||
right: <line def> | num
|
right: <anchor> | num
|
||||||
bottom:
|
bottom:
|
||||||
left: <line def>
|
left: <anchor>
|
||||||
right: <line def> | num
|
right: <anchor> | num
|
||||||
waypoints:
|
waypoints:
|
||||||
- percent: num
|
- percent: num
|
||||||
width: num | [num_left, num_right]
|
width: num | [num_left, num_right]
|
||||||
|
@ -333,13 +335,12 @@ glue:
|
||||||
- ...
|
- ...
|
||||||
```
|
```
|
||||||
|
|
||||||
...where a `<line def>` looks like:
|
...where an `<anchor>` is the same as it was for points:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
ref: <point reference>
|
ref: <point reference>
|
||||||
shift: [x, y]
|
shift: [x, y] # default = [0, 0]
|
||||||
rotate: num
|
rotate: num # default = 0
|
||||||
origin: [x, y]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The section's `top` and `bottom` are both formatted the same, and describe the center line's top and bottom intersections, respectively.
|
The section's `top` and `bottom` are both formatted the same, and describe the center line's top and bottom intersections, respectively.
|
||||||
|
@ -359,6 +360,7 @@ If this is insufficient (maybe because it would leave holes), the `waypoints` ca
|
||||||
Here, `percent` means the y coordinate along the centerline (going from the top intersection to the bottom intersection), and `width` means the offset on the x axis.
|
Here, `percent` means the y coordinate along the centerline (going from the top intersection to the bottom intersection), and `width` means the offset on the x axis.
|
||||||
|
|
||||||
If this is somehow _still_ insufficient (or there were problems with the binding phase), we can specify additional primitive shapes under the `extra` key (similarly to how we would use them in the exports; see below).
|
If this is somehow _still_ insufficient (or there were problems with the binding phase), we can specify additional primitive shapes under the `extra` key (similarly to how we would use them in the exports; see below).
|
||||||
|
These are then added to what we have so far to finish out the glue.
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
@ -376,7 +378,9 @@ Now we can configure what we want to "export" as outlines from this phase, given
|
||||||
- `side: left | right` : the side we want
|
- `side: left | right` : the side we want
|
||||||
- `glue` : just the glue, but the "ideal" version of it. This means that instead of the `glue` we defined above, we get `all` - `left` - `right`, so the _exact_ middle piece we would have needed to glue everything together. Parameters:
|
- `glue` : just the glue, but the "ideal" version of it. This means that instead of the `glue` we defined above, we get `all` - `left` - `right`, so the _exact_ middle piece we would have needed to glue everything together. Parameters:
|
||||||
- everything we could specify for `all` (since those are needed for the calculation)
|
- everything we could specify for `all` (since those are needed for the calculation)
|
||||||
- `side: left | right | both # default = both)` : optionally, we could choose only one side of the glue as well
|
- `raw: boolean # default = false)` : optionally, we could choose to get the "raw" (i.e., the non-idealized) glue as well
|
||||||
|
- `ref` : a previously defined outline, see below.
|
||||||
|
- `name: outline_name` : the name of the referenced outline
|
||||||
|
|
||||||
Additionally, we can use primitive shapes:
|
Additionally, we can use primitive shapes:
|
||||||
|
|
||||||
|
@ -384,8 +388,7 @@ Additionally, we can use primitive shapes:
|
||||||
- `ref: <point reference>` : what position and rotation to consider as the origin
|
- `ref: <point reference>` : what position and rotation to consider as the origin
|
||||||
- `rotate: num` : extra rotation
|
- `rotate: num` : extra rotation
|
||||||
- `shift: [x, y]` : extra translation
|
- `shift: [x, y]` : extra translation
|
||||||
- `width: num` : the width of the rectangle
|
- `size: num | [width, height]` : the size of the rectangle
|
||||||
- `height: num` : the height of the rectangle
|
|
||||||
- `circle` : an independent circle primitive. Parameters:
|
- `circle` : an independent circle primitive. Parameters:
|
||||||
- `ref`, `rotate`, and `shift` are the same as above
|
- `ref`, `rotate`, and `shift` are the same as above
|
||||||
- `radius: num` : the radius of the circle
|
- `radius: num` : the radius of the circle
|
||||||
|
@ -405,7 +408,7 @@ exports:
|
||||||
```
|
```
|
||||||
|
|
||||||
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 to further export declarations under the name specified (`my_name`, in this case).
|
Additionally, it is going to be available to further export declarations (using 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,28 @@ const detect_unexpected = exports.detect_unexpected = (obj, name, expected) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const xy = exports.xy = (raw, name) => {
|
const numarr = exports.numarr = (raw, name, length) => {
|
||||||
assert(type(raw) == 'array' && raw.length == 2, `Field "${name}" should be an array of length 2!`)
|
assert(type(raw) == 'array' && raw.length == length, `Field "${name}" should be an array of length ${length}!`)
|
||||||
const x = raw[0] || 0
|
raw = raw.map(val => val || 0)
|
||||||
const y = raw[1] || 0
|
raw.map(val => assert(type(val) == 'number', `Field "${name}" should contain numbers!`))
|
||||||
assert(type(x) == 'number' && type(y) == 'number', `Field "${name}" should contain numbers!`)
|
return raw
|
||||||
return {x, y}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.anchor = (raw, name, points={}) => {
|
const xy = exports.xy = (raw, name) => numarr(raw, name, 2)
|
||||||
detect_unexpected(raw, name, ['ref', 'shift', 'rotate'])
|
|
||||||
|
exports.wh = (raw, name) => {
|
||||||
|
if (!Array.isArray(raw)) raw = [raw, raw]
|
||||||
|
return a.xy(raw, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.trbl = (raw, name) => {
|
||||||
|
if (!Array.isArray(raw)) raw = [raw, raw, raw, raw]
|
||||||
|
if (raw.length == 2) raw = [raw[1], raw[0], raw[1], raw[0]]
|
||||||
|
return numarr(raw, name, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.anchor = (raw, name, points={}, check_unexpected=true) => {
|
||||||
|
if (check_unexpected) detect_unexpected(raw, name, ['ref', 'shift', 'rotate'])
|
||||||
let a = new Point()
|
let a = new Point()
|
||||||
if (raw.ref !== undefined) {
|
if (raw.ref !== undefined) {
|
||||||
assert(points[raw.ref], `Unknown point reference "${raw.ref}" in anchor "${name}"!`)
|
assert(points[raw.ref], `Unknown point reference "${raw.ref}" in anchor "${name}"!`)
|
||||||
|
@ -41,8 +53,8 @@ exports.anchor = (raw, name, points={}) => {
|
||||||
}
|
}
|
||||||
if (raw.shift !== undefined) {
|
if (raw.shift !== undefined) {
|
||||||
const xyval = xy(raw.shift, name + '.shift')
|
const xyval = xy(raw.shift, name + '.shift')
|
||||||
a.x += xyval.x
|
a.x += xyval[0]
|
||||||
a.y += xyval.y
|
a.y += xyval[1]
|
||||||
}
|
}
|
||||||
if (raw.rotate !== undefined) {
|
if (raw.rotate !== undefined) {
|
||||||
a.r += sane(raw.rotate || 0, name + '.rotate', 'number')
|
a.r += sane(raw.rotate || 0, name + '.rotate', 'number')
|
||||||
|
|
49
src/cli.js
49
src/cli.js
|
@ -1,33 +1,26 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
const m = require('makerjs')
|
// libs
|
||||||
|
|
||||||
const fs = require('fs-extra')
|
const fs = require('fs-extra')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const yaml = require('js-yaml')
|
const yaml = require('js-yaml')
|
||||||
const yargs = require('yargs')
|
const yargs = require('yargs')
|
||||||
|
|
||||||
|
// internals
|
||||||
|
|
||||||
const u = require('./utils')
|
const u = require('./utils')
|
||||||
|
const io = require('./io')
|
||||||
const points_lib = require('./points')
|
const points_lib = require('./points')
|
||||||
// const outline_lib = require('./outline')
|
const outline_lib = require('./outline')
|
||||||
|
|
||||||
const dump_model = (model, file='model') => {
|
// command line args
|
||||||
const assembly = m.model.originate({
|
|
||||||
models: u.deepcopy(model),
|
|
||||||
units: 'mm'
|
|
||||||
})
|
|
||||||
|
|
||||||
fs.mkdirpSync(path.dirname(`${file}.dxf`))
|
|
||||||
fs.writeFileSync(`${file}.dxf`, m.exporter.toDXF(assembly))
|
|
||||||
if (args.debug) {
|
|
||||||
fs.writeJSONSync(`${file}.json`, assembly, {spaces: 4})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = yargs
|
const args = yargs
|
||||||
.option('config', {
|
.option('config', {
|
||||||
alias: 'c',
|
alias: 'c',
|
||||||
demandOption: true,
|
demandOption: true,
|
||||||
describe: 'Config yaml file',
|
describe: 'Config yaml/json file',
|
||||||
type: 'string'
|
type: 'string'
|
||||||
})
|
})
|
||||||
.option('output', {
|
.option('output', {
|
||||||
|
@ -37,8 +30,9 @@ const args = yargs
|
||||||
type: 'string'
|
type: 'string'
|
||||||
})
|
})
|
||||||
.option('debug', {
|
.option('debug', {
|
||||||
|
alias: 'd',
|
||||||
default: false,
|
default: false,
|
||||||
hidden: true,
|
describe: 'Debug mode',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
})
|
})
|
||||||
.argv
|
.argv
|
||||||
|
@ -46,15 +40,28 @@ const args = yargs
|
||||||
fs.mkdirpSync(args.o)
|
fs.mkdirpSync(args.o)
|
||||||
|
|
||||||
const config_parser = args.c.endsWith('.yaml') ? yaml.load : JSON.parse
|
const config_parser = args.c.endsWith('.yaml') ? yaml.load : JSON.parse
|
||||||
const config = config_parser(fs.readFileSync(args.c).toString())
|
let config
|
||||||
|
try {
|
||||||
|
config = config_parser(fs.readFileSync(args.c).toString())
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(`Malformed input "${args.c}": ${err}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// points
|
||||||
|
|
||||||
|
console.log('Parsing points...')
|
||||||
const points = points_lib.parse(config.points)
|
const points = points_lib.parse(config.points)
|
||||||
if (args.debug) {
|
if (args.debug) {
|
||||||
fs.writeJSONSync(path.join(args.o, 'points.json'), points, {spaces: 4})
|
fs.writeJSONSync(path.join(args.o, 'points.json'), points, {spaces: 4})
|
||||||
const size = 14
|
const rect = u.rect(18, 18, [-9, -9])
|
||||||
const rect = u.rect(size, size, [-size/2, -size/2])
|
const points_demo = points_lib.position(points, rect)
|
||||||
const points_demo = outline_lib.layout(points, rect)
|
io.dump_model(points_demo, path.join(args.o, 'points_demo'), args.debug)
|
||||||
dump_model(points_demo, path.join(args.o, 'points_demo'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// outlines
|
||||||
|
|
||||||
|
// console.log('Generating outlines...')
|
||||||
|
|
||||||
|
// goodbye
|
||||||
|
|
||||||
console.log('Done.')
|
console.log('Done.')
|
||||||
|
|
18
src/io.js
Normal file
18
src/io.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
const m = require('makerjs')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const u = require('./utils')
|
||||||
|
|
||||||
|
exports.dump_model = (model, file='model', debug=false) => {
|
||||||
|
const assembly = m.model.originate({
|
||||||
|
models: u.deepcopy(model),
|
||||||
|
units: 'mm'
|
||||||
|
})
|
||||||
|
|
||||||
|
fs.mkdirpSync(path.dirname(`${file}.dxf`))
|
||||||
|
fs.writeFileSync(`${file}.dxf`, m.exporter.toDXF(assembly))
|
||||||
|
if (debug) {
|
||||||
|
fs.writeJSONSync(`${file}.json`, assembly, {spaces: 4})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,14 @@
|
||||||
const m = require('makerjs')
|
const m = require('makerjs')
|
||||||
const u = require('./utils')
|
const u = require('./utils')
|
||||||
|
const a = require('./assert')
|
||||||
const layout = exports.layout = (points, shape) => {
|
|
||||||
const shapes = {}
|
|
||||||
for (const [pname, p] of Object.entries(points)) {
|
|
||||||
shapes[pname] = p.position(u.deepcopy(shape))
|
|
||||||
}
|
|
||||||
return {layout: {models: shapes}}
|
|
||||||
}
|
|
||||||
|
|
||||||
const outline = exports._outline = (points, config={}) => params => {
|
const outline = exports._outline = (points, config={}) => params => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let size = params.size || [18, 18]
|
const size = a.wh(params.size || [18, 18], '')
|
||||||
if (!Array.isArray(size)) size = [size, size]
|
if (!Array.isArray(size)) size = [size, size]
|
||||||
|
size = a.xy(size, `outline.exports.${params.name}.size`)
|
||||||
const corner = params.corner || 0
|
const corner = params.corner || 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,3 +136,39 @@ const outline = exports._outline = (points, config={}) => params => {
|
||||||
u.dump_model({a: glue, b: left_keys, c: {models: right_keys}}, `all_after_left`)
|
u.dump_model({a: glue, b: left_keys, c: {models: right_keys}}, `all_after_left`)
|
||||||
glue = m.model.combineUnion(glue, right_keys)
|
glue = m.model.combineUnion(glue, right_keys)
|
||||||
u.dump_model({a: glue, b: {models: keys}}, `fullll`)
|
u.dump_model({a: glue, b: {models: keys}}, `fullll`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const parse_glue = exports._parse_glue = (config = {}, points = {}) => {
|
||||||
|
|
||||||
|
a.detect_unexpected(config, 'outline.glue', ['top', 'bottom', 'waypoints', 'extra'])
|
||||||
|
|
||||||
|
for (const y in ['top', 'bottom']) {
|
||||||
|
a.detect_unexpected(config[y], `outline.glue.${y}`, ['left', 'right'])
|
||||||
|
config[y].left = a.anchor(config[y].left, `outline.glue.${y}.left`, points)
|
||||||
|
if (a.type(config[y].right) != 'number') {
|
||||||
|
config[y].right = a.anchor(config[y].right, `outline.glue.${y}.right`, points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
config.bottom = a.sane(config.bottom, 'outline.glue.bottom', 'object')
|
||||||
|
config.bottom.left = a.anchor(config.bottom.left, 'outline.glue.bottom.left', points)
|
||||||
|
if (a.type(config.bottom.right) != 'number') {
|
||||||
|
config.bottom.right = a.anchor(config.bottom.right, 'outline.glue.bottom.right', points)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.parse = (config = {}, points = {}) => {
|
||||||
|
a.detect_unexpected(config, 'outline', ['glue', 'exports'])
|
||||||
|
const glue = parse_glue(config.glue, points)
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ const extend_pair = exports._extend_pair = (to, from) => {
|
||||||
if (from === undefined || from === null) return to
|
if (from === undefined || from === null) return to
|
||||||
if (to_type != from_type) return from
|
if (to_type != from_type) return from
|
||||||
if (from_type == 'object') {
|
if (from_type == 'object') {
|
||||||
const res = {}
|
const res = u.deepcopy(to)
|
||||||
for (const key of Object.keys(from)) {
|
for (const key of Object.keys(from)) {
|
||||||
res[key] = extend_pair(to[key], from[key])
|
res[key] = extend_pair(to[key], from[key])
|
||||||
}
|
}
|
||||||
|
@ -147,16 +147,6 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor) => {
|
||||||
col.rows[row] || {}
|
col.rows[row] || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
require('fs-extra').writeJSONSync('arst.json', {
|
|
||||||
default_key,
|
|
||||||
zone_wide_key,
|
|
||||||
col_key: col.key,
|
|
||||||
zone_wide_rows: zone_wide_rows[row] || {},
|
|
||||||
col_rows: col.rows[row] || {},
|
|
||||||
result: key
|
|
||||||
}, {spaces: 4})
|
|
||||||
throw 28
|
|
||||||
|
|
||||||
key.name = key.name || `${col_name}_${row}`
|
key.name = key.name || `${col_name}_${row}`
|
||||||
key.shift = a.xy(key.shift, `${key.name}.shift`)
|
key.shift = a.xy(key.shift, `${key.name}.shift`)
|
||||||
key.rotate = a.sane(key.rotate, `${key.name}.rotate`, 'number')
|
key.rotate = a.sane(key.rotate, `${key.name}.rotate`, 'number')
|
||||||
|
@ -206,7 +196,9 @@ const render_zone = exports._render_zone = (zone_name, zone, anchor) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.parse = (config) => {
|
exports.parse = (config = {}) => {
|
||||||
|
|
||||||
|
a.detect_unexpected(config, 'points', ['zones', 'rotate', 'mirror'])
|
||||||
|
|
||||||
let points = {}
|
let points = {}
|
||||||
|
|
||||||
|
@ -263,4 +255,12 @@ exports.parse = (config) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return filtered
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.position = (points, shape) => {
|
||||||
|
const shapes = {}
|
||||||
|
for (const [pname, p] of Object.entries(points)) {
|
||||||
|
shapes[pname] = p.position(u.deepcopy(shape))
|
||||||
|
}
|
||||||
|
return {layout: {models: shapes}}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue