experimental async threaded cam topo
This commit is contained in:
parent
237052b65d
commit
db13a61d3b
9 changed files with 158 additions and 35 deletions
|
|
@ -33,7 +33,7 @@ class CamOp {
|
|||
return 1;
|
||||
}
|
||||
|
||||
slice() { }
|
||||
async slice() { }
|
||||
|
||||
prepare() { }
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ class OpLevel extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, addSlices } = state;
|
||||
let { updateToolDiams, tabs, cutTabs } = state;
|
||||
|
|
@ -164,7 +164,7 @@ class OpRough extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, slicer, addSlices, unsafe, color } = state;
|
||||
let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state;
|
||||
|
|
@ -476,7 +476,7 @@ class OpOutline extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, slicer, addSlices, tshadow, thruHoles, unsafe, color } = state;
|
||||
let { updateToolDiams, zThru, zBottom, shadowTop, tabs, cutTabs, cutPolys } = state;
|
||||
|
|
@ -727,13 +727,13 @@ class OpContour extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { addSlices } = state;
|
||||
// we need topo for safe travel moves when roughing and outlining
|
||||
// not generated when drilling-only. then all z moves use bounds max.
|
||||
// also generates x and y contouring when selected
|
||||
let topo = new CAM.Topo({
|
||||
let topo = await CAM.Topo({
|
||||
// onupdate: (update, msg) => {
|
||||
onupdate: (index, total, msg) => {
|
||||
progress(index / total, msg);
|
||||
|
|
@ -816,7 +816,7 @@ class OpTrace extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { tool, rate, down, plunge, offset, thru } = op;
|
||||
let { settings, widget, addSlices, zMax, zTop, zThru, tabs } = state;
|
||||
|
|
@ -1019,7 +1019,7 @@ class OpPocket extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
const pocket = this;
|
||||
const debug = false;
|
||||
let { op, state } = this;
|
||||
|
|
@ -1036,7 +1036,7 @@ class OpPocket extends CamOp {
|
|||
let engrave = contour && op.engrave;
|
||||
if (contour) {
|
||||
down = 0;
|
||||
this.topo = new CAM.Topo({
|
||||
this.topo = await CAM.Topo({
|
||||
// onupdate: (update, msg) => {
|
||||
onupdate: (index, total, msg) => {
|
||||
progress((index / total) * 0.9, msg);
|
||||
|
|
@ -1242,7 +1242,7 @@ class OpDrill extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, addSlices, tslices, updateToolDiams } = state;
|
||||
let { zBottom, zThru, thruHoles, color } = state;
|
||||
|
|
@ -1332,7 +1332,7 @@ class OpRegister extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, bounds, addSlices, zMax, zThru, color } = state;
|
||||
let { updateToolDiams } = state;
|
||||
|
|
@ -1484,7 +1484,7 @@ class OpXRay extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let { widget, addSlices } = this.state;
|
||||
let slicer = new kiri.cam_slicer(widget.getPoints(), {
|
||||
zlist: true,
|
||||
|
|
@ -1512,26 +1512,21 @@ class OpShadow extends CamOp {
|
|||
super(state, op);
|
||||
}
|
||||
|
||||
slice(progress) {
|
||||
async slice(progress) {
|
||||
let state = this.state;
|
||||
let { ops, slicer, widget, unsafe, addSlices, shadowAt } = state;
|
||||
|
||||
let real = ops.map(rec => rec.op).filter(op => op);
|
||||
let drill = real.filter(op => op.type === 'drill').length > 0;
|
||||
let rough = real.filter(op => op.type === 'rough').length > 0;
|
||||
let outline = real.filter(op => op.type === 'outline').length > 0;
|
||||
let outlineOut = real.filter(op => op.type === 'outline' && op.outside).length > 0;
|
||||
let realOps = ops.map(rec => rec.op).filter(op => op);
|
||||
let trueShadow = state.settings.process.camTrueShadow === true;
|
||||
|
||||
let minStepDown = real
|
||||
let minStepDown = realOps
|
||||
.map(op => (op.down || 3) / (trueShadow ? 1 : 3))
|
||||
.reduce((a,v) => Math.min(a, v, 1));
|
||||
|
||||
let tslices = [];
|
||||
let tshadow = [];
|
||||
let tzindex = slicer.interval(minStepDown, { fit: true, off: 0.01, down: true, flats: true });
|
||||
let complex = tzindex.length > 50 || widget.vertices.length > 1000000;
|
||||
let skipTerrain = unsafe;// || (!rough && !outline && !drill && complex);
|
||||
let skipTerrain = unsafe;
|
||||
|
||||
if (skipTerrain) {
|
||||
console.log("skipping terrain generation");
|
||||
|
|
@ -1540,7 +1535,6 @@ class OpShadow extends CamOp {
|
|||
|
||||
let lsz; // only shadow up to bottom of last shadow for progressive union
|
||||
let terrain = slicer.slice(tzindex, { each: (data, index, total) => {
|
||||
// let shadow = trueShadow ? CAM.shadowAt(widget, data.z, lsz) : [];
|
||||
let shadow = trueShadow ? shadowAt(data.z, lsz) : [];
|
||||
tshadow = POLY.union(tshadow.slice().appendAll(data.tops).appendAll(shadow), 0.01, true);
|
||||
tslices.push(data.slice);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const POLY = polygons;
|
|||
* @param {Widget} widget
|
||||
* @param {Function} output
|
||||
*/
|
||||
CAM.slice = function(settings, widget, onupdate, ondone) {
|
||||
CAM.slice = async function(settings, widget, onupdate, ondone) {
|
||||
let mesh = widget.mesh,
|
||||
proc = settings.process,
|
||||
stock = settings.stock || {},
|
||||
|
|
@ -238,7 +238,7 @@ CAM.slice = function(settings, widget, onupdate, ondone) {
|
|||
setAxisIndex();
|
||||
for (let op of opList) {
|
||||
let weight = op.weight();
|
||||
op.slice((progress, message) => {
|
||||
await op.slice((progress, message) => {
|
||||
onupdate((opSum + (progress * weight)) / opTot, message || op.type());
|
||||
});
|
||||
tracker.rotation = isIndexed ? axisRotation : 0;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
/**
|
||||
* Slicing engine used by CAM
|
||||
*/
|
||||
// dep: kiri.slice
|
||||
gapp.register("kiri-mode.cam.slicer", [], (root, exports) => {
|
||||
|
||||
const { base, kiri } = root;
|
||||
|
|
@ -14,13 +15,18 @@ const { newSlice } = kiri;
|
|||
const POLY = polygons;
|
||||
|
||||
class Slicer {
|
||||
constructor(points, options) {
|
||||
constructor(points, options = {}) {
|
||||
this.options = {};
|
||||
this.setOptions(options);
|
||||
if (points) {
|
||||
this.setPoints(points, options);
|
||||
}
|
||||
}
|
||||
|
||||
get threaded() {
|
||||
return this.options.threaded;
|
||||
}
|
||||
|
||||
// notopok = when genso set, allow empty top array
|
||||
// emptyok = allow empty slices
|
||||
// openok = allow open tops
|
||||
|
|
@ -129,10 +135,12 @@ class Slicer {
|
|||
let zSum = this.zSum;
|
||||
let zMax = this.bounds.max.z;
|
||||
let points = this.points;
|
||||
let bucketCount = Math.max(1, Math.ceil(zMax / (zSum / points.length)) - 1);
|
||||
let bucketCount = this.threaded ? 0 : Math.max(1, Math.ceil(zMax / (zSum / points.length)) - 1);
|
||||
let zScale = this.zScale = 1 / (zMax / bucketCount);
|
||||
let buckets = this.buckets = {};
|
||||
|
||||
if (bucketCount > 1) {
|
||||
buckets.length = bucketCount;
|
||||
// create empty buckets
|
||||
let min = Math.floor(bounds.min.z * zScale);
|
||||
let max = Math.ceil(bounds.max.z * zScale);
|
||||
|
|
@ -158,6 +166,14 @@ class Slicer {
|
|||
return this;
|
||||
}
|
||||
|
||||
async sliceAsync(zarr, options) {
|
||||
if (!this.threaded) {
|
||||
return this.slice(zarr, options);
|
||||
}
|
||||
return await kiri.minions.camSliceZ(zarr, this, options);
|
||||
// return this.slice(zarr, options);
|
||||
}
|
||||
|
||||
// slice through points at given Z and return polygons
|
||||
slice(z, options, index, total, mark) {
|
||||
const opt = this.setOptions(options);
|
||||
|
|
|
|||
|
|
@ -21,11 +21,14 @@ const POLY = polygons;
|
|||
const RAD2DEG = 180 / Math.PI;
|
||||
|
||||
class Topo {
|
||||
constructor(opt = {}) {
|
||||
constructor() { }
|
||||
|
||||
async generate(opt = {}) {
|
||||
let { state, contour, onupdate, ondone } = opt;
|
||||
let { widget, settings, tshadow, center, tabs, color } = opt.state;
|
||||
|
||||
let density = parseInt(settings.controller.animesh || 100) * 2500,
|
||||
let { controller, process } = settings,
|
||||
density = parseInt(controller.animesh || 100) * 2500,
|
||||
axis = contour.axis.toLowerCase(),
|
||||
contourX = axis === "x",
|
||||
contourY = axis === "y",
|
||||
|
|
@ -33,7 +36,6 @@ class Topo {
|
|||
tolerance = contour.tolerance,
|
||||
flatness = contour.flatness || 0.005,
|
||||
bridge = contour.bridging || 0,
|
||||
proc = settings.process,
|
||||
shadow = tshadow,
|
||||
minX = bounds.min.x,
|
||||
maxX = bounds.max.x,
|
||||
|
|
@ -73,7 +75,7 @@ class Topo {
|
|||
newslices = [],
|
||||
tabsMax = tabs ? Math.max(...tabs.map(tab => tab.dim.z/2 + tab.pos.z)) : 0,
|
||||
tabsOn = tabs,
|
||||
tabHeight = Math.max(proc.camTabsHeight + zBottom, tabsMax),
|
||||
tabHeight = Math.max(process.camTabsHeight + zBottom, tabsMax),
|
||||
clipTab = tabsOn ? [] : null,
|
||||
clipTo = inside ? shadow : POLY.expand(shadow, toolDiameter/2 + resolution * 3),
|
||||
partOff = inside ? 0 : toolDiameter / 2 + resolution,
|
||||
|
|
@ -437,21 +439,25 @@ class Topo {
|
|||
|
||||
let points = base.verticesToPoints(widget.getGeoVertices(true, true));
|
||||
let slicer = new kiri.cam_slicer(points, {
|
||||
swapX: true, emptyok: true, notopok: true
|
||||
swapX: true, emptyok: true, notopok: true, threaded: controller.threaded
|
||||
});
|
||||
let sindex = slicer.interval(resolution);
|
||||
if (!topo.slices) stepsTotal += sindex.length * 2;
|
||||
if (contourX) stepsTotal += Math.round((maxY-minY) / toolStep);
|
||||
if (contourY) stepsTotal += Math.round((maxX-minX) / toolStep);
|
||||
|
||||
let slices = topo.slices = topo.slices || slicer.slice(sindex, { each: (data, index, total) => {
|
||||
let slices = topo.slices = topo.slices || await slicer.sliceAsync(sindex, { each: (data, index, total) => {
|
||||
onupdate(++stepsTaken, stepsTotal, "topo slice");
|
||||
}, genso: true });
|
||||
|
||||
processSlices(slices.filter(s => s.lines).map(data => data.slice));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
CAM.Topo = Topo;
|
||||
CAM.Topo = async function(opt) {
|
||||
return new Topo().generate(opt);
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ FDM.init = function(kiri, api) {
|
|||
const { ui, uc } = api;
|
||||
const rangeVars = {
|
||||
// slice
|
||||
// "sliceHeight": LANG.sl_lahi_s,
|
||||
"sliceShells": LANG.sl_shel_s,
|
||||
"sliceFillType": LANG.fi_type,
|
||||
"sliceFillWidth": LANG.fi_wdth_s,
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ FDM.slice = function(settings, widget, onupdate, ondone) {
|
|||
z += h;
|
||||
}
|
||||
}
|
||||
// reduce slice position by half height
|
||||
// reduce slice position by half layer height
|
||||
for (let i=0; i<zi.length; i++) {
|
||||
zi[i] = (zi[i] - zh[i] / 2).round(3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
// dep: geo.wasm
|
||||
// dep: kiri.codec
|
||||
// dep: kiri-mode.fdm.post
|
||||
// use: kiri-mode.cam.slicer
|
||||
// dep: ext.clip2
|
||||
gapp.register("kiri-run.minion", [], (root, exports) => {
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ const clib = self.ClipperLib;
|
|||
const ctyp = clib.ClipType;
|
||||
const ptyp = clib.PolyType;
|
||||
const cfil = clib.PolyFillType;
|
||||
const cache = self.cache = {};
|
||||
|
||||
let name = "unknown";
|
||||
|
||||
|
|
@ -136,6 +138,38 @@ const funcs = {
|
|||
});
|
||||
},
|
||||
|
||||
camSliceZ: (data, seq) => {
|
||||
const { zarr, set } = data;
|
||||
const slicer = new kiri.cam_slicer();
|
||||
set.points = cache.camPoints;
|
||||
set.options.each = (data, index, total) => {
|
||||
// log({ data, index, total });
|
||||
datas.push({
|
||||
z: data.z,
|
||||
lines: kiri.codec.encodePointArray(data.lines.map(line => [ line.p1, line.p2 ]).flat())
|
||||
});
|
||||
};
|
||||
Object.assign(slicer, set);
|
||||
const datas = [];
|
||||
slicer.slice(zarr);
|
||||
reply({ seq, data: datas });
|
||||
},
|
||||
|
||||
camSetPoints: data => {
|
||||
const points = new Float32Array(data.data.slice());
|
||||
let i = 0, p = 0, realp = new Array(points.length / 3);
|
||||
while (i < points.length) {
|
||||
realp[p++] = base.newPoint(points[i++], points[i++], points[i++]).round(3);
|
||||
}
|
||||
// log({ camSetPoints: points, realp });
|
||||
cache.camPoints = realp;
|
||||
},
|
||||
|
||||
camClearPoints: data => {
|
||||
// log({ camClearPoints: data });
|
||||
delete cache.camPoints;
|
||||
},
|
||||
|
||||
wasm: data => {
|
||||
if (data.enable) {
|
||||
base.wasm_ctrl.enable();
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ let debug = self.debug === true,
|
|||
},
|
||||
wgroup = {},
|
||||
wcache = {},
|
||||
pcache = {},
|
||||
minions = [],
|
||||
minionq = [],
|
||||
minifns = {},
|
||||
|
|
@ -179,6 +180,7 @@ kiri.minions = {
|
|||
reject("concurrent slice unavaiable");
|
||||
}
|
||||
let { each } = options;
|
||||
// todo use shared array buffer?
|
||||
let i = 0, floatP = new Float32Array(points.length * 3);
|
||||
for (let p of points) {
|
||||
floatP[i++] = p.x;
|
||||
|
|
@ -202,11 +204,72 @@ kiri.minions = {
|
|||
});
|
||||
},
|
||||
|
||||
camSliceZ(zarr, slicer, options) {
|
||||
let { points, bounds, zFlat, zLine, zList, zSum, ox } = slicer;
|
||||
let shared = new SharedArrayBuffer(points.length * 3 * 4)
|
||||
let i = 0, floatP = new Float32Array(shared);
|
||||
options = Object.assign(options, slicer.options);
|
||||
options.each();
|
||||
delete options.each;
|
||||
for (let p of points) {
|
||||
floatP[i++] = p.x;
|
||||
floatP[i++] = p.y;
|
||||
floatP[i++] = p.z;
|
||||
}
|
||||
minwork.broadcast('camSetPoints', shared);
|
||||
let ps = [];
|
||||
let step = zarr.length / concurrent;
|
||||
while (zarr.length) {
|
||||
let arr = zarr.slice(0, step);
|
||||
zarr = zarr.slice(step);
|
||||
ps.push(minwork.queueAsync({
|
||||
cmd: "camSliceZ",
|
||||
zarr: arr,
|
||||
set: {
|
||||
bounds,
|
||||
buckets: {},
|
||||
options,
|
||||
zFlat,
|
||||
zLine,
|
||||
zList,
|
||||
zSum,
|
||||
ox
|
||||
}
|
||||
}));
|
||||
}
|
||||
return Promise.all(ps).then(data => {
|
||||
minwork.broadcast('camClearPoints');
|
||||
let result = data
|
||||
.map(data => data.data)
|
||||
.flat()
|
||||
.map(rec => {
|
||||
const slice = kiri.newSlice(rec.z);
|
||||
const points = kiri.codec.decodePointArray(rec.lines);
|
||||
slice.lines = [];
|
||||
points.forEachPair((p1, p2) => {
|
||||
slice.lines.push(base.newLine(p1, p2));
|
||||
});
|
||||
return {
|
||||
z: rec.z,
|
||||
lines: slice.lines,
|
||||
slice
|
||||
};
|
||||
});
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
queue(work, ondone, direct) {
|
||||
minionq.push({work, ondone, direct});
|
||||
minwork.kick();
|
||||
},
|
||||
|
||||
queueAsync(work, direct) {
|
||||
return new Promise(resolve => {
|
||||
minwork.queue(work, resolve, direct);
|
||||
});
|
||||
},
|
||||
|
||||
kick() {
|
||||
if (minions.length && minionq.length) {
|
||||
let qrec = minionq.shift();
|
||||
|
|
@ -229,6 +292,14 @@ kiri.minions = {
|
|||
enable
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
broadcast(cmd, data, direct) {
|
||||
for (let minion of minions) {
|
||||
minion.postMessage({
|
||||
cmd, data
|
||||
}, direct);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ gapp.register("kiri.init", [], (root, exports) => {
|
|||
FDM_SLA = [MODES.FDM,MODES.SLA],
|
||||
FDM_CAM = [MODES.CAM,MODES.FDM],
|
||||
FDM_LZR = [MODES.LASER,MODES.FDM],
|
||||
FDM_CAM_SLA = [MODES.CAM,MODES.FDM,MODES.SLA],
|
||||
FDM_LZR_SLA = [MODES.LASER,MODES.FDM,MODES.SLA],
|
||||
CAM_LZR = [MODES.LASER,MODES.CAM],
|
||||
GCODE = [MODES.FDM, MODES.LASER, MODES.CAM],
|
||||
|
|
@ -1800,7 +1801,7 @@ gapp.register("kiri.init", [], (root, exports) => {
|
|||
parts: uc.newGroup(LANG.pt_menu, $('prefs-prt'), {inline: true}),
|
||||
detail: uc.newSelect(LANG.pt_qual_s, {title: LANG.pt_qual_l, action: detailSave}, "detail"),
|
||||
healMesh: uc.newBoolean(LANG.pt_heal_s, booleanSave, {title: LANG.pt_heal_l}),
|
||||
threaded: uc.newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l, modes:FDM_SLA}),
|
||||
threaded: uc.newBoolean(LANG.pt_thrd_s, booleanSave, {title: LANG.pt_thrd_l, modes:FDM_CAM_SLA}),
|
||||
assembly: uc.newBoolean(LANG.pt_assy_s, booleanSave, {title: LANG.pt_assy_l, modes:FDM_SLA}),
|
||||
|
||||
prefadd: uc.checkpoint($('prefs-add')),
|
||||
|
|
|
|||
Loading…
Reference in a new issue