fixup cam minion slicing, api settings cross compatibility

This commit is contained in:
Stewart Allen 2025-07-08 19:20:55 -04:00
commit e10450a5ea
5 changed files with 100 additions and 97 deletions

View file

@ -1,8 +1,7 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { base, util } from '../../geo/base.js';
import { broker } from '../../moto/broker.js';
import { decode, decodePointArray, encode, encodePointArray } from '../../kiri/codec.js';
import { decode, decodePointArray } from '../../kiri/codec.js';
import { newLine, newOrderedLine } from '../../geo/line.js';
import { newPoint } from '../../geo/point.js';
import { newSlice } from '../../kiri/slice.js';
@ -23,20 +22,17 @@ const end = function () {
export class Slicer {
constructor(widget, options = { zlist: true, zline: true, lines: false }) {
this.minions = self.kiri_worker.minions;
this.minions = self.kiri_worker?.minions;
this.options = {};
this.setOptions(options);
if (widget) {
this.threaded = widget.settings.controller.threaded;
this.setPoints(widget.getGeoVertices({ unroll: true, translate: true }));
this.computeFeatures();
}
}
get threaded() {
return this.options.threaded;
}
// zList = generate list of z vertices
// zline = generate list of z vertices with coplanar lines
// trace = find z coplanar trace lines
@ -480,32 +476,3 @@ function makeZLine(phash, p1, p2, coplanar, edge) {
Slicer.checkOverUnderOn = checkOverUnderOn;
Slicer.intersectPoints = intersectPoints;
broker.subscribe("minion.started", msg => {
const { funcs, cache, reply, log } = msg;
funcs.cam_slice_init = () => {
cache.slicer = new Slicer();
};
funcs.cam_slice_cleanup = () => {
delete cache.slicer;
};
funcs.cam_slice = (data, seq) => {
const { bucket, opt } = data;
// log({ slice: bucket, opt });
cache.slicer.sliceBucket(bucket, opt, slice => {
// console.log({ slice });
}).then(data => {
data.forEach(rec => {
rec.polys = encode(rec.polys);
if (rec.lines) {
const points = rec.lines.map(l => [l.p1, l.p2]).flat();
rec.lines = encodePointArray(points);
}
});
reply({ seq, slices: data });
});
};
});

View file

@ -7,12 +7,12 @@ import '../add/class.js';
import '../add/three.js';
import { base } from '../geo/base.js';
import { codec, encode } from '../kiri/codec.js';
import { doTopShells } from '../kiri-mode/fdm/post.js';
import { newPoint } from '../geo/point.js';
import { polygons as POLY } from '../geo/polygons.js';
import { sliceZ } from '../geo/slicer.js';
import { codec } from '../kiri/codec.js';
import { broker } from '../moto/broker.js';
import { doTopShells } from '../kiri-mode/fdm/post.js';
import { Slicer as cam_slicer } from '../kiri-mode/cam/slicer.js';
import { wasm_ctrl } from '../geo/wasm.js';
const clib = self.ClipperLib;
@ -29,9 +29,14 @@ self.alert = function(o) {
};
self.onmessage = function(msg) {
let data = msg.data;
let cmd = data.cmd;
(funcs[cmd] || funcs.bad)(data, data.seq, cmd);
let { data } = msg;
let { cmd } = data;
debug('MINION.onmessage', { cmd, data });
try {
(funcs[cmd] || funcs.invalid)(data, data.seq, cmd);
} catch (error) {
log('MINION.dispatch.error', error);
}
};
function reply(msg, direct) {
@ -42,12 +47,22 @@ function log() {
console.log(`[${name}]`, ...arguments);
}
function debug() {
// console.log(`[${name}]`, ...arguments);
}
const funcs = self.minion = {
label(data, seq) {
name = data.name;
invalid(data, seq, cmd) {
console.error({ invalid_minion_command: cmd, data });
reply({ seq, error: `invalid command (${cmd})` });
},
config: data => {
label(data, seq) {
name = data.name;
self.kiri_minion = { name, cache, log };
},
config(data) {
if (data.base) {
Object.assign(base.config, data.base);
} else {
@ -55,7 +70,7 @@ const funcs = self.minion = {
}
},
union: (data, seq) => {
union(data, seq) {
if (!(data.polys && data.polys.length)) {
reply({ seq, union: codec.encode([]) });
return;
@ -66,7 +81,7 @@ const funcs = self.minion = {
reply({ seq, union: codec.encode(union) }, state.zeros);
},
topShells: (data, seq) => {
topShells(data, seq) {
let top = codec.decode(data.top, {full: true});
let {z, count, offset1, offsetN, fillOffset, opt} = data;
doTopShells(z, top, count, offset1, offsetN, fillOffset, opt);
@ -74,7 +89,7 @@ const funcs = self.minion = {
reply({ seq, top: codec.encode(top, {full: true}) }, state.zeros);
},
fill: (data, seq) => {
fill(data, seq) {
let polys = codec.decode(data.polys);
let { angle, spacing, minLen, maxLen } = data;
let fill = POLY.fillArea(polys, angle, spacing, [], minLen, maxLen);
@ -89,7 +104,7 @@ const funcs = self.minion = {
reply({ seq, fill: arr }, [ arr.buffer ]);
},
clip: (data, seq) => {
clip(data, seq) {
const clip = new clib.Clipper();
const ctre = new clib.PolyTree();
const clips = [];
@ -115,8 +130,8 @@ const funcs = self.minion = {
reply({ seq, clips }, state.zeros);
},
sliceZ: (data, seq) => {
console.log('minion.sliceZ', { data, seq });
sliceZ(data, seq) {
log('minion.sliceZ', { data, seq });
let { z, points, options } = data;
let i = 0, p = 0, realp = new Array(points.length / 3);
while (i < points.length) {
@ -136,7 +151,7 @@ const funcs = self.minion = {
});
},
putCache: msg => {
putCache(msg) {
const { key, data } = msg;
// log({ minion_putCache: key, data });
if (data) {
@ -146,13 +161,13 @@ const funcs = self.minion = {
}
},
clearCache: msg => {
clearCache(msg) {
for (let key in cache) {
delete cache[key];
}
},
wasm: data => {
wasm(data) {
if (data.enable) {
wasm_ctrl.enable();
} else {
@ -160,9 +175,27 @@ const funcs = self.minion = {
}
},
bad: (data, seq, cmd) => {
reply({ seq, error: `invalid command (${cmd})` });
}
};
cam_slice_init() {
cache.slicer = new cam_slicer();
},
broker.publish("minion.started", { funcs, cache, reply, log });
cam_slice_cleanup() {
delete cache.slicer;
},
cam_slice(data, seq) {
const { bucket, opt } = data;
cache.slicer.sliceBucket(bucket, opt, slice => {
// log({ slice });
}).then(data => {
data.forEach(rec => {
rec.polys = encode(rec.polys);
if (rec.lines) {
const points = rec.lines.map(l => [l.p1, l.p2]).flat();
rec.lines = encodePointArray(points);
}
});
reply({ seq, slices: data });
});
},
};

View file

@ -6,7 +6,6 @@ import '../add/three.js';
import '../ext/jszip.js';
import { base } from '../geo/base.js';
import { broker } from '../moto/broker.js';
import { codec } from '../kiri/codec.js';
import { util } from '../geo/base.js';
import { newPoint } from '../geo/point.js';
@ -28,8 +27,7 @@ import { WJET } from '../kiri-mode/wjet/driver.js';
const { time } = util;
let debug = (self.debug === true),
drivers = {
let drivers = {
DRAG,
CAM,
FDM,
@ -39,7 +37,7 @@ let debug = (self.debug === true),
WJET
},
ccvalue = self.navigator ? self.navigator.hardwareConcurrency || 0 : 0,
concurrent = Math.min(4, self.Worker && ccvalue > 3 ? ccvalue - 1 : 0),
concurrent = 1,//Math.min(4, self.Worker && ccvalue > 3 ? ccvalue - 1 : 0),
current = {
print: null,
snap: null,
@ -53,6 +51,11 @@ let debug = (self.debug === true),
minifns = {},
miniseq = 0;
function debug() {
if (self.debug === true)
console.log(...arguments);
}
// catch clipper alerts and convert to console messages
self.alert = function(o) {
console.log(o);
@ -88,11 +91,11 @@ const minwork = {
for (let i=0; i < concurrent; i++) {
let minion = new Worker(`/lib/kiri-run/minion.js`, { type: 'module' });
minion.onerror = (error) => {
console.log({ MINION_ERROR: error });
debug({ MINION_ERROR: error });
error.preventDefault();
};
minion.onmessageerror = (error) => {
console.log({ MINION_MESSAGE_ERROR: error });
debug({ MINION_MESSAGE_ERROR: error });
error.preventDefault();
};
minion.onmessage = minhandler;
@ -189,11 +192,11 @@ const minwork = {
},
sliceZ(z, points, options) {
console.log('minwork.sliceZ', { z, points, options });
debug('minwork.sliceZ', { z, points, options });
return new Promise((resolve, reject) => {
if (concurrent < 2) {
reject("concurrent slice unavaiable");
}
// if (concurrent < 2) {
// reject("concurrent slice unavaiable");
// }
let { each } = options;
// todo use shared array buffer?
let i = 0, floatP = new Float32Array(points.length * 3);
@ -202,7 +205,7 @@ const minwork = {
floatP[i++] = p.y;
floatP[i++] = p.z;
}
const state = { zeros: [] };
const state = { zeros: [ floatP.buffer ] };
minwork.queue({
cmd: "sliceZ",
z,
@ -219,12 +222,13 @@ const minwork = {
},
queue(work, ondone, direct) {
if (direct) {
return ondone(work);
}
debug('WORKER.queue', { work, direct });
// if (direct) {
// return ondone(work);
// }
let seq = ++miniseq;
minifns[seq] = ondone;
minionq.push({ seq, work });
minionq.push({ seq, work, direct });
minwork.kick();
},
@ -239,9 +243,10 @@ const minwork = {
return;
}
let minion = minions.shift();
let { seq, work } = minionq.shift();
let { seq, work, direct } = minionq.shift();
work.seq = seq;
minion.postMessage(work);
debug('WORKER.send', { work, direct });
minion.postMessage(work, direct);
minions.push(minion);
},
@ -768,5 +773,3 @@ CAM.init(worker);
FDM.init(worker);
LASER.init(worker);
SLA.init(worker);
broker.publish("worker.started", { dispatch, minions: minwork });

View file

@ -756,9 +756,14 @@ function setEnableWASM(bool) {
api.event.emit("set.assembly", bool);
}
// extend API (api.conf)
const apiConf = {
dbo: () => { return ls2o('ws-settings') },
// merged api for backward compatibility
const apiSet = {
dbo() { return ls2o('ws-settings') },
dev() { return settings.device },
proc() { return settings.process },
ctrl() { return settings.controller },
mode() { return settings.mode },
prof() { return settings.sproc[settings.mode] },
get: getSettings,
put: putSettings,
load: loadSettings,
@ -773,20 +778,9 @@ const apiConf = {
restore: restoreSettings,
export: settingsExport,
import: settingsImport,
};
// extend API (api.settings)
const apiSet = {
get: getSettings,
import: settingsImport,
import_zip: settingsImportZip,
import_url: settingsImportUrl,
import_prusa: settingsPrusaConvert,
dev() { return settings.device },
proc() { return settings.process },
ctrl() { return settings.controller },
mode() { return settings.mode },
prof() { return settings.sproc[settings.mode] },
sync: {
async get() {},
async put() {},
@ -794,14 +788,16 @@ const apiSet = {
}
};
export default {
conf: apiConf,
set: apiSet,
settings: apiSet
};
// for backward compatibility
apiSet.conf = apiSet;
apiSet.set = apiSet;
apiSet.settings = apiSet;
export default apiSet;
// for backward compatibility
export {
apiConf as conf,
apiSet as conf,
apiSet as set,
apiSet as settings
};

View file

@ -5,6 +5,7 @@ class Broker {
#topics = {};
#used = {};
#send = {};
#uuid = ((Math.random() * 0xffffffff) | 0).toString(36);
constructor() {
// using the pattern "broker.send(msg)" induces runtime errors
@ -36,6 +37,9 @@ class Broker {
// creates topic if new topic
// creates a send function if new topic
subscribe(topic, listener) {
if (this.#debug) {
console.log({ subscribe: topic, listener });
}
if (Array.isArray(topic)) {
for (let t of topic) {
this.subscribe(t, listener);
@ -91,7 +95,7 @@ class Broker {
this.publish(".topic.publish", {topic, message, options});
}
if (this.#debug && !topic.startsWith(".")) {
console.log({ topic, message });
console.log({ publish: topic, message });
}
// store last seen message on a topic
// acts as a tracker for all used topics