grid-apps-cmms/src/kiri/worker.js

522 lines
18 KiB
JavaScript
Raw Normal View History

2020-06-03 22:39:30 -04:00
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
"use strict";
2020-11-03 11:19:13 -05:00
let BASE = self.base,
KIRI = self.kiri,
UTIL = BASE.util,
time = UTIL.time,
2020-06-16 11:08:50 -04:00
current = self.worker = {
print: null,
snap: null
2020-11-03 11:19:13 -05:00
},
cache = {};
// catch clipper alerts and convert to console messages
self.alert = function(o) {
console.log(o);
};
console.log(`kiri | init work | ${KIRI.version || "rogue"}`);
2020-11-03 11:19:13 -05:00
BASE.debug.disable();
// code is running in the worker / server context
const dispatch =
KIRI.server =
KIRI.worker = {
cache: cache,
decimate: function(data, send) {
let { vertices, options } = data;
vertices = new Float32Array(vertices),
vertices = BASE.pointsToVertices(BASE.verticesToPoints(vertices, options));
send.done(vertices);
},
snap: function(data, send) {
2020-06-16 11:08:50 -04:00
current.snap = data;
send.done();
},
slice: function(data, send) {
let settings = data.settings,
vertices = new Float32Array(data.vertices),
position = data.position,
tracking = data.tracking,
points = BASE.verticesToPoints(vertices, { maxpass: 0 }),
2020-05-06 14:04:43 -04:00
state = data.state || {},
rotation = state.rotation,
centerz = state.centerz,
movez = state.movez;
if (rotation) {
state.rotate = new THREE.Matrix4().makeRotationY(-rotation);
}
send.data({update:0.05, updateStatus:"slicing"});
2020-11-03 11:19:13 -05:00
let widget = KIRI.newWidget(data.id).setPoints(points),
last = time(),
now;
// do it here so cancel can work
cache[data.id] = widget;
// fake mesh object to satisfy printing
widget.track = tracking;
widget.mesh = {
widget: widget,
position: position
};
try {
widget.slice(settings, function(error) {
if (error) {
delete cache[data.id];
send.data({error: error});
} else {
const slices = widget.slices || [];
send.data({send_start: time()});
send.data({
topo: settings.synth.sendTopo ? widget.topo : null,
stats: widget.stats,
slices: slices.length
});
slices.forEach(function(slice,index) {
const state = { zeros: [] };
send.data({index: index, slice: slice.encode(state)}, state.zeros);
})
send.data({send_end: time()});
}
send.done({done: true});
}, function(update, msg) {
2020-11-03 11:19:13 -05:00
now = time();
if (now - last < 10 && update < 0.99) return;
// on update
send.data({update: (0.05 + update * 0.95), updateStatus: msg});
last = now;
});
} catch (error) {
send.data({error: error.toString()});
2020-04-13 19:28:17 -04:00
console.log(error);
}
},
2020-11-08 13:04:54 -05:00
prepare: function(data, send) {
2020-11-08 10:55:36 -05:00
// create widget array from id:widget cache
2020-11-08 13:04:54 -05:00
const widgets = Object.values(cache);
2020-11-08 10:55:36 -05:00
// let client know we've started
send.data({update:0.05, updateStatus:"preview"});
2020-11-08 13:04:54 -05:00
const drivers = KIRI.driver;
const settings = data.settings;
const mode = settings.mode;
const driver = drivers[mode];
if (!(driver && driver.prepare)) {
return console.log({invalid_print_driver: mode, driver});
}
const layers = driver.prepare(widgets, settings, (progress, message, layer) => {
const state = { zeros: [] };
const emit = { progress, message };
if (layer) {
emit.layer = KIRI.codec.encode(layer, state)
}
send.data(emit);
});
2020-11-08 13:04:54 -05:00
2020-11-10 23:22:58 -05:00
const print = current.print || {};
const maxSpeed = print.maxSpeed || undefined;
const state = { zeros: [] };
send.data({ progress: 1, message: "transfer" });
send.done({
done: true,
// output: KIRI.codec.encode(layers, state),
2020-11-11 08:07:02 -05:00
maxSpeed
}, state.zeros);
},
2020-04-14 09:26:59 -04:00
2020-11-08 17:35:40 -05:00
export: function(data, send) {
const mode = data.settings.mode;
const driver = KIRI.driver[mode];
if (!(driver && driver.export)) {
console.log({missing_export_driver: mode});
return send.done()
}
2020-11-08 19:38:02 -05:00
let output;
driver.export(current.print, function(line, direct) {
send.data({line}, direct);
}, function(done) {
// SLA workaround
output = done;
2020-11-08 17:35:40 -05:00
});
2020-11-08 19:38:02 -05:00
const { bounds, time, lines, bytes, distance, settings } = current.print;
2020-11-08 17:35:40 -05:00
send.done({
done: true,
2020-11-08 19:38:02 -05:00
output: output ? output : { bounds, time, lines, bytes, distance, settings }
2020-04-14 09:26:59 -04:00
});
},
2020-11-11 08:07:02 -05:00
colors: function(data, send) {
const { colors, max } = data;
const colorMap = {};
colors.forEach(color => {
colorMap[color] = KIRI.driver.FDM.rateToColor(color, max);
});
send.done(colorMap);
},
2020-11-09 13:57:18 -05:00
parse: function(args, send) {
const { settings, code, type } = args;
const origin = settings.origin;
const offset = {
x: origin.x,
y: -origin.y,
z: origin.z
};
2020-11-10 21:54:18 -05:00
const print = current.print = KIRI.newPrint(settings, Object.values(cache));
const tools = settings.device.extruders;
2020-11-09 18:21:59 -05:00
const mode = settings.mode;
2020-11-12 12:15:39 -05:00
const thin = settings.controller.lineType === 'line' || mode !== 'FDM';
const flat = settings.controller.lineType === 'flat' && mode === 'FDM';
2020-11-09 13:57:18 -05:00
const parsed = print.parseGCode(code, offset, progress => {
send.data({ progress: progress * 0.25 });
2020-11-09 13:57:18 -05:00
}, done => {
2020-11-13 09:27:42 -05:00
const maxSpeed = print.maxSpeed;
const layers = KIRI.driver.FDM.prepareRender(done.output, progress => {
send.data({ progress: 0.25 + progress * 0.75 });
2020-11-12 12:15:39 -05:00
}, { thin, flat, tools });
2020-11-13 09:27:42 -05:00
send.done({parsed: KIRI.codec.encode(layers), maxSpeed});
2020-11-09 18:21:59 -05:00
}, { fdm : mode === 'FDM' });
},
2020-11-09 23:01:41 -05:00
parse_svg: function(parsed, send) {
parsed.forEach(layer => {
layer.forEach(out => {
const { x, y, z } = out.point;
out.point = BASE.newPoint(x,y,z || 0);
});
});
const layers = KIRI.driver.FDM.prepareRender(parsed, progress => {
send.data({ progress });
}, { thin: true });
send.done({parsed: KIRI.codec.encode(layers)});
},
clear: function(data, send) {
2020-06-16 11:08:50 -04:00
current.snap = null;
current.print = null;
if (!data.id) {
cache = {};
send.done({ clear: true });
return;
}
let had = cache[data.id] !== undefined;
delete cache[data.id];
send.done({
id: data.id,
had: had,
has: cache[data.id] !== undefined
});
},
config: function(data, send) {
const update = {};
if (data.base) {
update.base = data.base;
2020-11-03 11:19:13 -05:00
Object.assign(BASE.config, data.base);
} else {
console.log({invalid:data});
}
send.done({config: update});
2020-11-26 23:18:28 -05:00
},
image2mesh: function(info, send) {
let img = new png.PNG();
img.parse(info.png, (err, output) => {
let { width, height, data } = output;
let { bedDepth, bedWidth } = info.settings.device;
let imageAspect = height / width;
let deviceAspect = bedDepth / bedWidth;
let div = 1;
if (imageAspect < deviceAspect) {
div = width / bedWidth;
} else {
div = height / bedDepth;
}
let points =
width * height + // grid
2020-11-27 12:16:03 -05:00
height * 2 + 0 + // left/right
2020-11-27 15:03:25 -05:00
width * 2 + 0; // top/bottom
2020-11-27 12:16:03 -05:00
let flats =
((height-1) * (width-1)) + // surface
2020-11-27 15:03:25 -05:00
((height-1) * 2) + // left/right
((width-1) * 2) + // top/bottom
1; // base
2020-11-27 12:16:03 -05:00
// convert png to grayscale
let gray = new Uint8Array(width * height);
let gi = 0;
let invi = info.inv_image ? true : false;
let inva = info.inv_alpha ? true : false;
2020-11-27 15:03:25 -05:00
let border = info.border || 0;
2020-11-27 12:16:03 -05:00
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
2020-11-26 23:18:28 -05:00
let di = (x + width * y) * 4;
let r = data[di];
let g = data[di+1];
let b = data[di+2];
let a = data[di+3];
let v = ((r + g + b) / 3);
2020-11-27 15:03:25 -05:00
if (border) {
if (x < border || y < border || x > width-border-1 || y > height-border-1) {
v = 255;
}
}
if (invi) v = 255 - v;
if (inva) a = 255 - a;
v *= (a/255);
2020-11-27 12:16:03 -05:00
gray[gi++] = v;
}
}
let blur = parseInt(info.blur || 0);
while (blur-- > 0) {
let blur = new Uint8Array(width * height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let xl = Math.max(x-1,0);
let xr = Math.min(x+1,width-1);
let yu = Math.max(y-1,0);
let yd = Math.min(y+1,height-1);
blur[x + width * y] = (
gray[xl + (width * yu)] +
gray[x + (width * yu)] +
gray[xr + (width * yu)] +
gray[xl + (width * y)] +
gray[x + (width * y)] + // self
gray[xr + (width * y)] +
gray[xl + (width * yd)] +
gray[x + (width * yd)] +
gray[xr + (width * yd)]
) / 9;
}
}
gray = blur;
}
2020-11-27 12:16:03 -05:00
// create indexed mesh output
let base = parseInt(info.base || 0);
2020-11-27 12:16:03 -05:00
let verts = new Float32Array(points * 3);
let faces = new Uint32Array(flats * 6);
let w2 = width / 2;
let h2 = height / 2;
let vi = 0;
let ii = 0;
let VI = 0;
let VB = 0;
// create surface vertices & faces
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
let v = gray[x + width * y];
// create vertex @ x,y
2020-11-26 23:18:28 -05:00
verts[vi++] = (-w2 + x) / div;
verts[vi++] = (h2 - y) / div;
verts[vi++] = ((255 - v) / 50) + base;
2020-11-27 12:16:03 -05:00
VI++;
// create two surface faces on the rect between x-1,y-1 and x,y
2020-11-26 23:18:28 -05:00
if (x > 0 && y > 0) {
let p1 = (x - 1) * height + (y - 0);
let p2 = (x - 0) * height + (y - 1);
let p3 = (x - 0) * height + (y - 0);
let p4 = (x - 1) * height + (y - 1);
2020-11-27 12:16:03 -05:00
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p3;
faces[ii++] = p1;
faces[ii++] = p4;
faces[ii++] = p2;
2020-11-26 23:18:28 -05:00
}
}
2020-11-26 23:49:44 -05:00
send.data({progress: x / width});
2020-11-26 23:18:28 -05:00
}
2020-11-27 12:16:03 -05:00
// create top vertices & faces
VB = VI;
let TL = VI;
for (let x = 0; x < width; x++) {
2020-11-27 12:16:03 -05:00
let y = 0;
verts[vi++] = (-w2 + x) / div;
verts[vi++] = (h2 - y) / div;
verts[vi++] = 0;
VI++;
// create two top faces on the rect x-1,0, x,z
if (x > 0) {
let p1 = VB + (x - 1);
let p2 = VB + (x - 0);
let p3 = (x * height);
let p4 = (x - 1) * height;
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p3;
faces[ii++] = p1;
faces[ii++] = p3;
faces[ii++] = p4;
}
}
// create bottom vertices & faces
VB = VI;
let BL = VI;
for (let x = 0; x < width; x++) {
let y = height - 1;
verts[vi++] = (-w2 + x) / div;
verts[vi++] = (h2 - y) / div;
verts[vi++] = 0;
VI++;
// create two top faces on the rect x-1,0, x,z
if (x > 0) {
let p1 = VB + (x - 1);
let p2 = VB + (x - 0);
let p3 = (x * height) + y;
let p4 = (x - 1) * height + y;
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p3;
faces[ii++] = p1;
faces[ii++] = p3;
faces[ii++] = p4;
}
}
// create left vertices & faces
VB = VI;
for (let y=0; y < height; y++) {
let x = 0;
verts[vi++] = (-w2 + x) / div;
verts[vi++] = (h2 - y) / div;
verts[vi++] = 0;
VI++;
// create two left faces on the rect y-1,0, y,z
if (y > 0) {
let p1 = VB + (y + 0);
let p2 = VB + (y - 1);
let p3 = 0 + (y - 1);
let p4 = 0 + (y - 0);
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p3;
faces[ii++] = p1;
faces[ii++] = p3;
faces[ii++] = p4;
}
}
// create right vertices & faces
VB = VI;
let TR = VI;
for (let y=0; y < height; y++) {
let x = width - 1;
verts[vi++] = (-w2 + x) / div;
verts[vi++] = (h2 - y) / div;
verts[vi++] = 0;
VI++;
// create two right faces on the rect y-1,0, y,z
if (y > 0) {
let p1 = VB + (y + 0);
let p2 = VB + (y - 1);
let p3 = (x * height) + (y - 1);
let p4 = (x * height) + (y - 0);
faces[ii++] = p1;
faces[ii++] = p2;
faces[ii++] = p3;
faces[ii++] = p1;
faces[ii++] = p3;
faces[ii++] = p4;
}
}
let BR = VI-1;
// create base two faces
faces[ii++] = TL;
faces[ii++] = TR;
faces[ii++] = BR;
faces[ii++] = TL;
faces[ii++] = BR;
faces[ii++] = BL;
// flatten for now until we support indexed mesh
// throughout KM (widget, storage, decimation)
2020-11-26 23:49:44 -05:00
let bigv = new Float32Array(ii * 3);
let bgi = 0;
for (let i=0; i<ii; i++) {
2020-11-27 12:16:03 -05:00
let iv = faces[i] * 3;
2020-11-26 23:49:44 -05:00
bigv[bgi++] = verts[iv];
bigv[bgi++] = verts[iv+1];
bigv[bgi++] = verts[iv+2];
}
2020-11-27 12:16:03 -05:00
// send.done({done: {verts, faces, bigv, vi, ii}}, [ bigv.buffer ]);
2020-11-26 23:49:44 -05:00
send.done({done: {bigv}}, [ bigv.buffer ]);
2020-11-26 23:18:28 -05:00
});
}
};
self.onmessage = function(e) {
2020-11-03 11:19:13 -05:00
let time_recv = time(),
msg = e.data,
run = dispatch[msg.task],
send = {
data : function(data,direct) {
// if (direct && direct.length) {
// console.log({
// zeros: direct.length,
// sz:direct.map(z => z.byteLength).reduce((a,v) => a+v)
// });
// }
self.postMessage({
seq: msg.seq,
task: msg.task,
done: false,
data: data
}, direct);
},
done : function(data,direct) {
// if (direct && direct.length) {
// console.log({
// zeros: direct.length,
// sz:direct.map(z => z.byteLength).reduce((a,v) => a+v)
// });
// }
self.postMessage({
seq: msg.seq,
task: msg.task,
done: true,
data: data
}, direct);
}
};
if (run) {
let time_xfer = (time_recv - msg.time),
output = run(msg.data, send),
2020-11-03 11:19:13 -05:00
time_send = time(),
time_proc = time_send - time_recv;
if (output) self.postMessage({
seq: msg.seq,
task: msg.task,
time_send: time_xfer,
time_proc: time_proc,
// replaced on reply side
2020-11-03 11:19:13 -05:00
time_recv: time(),
data: output
});
} else {
console.log({kiri_msg:e});
}
};
2020-06-16 11:08:50 -04:00
2020-11-03 11:19:13 -05:00
// load kiri modules
KIRI.loader.forEach(fn => {
fn(dispatch);
});