move cli, engine, frame access points to kiri-run
This commit is contained in:
parent
74ae6109e2
commit
827847a321
5 changed files with 3 additions and 3 deletions
219
src/kiri-run/cli.js
Normal file
219
src/kiri-run/cli.js
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/** example of how to use Kiri:Moto's slicer engine from the command-line */
|
||||
let fs = require('fs');
|
||||
let args = process.argv.slice(2);
|
||||
let opts = {
|
||||
dir: process.cwd(),
|
||||
output: "-",
|
||||
model: "web/obj/cube.stl",
|
||||
source: "src/cli/kiri-source.json",
|
||||
process: "src/cli/kiri-fdm-process.json",
|
||||
device: "src/cli/kiri-fdm-device.json",
|
||||
};
|
||||
for (let i=0; i<args.length; i++) {
|
||||
let arg = args[i].split('=');
|
||||
let [ key, val ] = arg;
|
||||
if (arg.length === 2) {
|
||||
key = arg[0];
|
||||
val = arg[1];
|
||||
} else if (key.indexOf('--') === 0) {
|
||||
val = true;
|
||||
} else if (key.indexOf('-') === 0) {
|
||||
val = args[++i];
|
||||
} else {
|
||||
val = undefined;
|
||||
}
|
||||
if (key && val) {
|
||||
key = key.replace(/-/g,'');
|
||||
opts[key.trim()] = val;
|
||||
} else {
|
||||
key = "model";
|
||||
val = arg;
|
||||
}
|
||||
}
|
||||
if (opts.help) {
|
||||
console.log([
|
||||
"cli <options> <file>",
|
||||
" --verbose | enable verbose logging",
|
||||
" --dir=[dir] | root directory for file paths (default: '.')",
|
||||
" --model=[file] | model file to load (or last parameter)",
|
||||
" --source=[file] | source file list (defaults to kiri engine)",
|
||||
" --device=[file] | device definition file (json)",
|
||||
" --process=[file] | process definition file (json)",
|
||||
" --output=[file] | gcode output to file or '-' for stdout",
|
||||
" --position=x,y,z | move loaded model to position x,y,z",
|
||||
" --rotate=x,y,z | rotate loaded model x,y,z radians",
|
||||
" --scale=x,y,z | scale loaded model in x,y,z",
|
||||
" --move=x,y,z | move loaded model x,y,z millimeters"
|
||||
].join("\r\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
let { dir, verbose, model, output, position, move, scale, rotate } = opts;
|
||||
let exports_save = exports,
|
||||
navigator = { userAgent: "" },
|
||||
module_save = module,
|
||||
THREE = {},
|
||||
gapp = {},
|
||||
geo = {},
|
||||
self = this.self = {
|
||||
gapp,
|
||||
THREE,
|
||||
location: { hostname: 'local', port: 0, protocol: 'fake' },
|
||||
postMessage: (msg) => {
|
||||
self.kiri.client.onmessage({data:msg});
|
||||
}
|
||||
};
|
||||
|
||||
// fake fetch for worker to get wasm, if needed
|
||||
let fetch = function(url, opts = {}) {
|
||||
if (verbose) console.log({fetch: url});
|
||||
if (!url.startsWith('/')) {
|
||||
url = `${dir}/${url}`;
|
||||
}
|
||||
let buf = fs.readFileSync(url);
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(new Promise((resolve, reject) => {
|
||||
if (opts.format === 'string') {
|
||||
return resolve(buf.toString());
|
||||
}
|
||||
if (opts.format === 'buffer') {
|
||||
return resolve(buf);
|
||||
}
|
||||
if (opts.format === 'eval') {
|
||||
return resolve(eval('(' + buf + ')'));
|
||||
}
|
||||
resolve({
|
||||
arrayBuffer: function() {
|
||||
return buf;
|
||||
}
|
||||
});
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
// imitate worker process
|
||||
class Worker {
|
||||
constructor(url) {
|
||||
if (verbose) console.log({worker: url});
|
||||
}
|
||||
|
||||
postMessage(msg) {
|
||||
setImmediate(() => {
|
||||
self.kiri.worker.onmessage({data:msg});
|
||||
});
|
||||
}
|
||||
|
||||
onmessage(msg) {
|
||||
// if we end up here, something went wrong
|
||||
console.trace('worker-recv', msg);
|
||||
}
|
||||
|
||||
terminate() {
|
||||
// if we end up here, something went wrong
|
||||
console.trace('worker terminate');
|
||||
}
|
||||
}
|
||||
|
||||
// node is missing these functions so put them in scope during eval
|
||||
function atob(a) {
|
||||
return Buffer.from(a).toString('base64');
|
||||
}
|
||||
|
||||
function btoa(b) {
|
||||
return Buffer.from(b, 'base64').toString();
|
||||
}
|
||||
|
||||
async function run() {
|
||||
let files = await fetch(opts.source, { format: "eval" } );
|
||||
let device = await fetch(opts.device, { format: "eval" } );
|
||||
let process = await fetch(opts.process, { format: "eval" } );
|
||||
|
||||
for (let file of files.map(p => `${dir}/src/${p}.js`)) {
|
||||
let isPNG = file.indexOf("/pngjs") > 0;
|
||||
let isClip = file.indexOf("/clip") > 0;
|
||||
let isEarcut = file.indexOf("/earcut") > 0;
|
||||
let isTHREE = file.indexOf("/three") > 0;
|
||||
if (isTHREE) {
|
||||
// THREE.js kung-fu fake-out
|
||||
exports = {};
|
||||
}
|
||||
let swapMod = isEarcut;
|
||||
if (swapMod) {
|
||||
module = { exports: {} };
|
||||
}
|
||||
let clearMod = isPNG || isClip;
|
||||
if (clearMod) {
|
||||
module = undefined;
|
||||
}
|
||||
try {
|
||||
if (verbose) console.log(`loading ... ${file}`);
|
||||
eval(fs.readFileSync(`${file}`).toString());
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
if (isClip) {
|
||||
ClipperLib = self.ClipperLib;
|
||||
}
|
||||
if (isTHREE) {
|
||||
Object.assign(THREE, exports);
|
||||
// restore exports after faking out THREE.js
|
||||
exports = exports_save;
|
||||
}
|
||||
if (isEarcut) {
|
||||
self.earcut = module.exports;
|
||||
}
|
||||
if (clearMod || swapMod) {
|
||||
module = module_save;
|
||||
}
|
||||
}
|
||||
|
||||
let { kiri, moto, load } = self;
|
||||
|
||||
console.log({version: kiri.version});
|
||||
|
||||
let engine = kiri.newEngine();
|
||||
let data = await fetch(model)
|
||||
let buf = data.arrayBuffer().buffer;
|
||||
|
||||
return engine.parse(buf)
|
||||
.then(data => { if (verbose) console.log({loaded: data}) })
|
||||
.then(() => {
|
||||
if (position) {
|
||||
let [ x, y, z ] = position.split(',').map(v => parseFloat(v || 0));
|
||||
if (verbose) console.log('moveTo', {x, y, z});
|
||||
engine.moveTo(x,y,z);
|
||||
}
|
||||
if (move) {
|
||||
let [ x, y, z ] = move.split(',').map(v => parseFloat(v || 0));
|
||||
if (verbose) console.log('move', {x, y, z});
|
||||
engine.move(x,y,z);
|
||||
}
|
||||
if (scale) {
|
||||
let [ x, y, z ] = scale.split(',').map(v => parseFloat(v || 0));
|
||||
if (verbose) console.log('scale', {x, y, z});
|
||||
engine.scale(x,y,z);
|
||||
}
|
||||
if (rotate) {
|
||||
let [ x, y, z ] = rotate.split(',').map(v => parseFloat(v || 0));
|
||||
if (verbose) console.log('rotate', {x, y, z});
|
||||
engine.rotate(x,y,z);
|
||||
}
|
||||
})
|
||||
.then(() => engine.setProcess(process))
|
||||
.then(() => engine.setDevice(device))
|
||||
.then(eng => eng.slice())
|
||||
.then(eng => eng.prepare())
|
||||
.then(eng => engine.export())
|
||||
.then(gcode => {
|
||||
if (output === '-') {
|
||||
console.log({gcode});
|
||||
} else {
|
||||
fs.writeFileSync(output, gcode);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log({error});
|
||||
});
|
||||
}
|
||||
|
||||
run();
|
||||
167
src/kiri-run/engine.js
Normal file
167
src/kiri-run/engine.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
||||
let KIRI = self.kiri = self.kiri || {};
|
||||
KIRI.newEngine = ()=> { return new Engine ()};
|
||||
|
||||
if (!KIRI.api) {
|
||||
KIRI.api = {
|
||||
event: {
|
||||
emit: () => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Engine {
|
||||
constructor() {
|
||||
this.widget = KIRI.newWidget();
|
||||
this.settings = {
|
||||
mode: "FDM",
|
||||
controller: {},
|
||||
render: false,
|
||||
filter: { FDM: "internal" },
|
||||
device: KIRI.conf.defaults.fdm.d, // device profile
|
||||
process: KIRI.conf.defaults.fdm.p, // slicing settings
|
||||
widget: { [ this.widget.id ]: {} }
|
||||
};
|
||||
this.listener = () => {};
|
||||
}
|
||||
|
||||
load(url) {
|
||||
return new Promise((accept, reject) => {
|
||||
try {
|
||||
new load.STL().load(url, vertices => {
|
||||
this.listener({loaded: url, vertices});
|
||||
this.widget.loadVertices(vertices).center();
|
||||
accept(this);
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
KIRI.api.platform.clear();
|
||||
}
|
||||
|
||||
parse(data) {
|
||||
return new Promise((accept, reject) => {
|
||||
try {
|
||||
let vertices = new load.STL().parse(data);
|
||||
this.listener({parsed: data, vertices});
|
||||
this.widget.loadVertices(vertices).center();
|
||||
accept(this);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setListener(listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
setRender(bool) {
|
||||
this.settings.render = bool;
|
||||
return this;
|
||||
}
|
||||
|
||||
setMode(mode) {
|
||||
this.settings.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
setDevice(device) {
|
||||
Object.assign(this.settings.device, device);
|
||||
return this;
|
||||
}
|
||||
|
||||
setProcess(process) {
|
||||
Object.assign(this.settings.process, process);
|
||||
return this;
|
||||
}
|
||||
|
||||
setTools(tools) {
|
||||
this.settings.tools = tools;
|
||||
return this;
|
||||
}
|
||||
|
||||
setStock(stock) {
|
||||
this.settings.stock = stock;
|
||||
return this;
|
||||
}
|
||||
|
||||
setOrigin(x,y,z) {
|
||||
this.settings.origin = { x, y, z };
|
||||
return this;
|
||||
}
|
||||
|
||||
moveTo(x, y, z) {
|
||||
this.widget.move(x, y, z, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
move(x, y, z) {
|
||||
this.widget.move(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(x, y, z) {
|
||||
this.widget.scale(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
rotate(x, y, z) {
|
||||
this.widget.rotate(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
slice() {
|
||||
return new Promise((accept, reject) => {
|
||||
KIRI.client.clear();
|
||||
KIRI.client.sync([ this.widget ]);
|
||||
KIRI.client.rotate(this.settings);
|
||||
KIRI.client.slice(this.settings, this.widget, msg => {
|
||||
this.listener({slice:msg});
|
||||
if (msg.error) {
|
||||
reject(msg.error);
|
||||
}
|
||||
if (msg.done) {
|
||||
accept(this);
|
||||
}
|
||||
});
|
||||
KIRI.client.unrotate(this.settings);
|
||||
});
|
||||
}
|
||||
|
||||
prepare() {
|
||||
return new Promise((accept, reject) => {
|
||||
KIRI.client.prepare(this.settings, update => {
|
||||
this.listener({prepare:{update}});
|
||||
}, done => {
|
||||
this.listener({prepare:{done:true}});
|
||||
accept(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export() {
|
||||
return new Promise((accept, reject) => {
|
||||
let output = [];
|
||||
KIRI.client.export(this.settings, segment => {
|
||||
this.listener({export:{segment}});
|
||||
output.push(segment);
|
||||
}, done => {
|
||||
this.listener({export:{done}});
|
||||
accept(output.join('\r\n'));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
87
src/kiri-run/frame.js
Normal file
87
src/kiri-run/frame.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
let KIRI = self.kiri = self.kiri || {};
|
||||
let frame, cwin, onevent = {}, targetOrigin;
|
||||
|
||||
function $(id) { return document.getElementById(id) }
|
||||
|
||||
function setFrame(fo) {
|
||||
frame = fo;
|
||||
cwin = fo.contentWindow;
|
||||
}
|
||||
|
||||
function send(msg) {
|
||||
cwin.postMessage(msg, targetOrigin);
|
||||
}
|
||||
|
||||
function recv(msg) {
|
||||
let { origin, source, target, data } = msg;
|
||||
let efn = onevent[data.event];
|
||||
if (efn) {
|
||||
efn(data.data, data.event);
|
||||
}
|
||||
if (API.onmessage) {
|
||||
API.onmessage(data, msg);
|
||||
}
|
||||
}
|
||||
|
||||
let API = KIRI.frame = {
|
||||
setFrame: (io, target) => {
|
||||
let type = typeof(io);
|
||||
targetOrigin = target;
|
||||
switch (type) {
|
||||
case 'string': setFrame($(io)); break;
|
||||
case 'object': setFrame(io); break;
|
||||
default: throw `invalid frame type ${type}`;
|
||||
}
|
||||
window.addEventListener('message', msg => {
|
||||
let { origin, source, target, data } = msg;
|
||||
if (source.window === cwin) {
|
||||
recv(msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
send: send,
|
||||
|
||||
load: (load) => { send({ load }) },
|
||||
|
||||
clear: () => { send({ clear: true }) },
|
||||
|
||||
parse: (data, type) => { send({ parse: data, type })},
|
||||
|
||||
get: (scope) => { send({ get: scope })},
|
||||
|
||||
setMode: (mode) => { send({ mode }) },
|
||||
|
||||
setDevice: (device) => { send({ device })},
|
||||
|
||||
setProcess: (process) => { send({ process })},
|
||||
|
||||
setFeatures: (features) => { send({ features })},
|
||||
|
||||
slice: () => { send({ function: "slice", callback: true }) },
|
||||
|
||||
prepare: () => { send({ function: "prepare", callback: true }) },
|
||||
|
||||
export: (cb) => { send({ function: "export", callback: cb ? true : false }) },
|
||||
|
||||
onmessage: () => { },
|
||||
|
||||
progress: (progress, message) => { send({ progress, message }) },
|
||||
|
||||
alert: (alert, time) => { send({ alert, time }) },
|
||||
|
||||
emit: (emit, message) => { send({ emit, message }) },
|
||||
|
||||
onevent: (event, fn) => {
|
||||
onevent[event] = fn;
|
||||
send({ event });
|
||||
}
|
||||
};
|
||||
|
||||
API.on = API.onevent;
|
||||
})();
|
||||
Loading…
Reference in a new issue