diff --git a/package-lock.json b/package-lock.json index bd79727..62545eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "fs-extra": "^9.0.1", "js-yaml": "^3.14.0", "json5": "^2.2.0", - "makerjs": "github:mrzealot/maker.js-dist#a0ca32948845efe8ad5c9ca454f1285926853138", + "kle-serial": "github:mrzealot/kle-serial", + "makerjs": "github:mrzealot/maker.js-dist", "mathjs": "^8.1.1", "yargs": "^15.4.1" }, @@ -1805,6 +1806,15 @@ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-2.0.1.tgz", "integrity": "sha512-9KqSdmWCkBIisFIGclT0FRagKhI7IVbMyUjsxCFG0Ly1Dg6whlxJ7b9lrq8ifk3X/fGeJzok1R75LQfZTfA5zQ==" }, + "node_modules/kle-serial": { + "name": "@ijprest/kle-serial", + "version": "0.15.1", + "resolved": "git+ssh://git@github.com/mrzealot/kle-serial.git#f6afaf61d32f7d4a82ec3da95b98f3f43997bd57", + "license": "MIT", + "dependencies": { + "json5": "^2.1.0" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4703,6 +4713,13 @@ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-2.0.1.tgz", "integrity": "sha512-9KqSdmWCkBIisFIGclT0FRagKhI7IVbMyUjsxCFG0Ly1Dg6whlxJ7b9lrq8ifk3X/fGeJzok1R75LQfZTfA5zQ==" }, + "kle-serial": { + "version": "git+ssh://git@github.com/mrzealot/kle-serial.git#f6afaf61d32f7d4a82ec3da95b98f3f43997bd57", + "from": "kle-serial@github:mrzealot/kle-serial", + "requires": { + "json5": "^2.1.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4764,7 +4781,7 @@ "makerjs": { "version": "git+ssh://git@github.com/mrzealot/maker.js-dist.git#a0ca32948845efe8ad5c9ca454f1285926853138", "integrity": "sha512-MsYj7ch7Et7He/Nsd2BLvoPsJlyUoi7KkLyiqP0ap0ff+LcrdyKCqpr9vpudthaHjMmsWeVuPb0HnzfYbGw1wQ==", - "from": "makerjs@github:mrzealot/maker.js-dist#a0ca32948845efe8ad5c9ca454f1285926853138", + "from": "makerjs@github:mrzealot/maker.js-dist", "requires": { "@danmarshall/jscad-typings": "^1.0.0", "@types/bezier-js": "^0.0.6", diff --git a/package.json b/package.json index 625eac5..c0bcf77 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,12 @@ "coverage": "nyc --reporter=html --reporter=text npm test" }, "dependencies": { + "kle-serial": "github:mrzealot/kle-serial", "@jscad/openjscad": "^1.6.1", "fs-extra": "^9.0.1", "js-yaml": "^3.14.0", "json5": "^2.2.0", - "makerjs": "github:mrzealot/maker.js-dist#a0ca32948845efe8ad5c9ca454f1285926853138", + "makerjs": "github:mrzealot/maker.js-dist", "mathjs": "^8.1.1", "yargs": "^15.4.1" }, diff --git a/src/ergogen.js b/src/ergogen.js index d4f9302..3030696 100644 --- a/src/ergogen.js +++ b/src/ergogen.js @@ -1,11 +1,5 @@ -const yaml = require('js-yaml') -const json5 = require('json5') -const makerjs = require('makerjs') -const jscad = require('@jscad/openjscad') - -const a = require('./assert') const u = require('./utils') - +const io = require('./io') const prepare = require('./prepare') const units_lib = require('./units') const points_lib = require('./points') @@ -13,79 +7,20 @@ const outlines_lib = require('./outlines') const cases_lib = require('./cases') const pcbs_lib = require('./pcbs') -const noop = () => {} - -const twodee = (model, debug) => { - const assembly = makerjs.model.originate({ - models: { - export: u.deepcopy(model) - }, - units: 'mm' - }) - - const result = { - dxf: makerjs.exporter.toDXF(assembly), - } - if (debug) { - result.json = assembly - result.svg = makerjs.exporter.toSVG(assembly) - } - return result -} - -const threedee = async (script, debug) => { - const compiled = await new Promise((resolve, reject) => { - jscad.compile(script, {}).then(compiled => { - resolve(compiled) - }) - }) - const result = { - stl: jscad.generateOutput('stla', compiled).asBuffer() - } - if (debug) { - result.jscad = script - } - return result -} - module.exports = { version: '__ergogen_version', - process: async (raw, debug=false, logger=noop) => { - - const prefix = 'Interpreting format... ' - let config = raw - if (a.type(raw)() != 'object') { - try { - config = yaml.safeLoad(raw) - logger(prefix + 'YAML detected.') - } catch (yamlex) { - try { - config = json5.parse(raw) - logger(prefix + 'JSON detected.') - } catch (jsonex) { - try { - config = new Function(raw)() - logger(prefix + 'JS code detected.') - } catch (codeex) { - logger('YAML exception:', yamlex) - logger('JSON exception:', jsonex) - logger('Code exception:', codeex) - throw new Error('Input is not valid YAML, JSON, or JS code!') - } - } - } - if (!config) { - throw new Error('Input appears to be empty!') - } - } + process: async (raw, debug=false, logger=()=>{}) => { + let [config, format] = io.interpret(raw, logger) + logger('Interpreting format: ' + format) + logger('Preprocessing input...') config = prepare.unnest(config) config = prepare.inherit(config) const results = {} if (debug) { results.raw = raw - results.canonical = config + results.canonical = u.deepcopy(config) } logger('Calculating variables...') @@ -94,6 +29,7 @@ module.exports = { results.units = units } + logger('Parsing points...') if (!config.points) { throw new Error('Input does not contain any points!') @@ -101,7 +37,7 @@ module.exports = { const points = points_lib.parse(config.points, units) if (debug) { results.points = points - results.demo = twodee(points_lib.visualize(points), debug) + results.demo = io.twodee(points_lib.visualize(points), debug) } logger('Generating outlines...') @@ -109,7 +45,7 @@ module.exports = { results.outlines = {} for (const [name, outline] of Object.entries(outlines)) { if (!debug && name.startsWith('_')) continue - results.outlines[name] = twodee(outline, debug) + results.outlines[name] = io.twodee(outline, debug) } logger('Extruding cases...') @@ -117,7 +53,7 @@ module.exports = { results.cases = {} for (const [case_name, case_script] of Object.entries(cases)) { if (!debug && case_name.startsWith('_')) continue - results.cases[case_name] = await threedee(case_script, debug) + results.cases[case_name] = await io.threedee(case_script, debug) } logger('Scaffolding PCBs...') diff --git a/src/io.js b/src/io.js new file mode 100644 index 0000000..4113dfe --- /dev/null +++ b/src/io.js @@ -0,0 +1,78 @@ +const json5 = require('json5') +const yaml = require('js-yaml') +const makerjs = require('makerjs') +const jscad = require('@jscad/openjscad') + +const u = require('./utils') +const a = require('./assert') +const kle = require('./kle') + +exports.interpret = (raw, logger) => { + let config + let format + if (a.type(raw)() != 'object') { + try { + config = yaml.safeLoad(raw) + format = 'YAML' + } catch (yamlex) { + try { + config = json5.parse(raw) + format = 'JSON' + } catch (jsonex) { + try { + config = new Function(raw)() + format = 'JS Code' + } catch (codeex) { + logger('YAML exception:', yamlex) + logger('JSON exception:', jsonex) + logger('Code exception:', codeex) + throw new Error('Input is not valid YAML, JSON, or JS Code!') + } + } + } + if (!config) { + throw new Error('Input appears to be empty!') + } + } + + try { + // assume it's KLE and try to convert it + // if it's not, it'll throw anyway + return kle.convert(config, logger) + } catch (kleex) { + return [config, format] + } +} + +exports.twodee = (model, debug) => { + const assembly = makerjs.model.originate({ + models: { + export: u.deepcopy(model) + }, + units: 'mm' + }) + + const result = { + dxf: makerjs.exporter.toDXF(assembly), + } + if (debug) { + result.json = assembly + result.svg = makerjs.exporter.toSVG(assembly) + } + return result +} + +exports.threedee = async (script, debug) => { + const compiled = await new Promise((resolve, reject) => { + jscad.compile(script, {}).then(compiled => { + resolve(compiled) + }) + }) + const result = { + stl: jscad.generateOutput('stla', compiled).asBuffer() + } + if (debug) { + result.jscad = script + } + return result +} diff --git a/src/kle.js b/src/kle.js new file mode 100644 index 0000000..8bb92f8 --- /dev/null +++ b/src/kle.js @@ -0,0 +1,45 @@ +const kle = require('kle-serial') + +exports.convert = (config, logger) => { + const keyboard = kle.Serial.deserialize(config) + const result = {points: {zones: {}}} + + let index = 1 + for (const key of keyboard.keys) { + const id = `key${index++}` + const colid = `${id}col` + const rowid = `${id}row` + + // need to account for keycap sizes, as KLE anchors + // at the corners, while we consider the centers + const x = key.x + (key.width - 1) / 2 + const y = key.y + (key.height - 1) / 2 + + // KLE deals in absolute rotation origins so we calculate + // a relative difference as an origin for the column rotation + // again, considering corner vs. center with the extra half width/height + const diff_x = key.rotation_x - (key.x + key.width / 2) + const diff_y = key.rotation_y - (key.y + key.height / 2) + + const converted = { + anchor: { + shift: [`${x} u`, `${-y} u`], + }, + columns: {} + } + converted.columns[colid] = { + rotate: -key.rotation_angle, + origin: [`${diff_x} u`, `${-diff_y} u`], + rows: {} + } + converted.columns[colid].rows[rowid] = { + width: key.width, + height: key.height, + label: key.labels[0] + } + + result.points.zones[id] = converted + } + + return [result, 'KLE'] +} \ No newline at end of file