migrate fdm, sla, laser to new core slicer

This commit is contained in:
Stewart Allen 2022-02-16 22:57:12 -05:00
commit a438275e9a
11 changed files with 357 additions and 1081 deletions

View file

@ -56,19 +56,19 @@
* that will not quickly encode in threaded mode. add to existing
* data object. return is ignored.
*/
FDM.slicePost = function(data, options, params) {
let { lines, groups, tops } = data;
let { z, index, total, height, thick } = params;
let { useAssembly, post } = options;
let { process, isSynth, isDanger, vaseMode } = post;
let { shellOffset, fillOffset, clipOffset } = post;
BASE.slicePost.FDM = function(data, options) {
let { z, lines, groups } = data;
let { useAssembly, post_args, index } = options;
let { process, isSynth, isDanger, vaseMode } = post_args;
let { shellOffset, fillOffset, clipOffset } = post_args;
if (isSynth) {
// do not shell synth widgets because
// they will be clipped against peers later
// which requires shelling post-clip
return;
}
let range = getRangeParameters(process, index);
let tops = POLY.nest(groups);
let range = FDM.getRangeParameters(process, index);
// calculate fractional shells
let shellFrac = (range.sliceShells - (range.sliceShells | 0));
let sliceShells = range.sliceShells | 0;
@ -80,7 +80,9 @@
let trg = shellFrac > 0.5 ? 1 : parts - 1;
sliceShells += rem >= trg ? 1 : 0;
}
let spaceMult = index === 0 ? process.firstLayerLineMult || 1 : 1;
let isFirst = index === 0;
let height = process.sliceHeight;
let spaceMult = isFirst ? process.firstLayerLineMult || 1 : 1;
let count = isSynth ? 1 : sliceShells;
let offset = shellOffset * spaceMult;
let fillOff = fillOffset * spaceMult;
@ -96,6 +98,7 @@
}
data.clip = clipOffset ? POLY.offset(nutops.map(t => t.simple), clipOffset) : undefined;
data.tops = nutops;
delete data.groups;
};
FDM.sliceAll = function(settings, onupdate) {
@ -200,20 +203,47 @@
sliceHeightBase = sliceMinHeight || sliceHeight;
}
SLICER.sliceWidget(widget, {
mode: 'FDM',
zCut: widget.track.zcut || 0,
zPress: isBelt ? process.firstLayerFlatten || 0 : 0,
isBelt,
height: sliceHeight,
minHeight: sliceMinHeight,
firstHeight: sliceHeightBase,
let bounds = widget.getBoundingBox();
let points = widget.getPoints();
let indices = [];
let heights = [];
// handle z cutting (floor method) and base flattening
let zPress = process.firstLayerFlatten || 0;
let zCut = widget.track.zcut || 0;
if (zCut || zPress) {
for (let p of points) {
if (!p._z) {
p._z = p.z;
if (zPress) {
if (isBelt) {
let zb = (p.z - p.y) * beltfact;
if (zb > 0 && zb <= zPress) {
p.y += zb * beltfact;
p.z -= zb * beltfact;
}
} else {
if (p.z <= zPress) p.z = 0;
}
}
if (zCut && !isBelt) {
p.z -= zCut;
}
}
}
}
BASE.slice(points, {
debug: process.xray,
xray: process.xray,
zMin: bounds.min.z,
zMax: bounds.max.z - zCut,
// support/synth usually has overlapping boxes
union: controller.healMesh || isSynth,
indices: process.indices || process.xray,
concurrent: isConcurrent,
useAssembly,
post: {
post: 'FDM',
post_args: {
shellOffset,
fillOffset,
clipOffset,
@ -223,13 +253,111 @@
process,
isDanger,
},
xray: process.xray,
debug: process.xray
}, slices => {
onSliceDone(slices).then(ondone);
}, update => {
return onupdate(0.0 + update * 0.5);
});
// z index generator
zGen(zopt) {
if (process.xray) {
return zopt.zIndexes;
}
let { zMin, zMax } = zopt;
let h1 = sliceHeight;
let h0 = sliceHeightBase || h1;
let hm = sliceMinHeight || 0;
let h = h0;
let z = h0;
let zi = indices; // indices
let zh = heights; // heights
if (hm) {
// adaptive increments based on z indices (var map to legacy code)
let zIncFirst = h0;
let zInc = h1;
let zIncMin = hm;
let zHeights = heights;
let zIndexes = indices;
let zOrdered = Object.values(zopt.zIndexes).map(v => parseFloat(v));
// console.log('adaptive slicing', zIncMin, ':', zInc, 'from', zMin, 'to', zMax);
let zPos = zIncFirst,
zOI = 0,
zDelta,
zDivMin,
zDivMax,
zStep,
nextZ,
lzp = zPos;
// adaptive slice height
// first slice/height is fixed from base
zHeights.push(zIncFirst);
zIndexes.push(zIncFirst);
// console.log({zIncFirst, zOrdered})
while (zPos < zMax && zOI < zOrdered.length) {
nextZ = zOrdered[zOI++];
if (zPos >= nextZ) {
// console.log('skip',{zPos},'>=',{nextZ});
continue;
}
zDelta = nextZ - zPos;
if (zDelta < zIncMin) {
// console.log('skip',{zDelta},'<',{zIncMin});
continue;
}
zDivMin = Math.floor(zDelta / zIncMin);
zDivMax = Math.floor(zDelta / zInc);
if (zDivMax && zDivMax <= zDivMin) {
if (zDelta % zInc > 0.01) zDivMax++;
zStep = zDelta / zDivMax;
// console.log(`--- zDivMax <= zDivMin ---`, zStep, zDelta % zInc)
} else {
zStep = zDelta;
}
// console.log({nextZ, zPos, zDelta, zStep, zDivMin, zDivMax})
while (zPos < nextZ) {
zHeights.push(zStep);
zIndexes.push(zPos + zStep);
zPos += zStep;
// console.log({D: zPos - lzp, zPos})
// lzp = zPos;
}
}
// console.log({zIndexes, zHeights});
} else {
// simple based + fixed increment
while (z <= zMax) {
zh.push(h);
zi.push(z);
h = h1;
z += h;
}
}
// reduce slice position by half height
for (let i=0; i<zi.length; i++) {
zi[i] = (zi[i] - zh[i] / 2).round(3);
}
return zi;
},
// slicer function (worker local or minion distributed)
slicer(z, points, opts) {
return (isConcurrent ? KIRI.minions.sliceZ : BASE.sliceZ)(z, points, opts);
},
onupdate(update) {
return onupdate(0.0 + update * 0.5)
}
}).then((output) => {
// post process slices and re-incorporate missing meta-data
return output.slices.map(data => {
let { z, clip, lines, groups } = data;
let slice = newSlice(z).addTops(data.tops);
slice.index = indices.indexOf(z);
slice.height = heights[slice.index];
slice.clips = clip;
if (process.xray) {
slice.lines = lines;
slice.groups = groups;
slice.xray = process.xray;
}
return slice;
});
}).then(slices => {
return onSliceDone(slices);
}).then(ondone);
async function doShadow(slices) {
if (widget.shadow) {
@ -271,6 +399,12 @@
}
}).reverse();
// connect slices into linked list for island/bridge projections
for (let i=1; i<slices.length; i++) {
slices[i-1].up = slices[i];
slices[i].down = slices[i-1];
}
widget.slices = slices;
if (!slices || slices.length === 0) {

View file

@ -22,7 +22,7 @@
exportSVG,
exportDXF
},
SLICER = KIRI.slicer,
newSlice = KIRI.newSlice,
newPoint = BASE.newPoint;
function init(kiri, api) {
@ -42,30 +42,66 @@
*/
function slice(settings, widget, onupdate, ondone) {
let proc = settings.process;
let offset = proc.laserOffset;
let offset = proc.laserOffset || 0;
let color = settings.controller.dark ? 0xbbbbbb : 0;
if (proc.laserSliceHeight < 0) {
return ondone("invalid slice height");
}
SLICER.sliceWidget(widget, {
single: proc.laserSliceSingle,
height: proc.laserSliceHeight,
minHeight: proc.laserSliceHeight === 0 ? proc.laserSliceHeightMin : 0
}, function(slices) {
widget.slices = slices;
slices.forEach(function(slice, index) {
let tops = slice.tops.map(t => t.poly);
let { laserSliceSingle, laserSliceHeight, laserSliceHeightMin } = proc;
let bounds = widget.getBoundingBox();
let points = widget.getPoints();
let indices = [];
BASE.slice(points, {
zMin: bounds.min.z,
zMax: bounds.max.z,
zGen(zopt) {
let { zMin, zMax, zIndexes } = zopt;
if (laserSliceSingle) {
indices = [ laserSliceHeight ];
} else if (laserSliceHeight) {
for (let z = zMin + laserSliceHeight / 2; z < zMax; z += laserSliceHeight) {
indices.push(z);
}
indices;
} else {
for (let i = 1; i < zIndexes.length; i++) {
indices.push((zIndexes[i-1] + zIndexes[i]) / 2);
}
// discard layers too
if (laserSliceHeightMin) {
let last;
indices = indices.filter(v => {
let ok = true;
if (last !== undefined && Math.abs(v - last) < laserSliceHeightMin) {
ok = false;
} else {
last = v;
}
return ok;
});
}
}
return indices;
},
onupdate(v) {
onupdate(v);
}
}).then(output => {
let slices = widget.slices = output.slices.map(data => {
let { z, lines, groups } = data;
let tops = POLY.nest(groups);
let slice = newSlice(z).addTops(tops);
slice.index = indices.indexOf(z);
let offsets = slice.offset = offset ?
POLY.offset(tops, offset, {z: slice.z, miter: 2 / offset}) : tops;
slice.output().setLayer("layer", { line: 0x888800 }).addPolys(tops);
slice.output().setLayer("cut", { line: color }).addPolys(offsets);
onupdate(0.80 + (index/slices.length) * 0.20);
return slice;
});
ondone();
}, function(update) {
onupdate(0.0 + update * 0.80)
});
};

View file

@ -10,7 +10,6 @@
UTIL = BASE.util,
SLA = KIRI.driver.SLA,
FDM = KIRI.driver.FDM.share,
SLICER = KIRI.slicer,
tracker = UTIL.pwait,
newTop = KIRI.newTop,
newSlice = KIRI.newSlice,
@ -78,13 +77,13 @@
let height = process.slaSlice || 0.05;
async function onSliceDone(slices) {
// hold onto last (empty) slice
let last = slices[slices.length-1];
// remove empty slices
slices = widget.slices = slices.filter(slice => slice.tops.length);
if (!process.slaOpenTop && !process.xray) {
// re-add last empty slice for open top
slices.push(last);
// re-add last empty slice for closed top
let cap = newSlice(bounds.max.z + height);
cap.index = slices.last().index + 1;
slices.push(cap);
}
// prepend raft layers to slices array
if (process.slaSupportEnable && process.slaSupportLayers) {
@ -209,18 +208,31 @@
doRender(widget);
}
SLICER.sliceWidget(widget, {
height: height,
add: !process.slaOpenTop,
union: controller.healMesh,
concurrent: isConcurrent,
let bounds = widget.getBoundingBox();
let points = widget.getPoints();
BASE.slice(points, {
indices: process.indices || process.xray,
union: controller.healMesh,
debug: process.xray,
xray: process.xray
}, function(slices) {
xray: process.xray,
zMin: bounds.min.z + height / 2,
zMax: bounds.max.z,
zInc: height,
// slicer function (worker local or minion distributed)
slicer(z, points, opts) {
return (isConcurrent ? KIRI.minions.sliceZ : BASE.sliceZ)(z, points, opts);
},
onupdate(v) {
return onupdate(0.0 + v * 0.25);
}
}).then(output => {
let slices = widget.slices = output.slices.map(data => {
let { z, lines, groups } = data;
let tops = POLY.nest(groups);
return newSlice(z).addTops(tops);
});
onSliceDone(slices).then(ondone);
}, function(update) {
return onupdate(0.0 + update * 0.25);
});
};