Bringing points stuff up to spec
This commit is contained in:
parent
0ab5a246e5
commit
a5e686b059
11 changed files with 474 additions and 108 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -113,4 +113,7 @@ dist
|
|||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
.pnp.*
|
||||
|
||||
# Project specific
|
||||
output
|
24
README.md
24
README.md
|
@ -87,7 +87,7 @@ In the following, we'll have an in-depth discussion about each of these, with an
|
|||
## Points
|
||||
|
||||
A point in this context refers to a 2D point `[x,y]` with a rotation/orientation `r` added in.
|
||||
These can be thought of as the middle points of the keycaps in a resulting keyboard layout, with an additional handling of and angle of the keycap.
|
||||
These can be thought of as the middle points of the keycaps in a resulting keyboard layout, with an additional handling of the angle of the keycap.
|
||||
|
||||
What makes this generator "ergo" is the implicit focus on the column-stagger.
|
||||
Of course we could simulate the traditional row-stagger by defining everything with a 90 degree rotation, but that's really not the goal here.
|
||||
|
@ -172,7 +172,7 @@ But if `key = {a: 1}` is extended by `key = {b: 2}`, the result is `key = {a: 1,
|
|||
|
||||
Lastly, while there are a few key-specific attributes that have special meaning in the context of points (listed below), any key with any data can be specified here.
|
||||
This can be useful for storing arbitrary meta-info about the keys, or just configuring later stages with key-level parameters.
|
||||
So, for example, when the outline phase specifies `bind` as a key-level parameter, it means that the global value can be extended just like any other key-level attribute.
|
||||
So, for example, when the outline phase specifies `bind` as a key-level parameter (see below), it means that the global value can be extended just like any other key-level attribute.
|
||||
|
||||
Now for the "official" key-level attributes:
|
||||
|
||||
|
@ -181,13 +181,15 @@ name: name_override # default = a concatenation of column and row
|
|||
shift: [x, y] # default = [0, 0]
|
||||
rotate: num # default = 0
|
||||
origin: [x, y] # default = the center of the key
|
||||
padding: num # default = 19
|
||||
skip: boolean # default = false
|
||||
asym: left|right|both # default = both
|
||||
asym: left | right | both # default = both
|
||||
```
|
||||
|
||||
`name` is the unique identifier of this specific key.
|
||||
It defaults to a `<row>_<column>` format, but can be overridden if necessary.
|
||||
`shift` and `rotate`/`origin` declare an extra, key-level translation or rotation, respectively.
|
||||
Then we leave `padding` amount of vertical space before moving on to the next key in the column.
|
||||
`skip` signals that the point is just a "helper" and should not be included in the output.
|
||||
This can happen when a _real_ point is more easily calculable through a "stepping stone", but then we don't actually want the stepping stone to be a key itself.
|
||||
Finally, `asym` relates to mirroring, which we'll cover in a second.
|
||||
|
@ -339,13 +341,11 @@ ref: <point reference>
|
|||
shift: [x, y]
|
||||
rotate: num
|
||||
origin: [x, y]
|
||||
relative: boolean # default = false
|
||||
```
|
||||
|
||||
The section's `top` and `bottom` are both formatted the same, and describe the center line's top and bottom intersections, respectively.
|
||||
In a one-piece case, this means that we project a line from a left-side reference point (optionally rotated and translated), another from the right, and converge them to where they meet.
|
||||
Split designs can specify `right` as a single number to mean the x coordinate where the side should be "cut off".
|
||||
(The `relative` flag means the unit of the translation specified in `shift` is not mm, but the size the point is laid out with; see below.)
|
||||
|
||||
This leads to a gluing middle patch that can be used to meld the left and right sides together, given by the counter-clockwise polygon:
|
||||
|
||||
|
@ -371,13 +371,13 @@ Now we can configure what we want to "export" as outlines from this phase, given
|
|||
- `all` : the combined outline that we've just created. Its parameters include:
|
||||
- `size: num | [num_x, num_y]` : the width/height of the rectangles to lay onto the points
|
||||
- `corner: num # default = 0)` : corner radius of the rectangle
|
||||
- `corner_style: rounded | beveled # default = rounded)` : the styleof the rectangle's corners
|
||||
- `bevel: num # default = 0)` : corner bevel of the rectangle, can be combined with rounding
|
||||
- `keys` : only one side of the laid out keys, without the glue. Parameters:
|
||||
- everything we could specify for `all`
|
||||
- `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:
|
||||
- everything we could specify for `all` (since those are needed for the calculation)
|
||||
- `side: left | right | both # default = both)` : optionally, wecould choose only one side of the glue as well
|
||||
- `side: left | right | both # default = both)` : optionally, we could choose only one side of the glue as well
|
||||
|
||||
Additionally, we can use primitive shapes:
|
||||
|
||||
|
@ -396,7 +396,6 @@ Additionally, we can use primitive shapes:
|
|||
|
||||
Using these, we define exports as follows:
|
||||
|
||||
|
||||
```yaml
|
||||
exports:
|
||||
my_name:
|
||||
|
@ -476,7 +475,7 @@ If we only want to use it as a building block for further exports, we can start
|
|||
## Case
|
||||
|
||||
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, optionally add some chamfer to the edges, and combine them into one 3D-printable object.
|
||||
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.
|
||||
That's it.
|
||||
Declarations might look like this:
|
||||
|
||||
|
@ -485,17 +484,16 @@ case:
|
|||
case_name:
|
||||
- outline: <outline ref>
|
||||
extrude: num # default = 1
|
||||
chamfer: [num_top, num_bottom] # default = [0, 0]
|
||||
translate: [x, y, z] # default = [0, 0, 0]
|
||||
rotate: [ax, ay, az] # default = [0, 0, 0]
|
||||
op: add|sub|diff # default = add
|
||||
op: add | sub | diff # default = add
|
||||
- ...
|
||||
...
|
||||
```
|
||||
|
||||
`outline` specifies which outline to import onto the xy plane, while `extrude` specifies how much it should be extruded along the z axis.
|
||||
After the camfer to the top and/or bottom outside edges, the object is `translate`d/`rotate`d, and combined with what we have so far according to `op`.
|
||||
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.
|
||||
After that, the object is `translate`d, `rotate`d, and combined with what we have so far according to `op`.
|
||||
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".
|
||||
|
||||
|
||||
|
||||
|
|
10
package.json
10
package.json
|
@ -11,7 +11,7 @@
|
|||
"bin": "./src/cli.js",
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"test": "nyc mocha -r chai/register-should"
|
||||
"test": "nyc --reporter=html --reporter=text mocha -r chai/register-should"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "^9.0.1",
|
||||
|
@ -28,7 +28,11 @@
|
|||
},
|
||||
"nyc": {
|
||||
"all": true,
|
||||
"include": ["src/*.js"],
|
||||
"exclude": ["src/cli.js"]
|
||||
"include": [
|
||||
"src/*.js"
|
||||
],
|
||||
"exclude": [
|
||||
"src/cli.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
65
src/cli.js
65
src/cli.js
|
@ -1,11 +1,28 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const m = require('makerjs')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const yaml = require('js-yaml')
|
||||
const yargs = require('yargs')
|
||||
|
||||
const points_lib = require('../helpers/points')
|
||||
const u = require('./utils')
|
||||
const points_lib = require('./points')
|
||||
const outline_lib = require('./outline')
|
||||
|
||||
const dump_model = (model, file='model') => {
|
||||
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
|
||||
.option('config', {
|
||||
alias: 'c',
|
||||
|
@ -24,48 +41,20 @@ const args = yargs
|
|||
hidden: true,
|
||||
type: 'boolean'
|
||||
})
|
||||
.option('outline', {
|
||||
default: true,
|
||||
describe: 'Generate 2D outlines',
|
||||
type: 'boolean'
|
||||
})
|
||||
.option('pcb', {
|
||||
default: false,
|
||||
describe: 'Generate PCB draft',
|
||||
type: 'boolean'
|
||||
})
|
||||
.option('case', {
|
||||
default: false,
|
||||
describe: 'Generate case files',
|
||||
type: 'boolean'
|
||||
})
|
||||
.argv
|
||||
|
||||
if (!args.outline && !args.pcb && !args.case) {
|
||||
yargs.showHelp('log')
|
||||
console.log('Nothing to do...')
|
||||
process.exit(0)
|
||||
}
|
||||
fs.mkdirpSync(args.o)
|
||||
|
||||
const config = yaml.load(fs.readFileSync(args.c).toString())
|
||||
const points = points_lib.parse(config)
|
||||
const config_parser = args.c.endsWith('.yaml') ? yaml.load : JSON.parse
|
||||
const config = config_parser(fs.readFileSync(args.c).toString())
|
||||
|
||||
const points = points_lib.parse(config.points)
|
||||
if (args.debug) {
|
||||
points_lib.dump(points)
|
||||
}
|
||||
|
||||
if (args.outline) {
|
||||
outline_lib.draw(points, config)
|
||||
fs.writeJSONSync(path.join(args.o, 'points.json'), points, {spaces: 4})
|
||||
const size = 14
|
||||
const rect = u.rect(size, size, [-size/2, -size/2])
|
||||
const points_demo = outline_lib.layout(points, rect)
|
||||
dump_model(points_demo, path.join(args.o, 'points_demo'))
|
||||
}
|
||||
|
||||
console.log('Done.')
|
||||
|
||||
// exports.dump_model = (model, file='model', json=false) => {
|
||||
// const assembly = m.model.originate({
|
||||
// models: deepcopy(model),
|
||||
// units: 'mm'
|
||||
// })
|
||||
|
||||
// if (json) fs.writeFileSync(`${file}.json`, JSON.stringify(assembly, null, ' '))
|
||||
// fs.writeFileSync(`${file}.dxf`, m.exporter.toDXF(assembly))
|
||||
// }
|
|
@ -1,23 +1,25 @@
|
|||
const m = require('makerjs')
|
||||
const fs = require('fs-extra')
|
||||
const assert = require('assert').strict
|
||||
|
||||
const u = require('./utils')
|
||||
|
||||
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 = (points, config) => {
|
||||
let size = params.size || [18, 18]
|
||||
if (!Array.isArray(size)) size = [size, size]
|
||||
const corner = params.corner || 0
|
||||
|
||||
assert.ok(config.outline)
|
||||
const footprint = config.outline.footprint || 18
|
||||
const corner = config.outline.corner || 0
|
||||
const global_bind = config.outline.bind || 5
|
||||
const global_bind = config.bind || 5
|
||||
|
||||
let glue = {paths: {}}
|
||||
|
||||
if (config.outline.glue) {
|
||||
|
||||
const glue_conf = config.outline.glue
|
||||
if (config.glue) {
|
||||
|
||||
const internal_part = (line) => {
|
||||
// taking the middle part only, so that we don't interfere with corner rounding
|
||||
|
@ -25,23 +27,33 @@ const outline = (points, config) => {
|
|||
}
|
||||
|
||||
const get_line = (def={}) => {
|
||||
const point = points[def.key]
|
||||
if (!point) throw new Error(`Point ${def.key} not found...`)
|
||||
const ref = points[def.ref]
|
||||
if (!ref) throw new Error(`Point ${def.ref} not found...`)
|
||||
|
||||
let from = [0, 0]
|
||||
let to = [ref.meta.mirrored ? -1 : 1, 0]
|
||||
|
||||
// todo: position according to point to get the lines...
|
||||
|
||||
let point = ref.clone().shift(def.shift || [0, 0])
|
||||
point.rotate(def.rotate || 0, point.add(def.origin || [0, 0]))
|
||||
|
||||
|
||||
const rect = m.model.originate(point.rect(footprint))
|
||||
line = rect.paths[def.line || 'top']
|
||||
return internal_part(line)
|
||||
}
|
||||
|
||||
assert.ok(glue_conf.top)
|
||||
const tll = get_line(glue_conf.top.left)
|
||||
const trl = get_line(glue_conf.top.right)
|
||||
assert.ok(config.glue.top)
|
||||
const tll = get_line(config.glue.top.left)
|
||||
const trl = get_line(config.glue.top.right)
|
||||
const tip = m.path.converge(tll, trl)
|
||||
const tlp = u.eq(tll.origin, tip) ? tll.end : tll.origin
|
||||
const trp = u.eq(trl.origin, tip) ? trl.end : trl.origin
|
||||
|
||||
assert.ok(glue_conf.bottom)
|
||||
const bll = get_line(glue_conf.bottom.left)
|
||||
const brl = get_line(glue_conf.bottom.right)
|
||||
assert.ok(config.glue.bottom)
|
||||
const bll = get_line(config.glue.bottom.left)
|
||||
const brl = get_line(config.glue.bottom.right)
|
||||
const bip = m.path.converge(bll, brl)
|
||||
const blp = u.eq(bll.origin, bip) ? bll.end : bll.origin
|
||||
const brp = u.eq(brl.origin, bip) ? brl.end : brl.origin
|
||||
|
@ -49,7 +61,7 @@ const outline = (points, config) => {
|
|||
const left_waypoints = []
|
||||
const right_waypoints = []
|
||||
|
||||
for (const w of glue_conf.waypoints || []) {
|
||||
for (const w of config.glue.waypoints || []) {
|
||||
const percent = w.percent / 100
|
||||
const center_x = tip[0] + percent * (bip[0] - tip[0])
|
||||
const center_y = tip[1] + percent * (bip[1] - tip[1])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const m = require('makerjs')
|
||||
const u = require('./utils')
|
||||
|
||||
class Point {
|
||||
module.exports = class Point {
|
||||
constructor(x=0, y=0, r=0, meta={}) {
|
||||
if (Array.isArray(x)) {
|
||||
this.x = x[0]
|
||||
|
@ -67,5 +67,3 @@ class Point {
|
|||
return this.position(rect)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Point
|
102
src/points.js
102
src/points.js
|
@ -1,7 +1,37 @@
|
|||
const m = require('makerjs')
|
||||
const u = require('./utils')
|
||||
const Point = require('./point')
|
||||
|
||||
const push_rotation = (list, angle, origin) => {
|
||||
const extend_pair = exports._extend_pair = (to, from) => {
|
||||
const to_type = u.type(to)
|
||||
const from_type = u.type(from)
|
||||
if (!from && ['array', 'object'].includes(to_type)) return to
|
||||
if (to_type != from_type) return from
|
||||
if (from_type == 'object') {
|
||||
const res = {}
|
||||
for (const key of Object.keys(from)) {
|
||||
res[key] = extend_pair(to[key], from[key])
|
||||
}
|
||||
return res
|
||||
} else if (from_type == 'array') {
|
||||
const res = u.deepcopy(to)
|
||||
for (const [i, val] of from.entries()) {
|
||||
res[i] = extend_pair(res[i], val)
|
||||
}
|
||||
return res
|
||||
} else return from
|
||||
}
|
||||
|
||||
const extend = exports._extend = (...args) => {
|
||||
let res = args[0]
|
||||
for (const arg of args) {
|
||||
if (res == arg) continue
|
||||
res = extend_pair(res, arg)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const push_rotation = exports._push_rotation = (list, angle, origin) => {
|
||||
let candidate = origin
|
||||
for (const r of list) {
|
||||
candidate = m.point.rotate(candidate, r.angle, r.origin)
|
||||
|
@ -12,9 +42,8 @@ const push_rotation = (list, angle, origin) => {
|
|||
})
|
||||
}
|
||||
|
||||
const render_zone = (cols, rows, anchor=new Point(), reverse=false) => {
|
||||
const render_zone = exports._render_zone = (cols, rows, zone_wide_key, anchor) => {
|
||||
|
||||
const sign = reverse ? -1 : 1
|
||||
const points = {}
|
||||
const rotations = []
|
||||
|
||||
|
@ -24,7 +53,7 @@ const render_zone = (cols, rows, anchor=new Point(), reverse=false) => {
|
|||
origin: anchor.p
|
||||
})
|
||||
|
||||
for (const col of cols) {
|
||||
for (const [colname, col] of Object.entries(cols)) {
|
||||
|
||||
anchor.y += col.stagger || 0
|
||||
const col_anchor = anchor.clone()
|
||||
|
@ -33,26 +62,36 @@ const render_zone = (cols, rows, anchor=new Point(), reverse=false) => {
|
|||
col_anchor.r = 0
|
||||
|
||||
// combine row data from zone-wide defs and col-specific defs
|
||||
const col_specific = col.rows || []
|
||||
const zone_wide = rows || []
|
||||
const actual_rows = []
|
||||
for (let i = 0; i < zone_wide.length && i < col_specific.length; ++i) {
|
||||
actual_rows.push(Object.assign({}, zone_wide[i], col_specific[i]))
|
||||
const col_specific_rows = col.rows || {}
|
||||
const zone_wide_rows = rows || {}
|
||||
const actual_rows = col_specific_rows || zone_wide_rows
|
||||
|
||||
// get key config through the 4-level extension
|
||||
const keys = []
|
||||
for (const row of Object.keys(actual_rows)) {
|
||||
const key = extend(zone_wide_key, col.key || {}, zone_wide_rows[row] || {}, col_specific_rows[row] || {})
|
||||
key.col = col
|
||||
key.row = row
|
||||
key.name = `${colname}_${row}`
|
||||
keys.push(key)
|
||||
}
|
||||
|
||||
for (const row of actual_rows) {
|
||||
// lay out keys
|
||||
for (const key of keys) {
|
||||
let point = col_anchor.clone()
|
||||
for (const r of rotations) {
|
||||
point.rotate(r.angle, r.origin)
|
||||
}
|
||||
point.r += col.angle || 0
|
||||
const name = `${col.name}_${row.name}`
|
||||
point.meta = {col, row, name}
|
||||
points[name] = point
|
||||
if (key.rotate) {
|
||||
point.rotate(key.rotate, point.add(key.origin || [0, 0]).p)
|
||||
}
|
||||
point.meta = key
|
||||
points[key.name] = point
|
||||
|
||||
col_anchor.y += row.padding || 19
|
||||
col_anchor.y += key.padding || 19
|
||||
}
|
||||
|
||||
// apply col-level rotation for the next columns
|
||||
if (col.rotate) {
|
||||
push_rotation(
|
||||
rotations,
|
||||
|
@ -61,23 +100,23 @@ const render_zone = (cols, rows, anchor=new Point(), reverse=false) => {
|
|||
)
|
||||
}
|
||||
|
||||
anchor.x += sign * (col.padding || 19)
|
||||
anchor.x += col.spread || 19
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
const anchor = (raw, points={}) => {
|
||||
const anchor = exports._anchor = (raw, points={}) => {
|
||||
let a = new Point()
|
||||
if (raw) {
|
||||
if (raw.ref && points[raw.ref]) {
|
||||
a = points[raw.ref].clone()
|
||||
}
|
||||
if (raw.shift) {
|
||||
a.x += raw.shift[0]
|
||||
a.y += raw.shift[1]
|
||||
a.x += raw.shift[0] || 0
|
||||
a.y += raw.shift[1] || 0
|
||||
}
|
||||
a.r += raw.angle || 0
|
||||
a.r += raw.rotate || 0
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
@ -90,29 +129,32 @@ exports.parse = (config) => {
|
|||
points = Object.assign(points, render_zone(
|
||||
zone.columns || [],
|
||||
zone.rows || [{name: 'default'}],
|
||||
anchor(zone.anchor, points),
|
||||
!!zone.reverse
|
||||
zone.key || {},
|
||||
anchor(zone.anchor, points)
|
||||
))
|
||||
}
|
||||
|
||||
if (config.angle) {
|
||||
if (config.rotate) {
|
||||
for (const p of Object.values(points)) {
|
||||
p.rotate(config.angle)
|
||||
p.rotate(config.rotate)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.mirror) {
|
||||
let axis = anchor(config.mirror, points).x
|
||||
axis += (config.mirror.distance || 0) / 2
|
||||
let axis = config.mirror.axis
|
||||
if (!axis) {
|
||||
axis = anchor(config.mirror, points).x
|
||||
axis += (config.mirror.distance || 0) / 2
|
||||
}
|
||||
const mirrored_points = {}
|
||||
for (const [name, p] of Object.entries(points)) {
|
||||
if (p.meta.col.asym == 'left' || p.meta.row.asym == 'left') continue
|
||||
if (p.meta.asym == 'left') continue
|
||||
const mp = p.clone().mirror(axis)
|
||||
mp.meta.mirrored = true
|
||||
delete mp.meta.asym
|
||||
mirrored_points[`mirror_${name}`] = mp
|
||||
if (p.meta.col.asym == 'right' || p.meta.row.asym == 'right') {
|
||||
p.meta.col.skip = true
|
||||
if (p.meta.asym == 'right') {
|
||||
p.meta.skip = true
|
||||
}
|
||||
}
|
||||
Object.assign(points, mirrored_points)
|
||||
|
@ -120,7 +162,7 @@ exports.parse = (config) => {
|
|||
|
||||
const filtered = {}
|
||||
for (const [k, p] of Object.entries(points)) {
|
||||
if (p.meta.col.skip || p.meta.row.skip) continue
|
||||
if (p.meta.skip) continue
|
||||
filtered[k] = p
|
||||
}
|
||||
|
||||
|
|
12
src/utils.js
12
src/utils.js
|
@ -1,7 +1,19 @@
|
|||
const m = require('makerjs')
|
||||
|
||||
exports.assert = (exp, msg) => {
|
||||
if (!exp) {
|
||||
throw new Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
exports.deepcopy = (value) => JSON.parse(JSON.stringify(value))
|
||||
|
||||
exports.type = (val) => {
|
||||
if (Array.isArray(val)) return 'array'
|
||||
if (val === null) return 'null'
|
||||
return typeof val
|
||||
}
|
||||
|
||||
const eq = exports.eq = (a=[], b=[]) => {
|
||||
return a[0] === b[0] && a[1] === b[1]
|
||||
}
|
||||
|
|
17
test/complex.js
Normal file
17
test/complex.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const yaml = require('js-yaml')
|
||||
const points_lib = require('../src/points')
|
||||
|
||||
const fixtures = path.join(__dirname, 'fixtures')
|
||||
const absolem_config = yaml.load(fs.readFileSync(path.join(fixtures, 'absolem.yaml')).toString())
|
||||
|
||||
describe('Absolem', function() {
|
||||
it('#points', function() {
|
||||
const expected = fs.readJSONSync(path.join(fixtures, 'absolem_points.json'))
|
||||
const actual = points_lib.parse(absolem_config.points)
|
||||
// remove metadata, so that it only checks the points
|
||||
Object.values(actual).map(val => delete val.meta)
|
||||
actual.should.deep.equal(expected)
|
||||
})
|
||||
})
|
109
test/fixtures/absolem.yaml
vendored
Normal file
109
test/fixtures/absolem.yaml
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
points:
|
||||
zones:
|
||||
matrix:
|
||||
anchor:
|
||||
rotate: 5
|
||||
columns:
|
||||
pinky:
|
||||
rotate: -5
|
||||
origin: [7, -7]
|
||||
rows:
|
||||
bottom:
|
||||
home:
|
||||
neighbors: [right]
|
||||
top:
|
||||
neighbors: [right]
|
||||
ring:
|
||||
stagger: 12
|
||||
rows:
|
||||
bottom:
|
||||
neighbors: [left]
|
||||
home:
|
||||
neighbors: [right]
|
||||
top:
|
||||
neighbors: [right]
|
||||
middle:
|
||||
stagger: 5
|
||||
rows:
|
||||
bottom:
|
||||
neighbors: [both]
|
||||
home:
|
||||
neighbors: [both]
|
||||
top:
|
||||
index:
|
||||
stagger: -6
|
||||
rows:
|
||||
bottom:
|
||||
neighbors: [right]
|
||||
home:
|
||||
neighbors: [left]
|
||||
top:
|
||||
neighbors: [left]
|
||||
inner:
|
||||
stagger: -2
|
||||
rows:
|
||||
bottom:
|
||||
home:
|
||||
neighbors: [left]
|
||||
top:
|
||||
neighbors: [left]
|
||||
rows:
|
||||
bottom:
|
||||
neighbors: [,up]
|
||||
home:
|
||||
neighbors: [,up]
|
||||
top:
|
||||
thumbfan:
|
||||
anchor:
|
||||
ref: inner_bottom
|
||||
shift: [-7, -19]
|
||||
columns:
|
||||
near:
|
||||
spread: 21.25
|
||||
rotate: -28
|
||||
origin: [9.5, -9]
|
||||
rows:
|
||||
thumb:
|
||||
neighbors: [right]
|
||||
home:
|
||||
spread: 21.25
|
||||
rotate: -28
|
||||
origin: [11.75, -9]
|
||||
rows:
|
||||
thumb:
|
||||
neighbors: [both]
|
||||
far:
|
||||
rows:
|
||||
thumb:
|
||||
neighbors: [left]
|
||||
rows:
|
||||
thumb:
|
||||
neighbors: [,up]
|
||||
rotate: -20
|
||||
mirror:
|
||||
ref: pinky_home
|
||||
distance: 223.7529778
|
||||
outline:
|
||||
bind: 10
|
||||
glue:
|
||||
top:
|
||||
left:
|
||||
ref: inner_top
|
||||
shift: [,7]
|
||||
right:
|
||||
ref: mirror_inner_top
|
||||
shift: [,7]
|
||||
bottom:
|
||||
left:
|
||||
ref: far_thumb
|
||||
shift: [7]
|
||||
rotate: 90
|
||||
right:
|
||||
ref: mirror_far_thumb
|
||||
shift: [-7]
|
||||
rotate: 90
|
||||
waypoints:
|
||||
- percent: 50
|
||||
width: 100
|
||||
- percent: 90
|
||||
width: 50
|
182
test/fixtures/absolem_points.json
vendored
Normal file
182
test/fixtures/absolem_points.json
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
"pinky_bottom": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"r": -15
|
||||
},
|
||||
"pinky_home": {
|
||||
"x": 4.9175619,
|
||||
"y": 18.3525907,
|
||||
"r": -15
|
||||
},
|
||||
"pinky_top": {
|
||||
"x": 9.8351237,
|
||||
"y": 36.7051814,
|
||||
"r": -15
|
||||
},
|
||||
"ring_bottom": {
|
||||
"x": 22.7244416,
|
||||
"y": 5.176704,
|
||||
"r": -20
|
||||
},
|
||||
"ring_home": {
|
||||
"x": 29.2228244,
|
||||
"y": 23.0308638,
|
||||
"r": -20
|
||||
},
|
||||
"ring_top": {
|
||||
"x": 35.7212071,
|
||||
"y": 40.8850235,
|
||||
"r": -20
|
||||
},
|
||||
"middle_bottom": {
|
||||
"x": 42.2887022,
|
||||
"y": 3.3767843,
|
||||
"r": -20
|
||||
},
|
||||
"middle_home": {
|
||||
"x": 48.7870849,
|
||||
"y": 21.2309442,
|
||||
"r": -20
|
||||
},
|
||||
"middle_top": {
|
||||
"x": 55.2854676,
|
||||
"y": 39.0851039,
|
||||
"r": -20
|
||||
},
|
||||
"index_bottom": {
|
||||
"x": 58.0907411,
|
||||
"y": -8.7597541,
|
||||
"r": -20
|
||||
},
|
||||
"index_home": {
|
||||
"x": 64.5891238,
|
||||
"y": 9.0944057,
|
||||
"r": -20
|
||||
},
|
||||
"index_top": {
|
||||
"x": 71.0875065,
|
||||
"y": 26.9485655,
|
||||
"r": -20
|
||||
},
|
||||
"inner_bottom": {
|
||||
"x": 75.2608606,
|
||||
"y": -17.1375221,
|
||||
"r": -20
|
||||
},
|
||||
"inner_home": {
|
||||
"x": 81.7592433,
|
||||
"y": 0.7166377,
|
||||
"r": -20
|
||||
},
|
||||
"inner_top": {
|
||||
"x": 88.257626,
|
||||
"y": 18.5707976,
|
||||
"r": -20
|
||||
},
|
||||
"near_thumb": {
|
||||
"x": 62.1846295,
|
||||
"y": -32.5975409,
|
||||
"r": -20
|
||||
},
|
||||
"home_thumb": {
|
||||
"x": 82.5841162,
|
||||
"y": -47.013742,
|
||||
"r": -48
|
||||
},
|
||||
"far_thumb": {
|
||||
"x": 94.7890169,
|
||||
"y": -68.8083815,
|
||||
"r": -76
|
||||
},
|
||||
"mirror_pinky_bottom": {
|
||||
"x": 233.5881016,
|
||||
"y": 0,
|
||||
"r": 15
|
||||
},
|
||||
"mirror_pinky_home": {
|
||||
"x": 228.67053969999998,
|
||||
"y": 18.3525907,
|
||||
"r": 15
|
||||
},
|
||||
"mirror_pinky_top": {
|
||||
"x": 223.7529779,
|
||||
"y": 36.7051814,
|
||||
"r": 15
|
||||
},
|
||||
"mirror_ring_bottom": {
|
||||
"x": 210.86365999999998,
|
||||
"y": 5.176704,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_ring_home": {
|
||||
"x": 204.36527719999998,
|
||||
"y": 23.0308638,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_ring_top": {
|
||||
"x": 197.8668945,
|
||||
"y": 40.8850235,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_middle_bottom": {
|
||||
"x": 191.29939939999997,
|
||||
"y": 3.3767843,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_middle_home": {
|
||||
"x": 184.8010167,
|
||||
"y": 21.2309442,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_middle_top": {
|
||||
"x": 178.30263399999998,
|
||||
"y": 39.0851039,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_index_bottom": {
|
||||
"x": 175.49736049999998,
|
||||
"y": -8.7597541,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_index_home": {
|
||||
"x": 168.99897779999998,
|
||||
"y": 9.0944057,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_index_top": {
|
||||
"x": 162.5005951,
|
||||
"y": 26.9485655,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_inner_bottom": {
|
||||
"x": 158.327241,
|
||||
"y": -17.1375221,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_inner_home": {
|
||||
"x": 151.82885829999998,
|
||||
"y": 0.7166377,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_inner_top": {
|
||||
"x": 145.3304756,
|
||||
"y": 18.5707976,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_near_thumb": {
|
||||
"x": 171.4034721,
|
||||
"y": -32.5975409,
|
||||
"r": 20
|
||||
},
|
||||
"mirror_home_thumb": {
|
||||
"x": 151.00398539999998,
|
||||
"y": -47.013742,
|
||||
"r": 48
|
||||
},
|
||||
"mirror_far_thumb": {
|
||||
"x": 138.79908469999998,
|
||||
"y": -68.8083815,
|
||||
"r": 76
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue