add thin wall pull-type to allow for multiple strategies

This commit is contained in:
Stewart Allen 2022-02-23 16:17:52 -05:00
commit 074dc2c99d
6 changed files with 145 additions and 129 deletions

View file

@ -53,14 +53,14 @@ base.slicePost.FDM = function(data, options) {
const count = isSynth ? 1 : sliceShells;
const offset = shellOffset * spaceMult;
const fillOff = fillOffset * spaceMult;
const thinType = isSynth ? undefined : process.sliceDetectThin;
const nutops = [];
// co-locate shell processing with top generation in slicer
for (let top of data.tops) {
nutops.push(FDM.doTopShells(z, top, count, offset/2, offset, fillOff, {
vase: vaseMode,
thin: process.detectThinWalls && !isSynth,
wasm: useAssembly,
danger: isDanger
thinType,
vaseMode,
useAssembly
}));
}
data.clip = clipOffset ? POLY.offset(nutops.map(t => t.simple), clipOffset) : undefined;
@ -81,10 +81,10 @@ FDM.doTopShells = function(z, top, count, offset1, offsetN, fillOffset, opt = {}
// add simple (low rez poly) where less accuracy is OK
top.simple = top.poly.simple();
let wasm = opt.wasm;
let wasm = opt.useAssembly;
let top_poly = [ top.poly ];
if (opt.vase) {
if (opt.vaseMode) {
// remove top poly inners in vase mode
top.poly = top.poly.clone(false);
}
@ -107,82 +107,86 @@ FDM.doTopShells = function(z, top, count, offset1, offsetN, fillOffset, opt = {}
let dist = p.first().distTo2D(p.last());
if (dist < 1) p.open = false;
} });
if (opt.danger && opt.thin) {
top.thin_fill = [];
top.fill_sparse = [];
let layers = POLY.inset(top_poly, offsetN, count, z, true);
last = layers.last().mid;
top.shells = layers.map(r => r.mid).flat();
top.gaps = layers.map(r => r.gap).flat();
let off = offsetN;
let min = off * 0.75;
let max = off * 4;
for (let poly of layers.map(r => r.gap).flat()) {
let centers = poly.centers(off/2, z, min, max, {lines:false});
top.fill_sparse.appendAll(centers);
// top.fill_lines.appendAll(centers);
}
} else if (opt.thin) {
top.thin_fill = [];
let oso = {z, count, gaps: [], outs: [], minArea: 0.05, wasm};
POLY.offset(top_poly, [-offset1, -offsetN], oso);
switch (opt.thinType) {
case "type 1":
top.thin_fill = [];
top.fill_sparse = [];
let layers = POLY.inset(top_poly, offsetN, count, z, true);
last = layers.last().mid;
top.shells = layers.map(r => r.mid).flat();
top.gaps = layers.map(r => r.gap).flat();
let off = offsetN;
let min = off * 0.75;
let max = off * 4;
for (let poly of layers.map(r => r.gap).flat()) {
let centers = poly.centers(off/2, z, min, max, {lines:false});
top.fill_sparse.appendAll(centers);
// top.fill_lines.appendAll(centers);
}
break;
case "type 2":
top.thin_fill = [];
let oso = {z, count, gaps: [], outs: [], minArea: 0.05, wasm};
POLY.offset(top_poly, [-offset1, -offsetN], oso);
oso.outs.forEach((polys, i) => {
polys.forEach(p => {
p.depth = i;
if (p.fill_off) {
p.fill_off.forEach(pi => pi.depth = i);
}
if (p.inner) {
for (let pi of p.inner) {
pi.depth = p.depth;
oso.outs.forEach((polys, i) => {
polys.forEach(p => {
p.depth = i;
if (p.fill_off) {
p.fill_off.forEach(pi => pi.depth = i);
}
}
top.shells.push(p);
if (p.inner) {
for (let pi of p.inner) {
pi.depth = p.depth;
}
}
top.shells.push(p);
});
last = polys;
});
last = polys;
});
// slice.solids.trimmed = slice.solids.trimmed || [];
oso.gaps.forEach((polys, i) => {
let off = (i == 0 ? offset1 : offsetN);
polys = POLY.offset(polys, -off * 0.8, {z, minArea: 0, wasm});
top.thin_fill.appendAll(cullIntersections(
fillArea(polys, 45, off/2, [], 0.01, off*2),
fillArea(polys, 135, off/2, [], 0.01, off*2),
));
gaps = polys;
});
} else {
// standard wall offsetting strategy
POLY.offset(
top_poly,
[-offset1, -offsetN],
{
z,
wasm,
count,
outs: top.shells,
flat: true,
call: (polys, onCount) => {
last = polys;
// mark each poly with depth (offset #) starting at 0
for (let p of polys) {
p.depth = count - onCount;
if (p.fill_off) p.fill_off.forEach(function(pi) {
// use negative offset for inners
pi.depth = -(count - onCount);
});
// mark inner depth to match parent
if (p.inner) {
for (let pi of p.inner) {
pi.depth = p.depth;
// slice.solids.trimmed = slice.solids.trimmed || [];
oso.gaps.forEach((polys, i) => {
let off = (i == 0 ? offset1 : offsetN);
polys = POLY.offset(polys, -off * 0.8, {z, minArea: 0, wasm});
top.thin_fill.appendAll(cullIntersections(
fillArea(polys, 45, off/2, [], 0.01, off*2),
fillArea(polys, 135, off/2, [], 0.01, off*2),
));
gaps = polys;
});
break;
default:
// standard wall offsetting strategy
POLY.offset(
top_poly,
[-offset1, -offsetN],
{
z,
wasm,
count,
outs: top.shells,
flat: true,
call: (polys, onCount) => {
last = polys;
// mark each poly with depth (offset #) starting at 0
for (let p of polys) {
p.depth = count - onCount;
if (p.fill_off) p.fill_off.forEach(function(pi) {
// use negative offset for inners
pi.depth = -(count - onCount);
});
// mark inner depth to match parent
if (p.inner) {
for (let pi of p.inner) {
pi.depth = p.depth;
}
}
}
}
}
}
);
);
break;
}
}
} else {
@ -210,4 +214,52 @@ FDM.doTopShells = function(z, top, count, offset1, offsetN, fillOffset, opt = {}
return top;
}
/**
* given an array of arrays of points (lines), eliminate intersections
* between groups, then return a unified array of shortest non-intersects.
*/
function cullIntersections() {
function toLines(pts) {
let lns = [];
for (let i=0, il=pts.length; i<il; i += 2) {
lns.push({a: pts[i], b: pts[i+1], l: pts[i].distTo2D(pts[i+1])});
}
return lns;
}
let aOa = [...arguments].filter(t => t);
if (aOa.length < 1) return;
let aa = toLines(aOa.shift());
while (aOa.length) {
let bb = toLines(aOa.shift());
loop: for (let i=0, il=aa.length; i<il; i++) {
let al = aa[i];
if (al.del) {
continue;
}
for (let j=0, jl=bb.length; j<jl; j++) {
let bl = bb[j];
if (bl.del) {
continue;
}
if (util.intersect(al.a, al.b, bl.a, bl.b, base.key.SEGINT)) {
if (al.l < bl.l) {
bl.del = true;
} else {
al.del = true;
}
continue;
}
}
}
aa = aa.filter(l => !l.del).concat(bb.filter(l => !l.del));
}
let good = [];
for (let i=0, il=aa.length; i<il; i++) {
let al = aa[i];
good.push(al.a);
good.push(al.b);
}
return good.length > 2 ? good : [];
}
});

View file

@ -1446,56 +1446,6 @@ function projectSolid(slice, polys, count, up, first) {
}
}
/**
* given an array of arrays of points (lines), eliminate intersections
* between groups, then return a unified array of shortest non-intersects.
*
* @returns {Point[]}
*/
function cullIntersections() {
function toLines(pts) {
let lns = [];
for (let i=0, il=pts.length; i<il; i += 2) {
lns.push({a: pts[i], b: pts[i+1], l: pts[i].distTo2D(pts[i+1])});
}
return lns;
}
let aOa = [...arguments].filter(t => t);
if (aOa.length < 1) return;
let aa = toLines(aOa.shift());
while (aOa.length) {
let bb = toLines(aOa.shift());
loop: for (let i=0, il=aa.length; i<il; i++) {
let al = aa[i];
if (al.del) {
continue;
}
for (let j=0, jl=bb.length; j<jl; j++) {
let bl = bb[j];
if (bl.del) {
continue;
}
if (util.intersect(al.a, al.b, bl.a, bl.b, base.key.SEGINT)) {
if (al.l < bl.l) {
bl.del = true;
} else {
al.del = true;
}
continue;
}
}
}
aa = aa.filter(l => !l.del).concat(bb.filter(l => !l.del));
}
let good = [];
for (let i=0, il=aa.length; i<il; i++) {
let al = aa[i];
good.push(al.a);
good.push(al.b);
}
return good.length > 2 ? good : [];
}
FDM.supports = function(settings, widget) {
let isBelt = settings.device.bedBelt;
let process = settings.process;

View file

@ -364,7 +364,7 @@ const conf = exports({
outputLoops: 0,
outputInvertX: false,
outputInvertY: false,
detectThinWalls: false,
sliceDetectThin: "off",
sliceMinHeight: 0,
sliceAdaptive: false,
zHopDistance: 0.2,

View file

@ -97,6 +97,11 @@ const LISTS = {
{ name: "2" },
{ name: "3" }
],
thin: [
{ name: "off" },
{ name: "type 1" },
{ name: "type 2" }
]
};
// primary device mode

View file

@ -117,6 +117,15 @@ gapp.register("kiri.init", [], (root, exports) => {
filamentSourceEditUpdate();
}
function thinWallSave() {
let opt = ui.sliceDetectThin;
let level = opt.options[opt.selectedIndex];
if (level) {
settings().process.sliceDetectThin = level.value;
api.conf.save();
}
}
function detailSave() {
let level = ui.detail.options[ui.detail.selectedIndex];
if (level) {
@ -1742,8 +1751,8 @@ gapp.register("kiri.init", [], (root, exports) => {
sliceSolidLayers: uc.newInput(LANG.sl_lsld_s, {title:LANG.sl_lsld_l, convert:uc.toInt, modes:FDM}),
sliceBottomLayers: uc.newInput(LANG.sl_lbot_s, {title:LANG.sl_lbot_l, convert:uc.toInt, modes:FDM}),
fdmSep: uc.newBlank({class:"pop-sep", modes:FDM}),
sliceDetectThin: uc.newSelect(LANG.ad_thin_s, {title: LANG.ad_thin_l, action: thinWallSave}, "thin"),
sliceAdaptive: uc.newBoolean(LANG.ad_adap_s, onBooleanClick, {title: LANG.ad_adap_l, modes:FDM, trigger: true}),
detectThinWalls: uc.newBoolean(LANG.ad_thin_s, onBooleanClick, {title: LANG.ad_thin_l, modes:FDM}),
laserOffset: uc.newInput(LANG.ls_offs_s, {title:LANG.ls_offs_l, convert:uc.toFloat, modes:LASER}),
laserSliceHeight: uc.newInput(LANG.ls_lahi_s, {title:LANG.ls_lahi_l, convert:uc.toFloat, modes:LASER, trigger: true}),

View file

@ -318,7 +318,7 @@ self.kiri.lang['en-us'] = {
sl_ordr_l: ["output shell order","inside to outside","or outside to inside","affects surface quality"],
sl_strt_s: "layer start",
sl_strt_l: ["layer starting point","last = last layer end","center = part center","origin = device origin"],
ad_thin_s: "thin walls",
ad_thin_s: "thin wall",
ad_thin_l: ["detect and fill gaps","between shell walls"],
// FDM FILL