complete refactor of thin wall detection

This commit is contained in:
Stewart Allen 2020-11-01 21:11:26 -05:00
commit de46926d4c
8 changed files with 197 additions and 165 deletions

View file

@ -122,6 +122,7 @@
inner: encode(this.inner, state),
// thinner: encode(this.thinner, state),
solids: encode(this.solids, state),
thin_fill: encodePointArray(this.thin_fill, state),
fill_lines: encodePointArray(this.fill_lines, state),
fill_sparse: encode(this.fill_sparse, state),
polish: encode(this.polish, state)
@ -135,6 +136,7 @@
top.inner = decode(v.inner, state);
// top.thinner = decode(v.thinner, state);
top.solids = decode(v.solids, state);
top.thin_fill = decodePointArray(v.thin_fill, state);
top.fill_lines = decodePointArray(v.fill_lines, state);
top.fill_sparse = decode(v.fill_sparse, state);
top.polish = decode(v.polish, state);

View file

@ -1513,7 +1513,7 @@
antiBacklash: UC.newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, bound:UC.bound(0,3), convert:UC.toInt, modes:FDM, expert:true}),
sliceRotation: LOCAL ? UC.newInput(LANG.ad_slrt_s, {title:LANG.ad_slrt_l, bound:UC.bound(-45,45), convert:UC.toFloat, modes:FDM, expert:true}) : null,
gcodePauseLayers: UC.newInput(LANG.ag_paws_s, {title:LANG.ag_paws_l, modes:FDM, expert:true, comma:true}),
detectThinWalls: LOCAL ? UC.newBoolean(LANG.ad_thin_s, onBooleanClick, {title: LANG.ad_thin_l, modes:FDM, expert:true}) : null,
detectThinWalls: UC.newBoolean(LANG.ad_thin_s, onBooleanClick, {title: LANG.ad_thin_l, modes:FDM, expert:true}),
outputLayerRetract: UC.newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l, modes:FDM, expert:true}),
// SLA

View file

@ -670,29 +670,63 @@
}
function outputFills(lines, options) {
if (!lines || lines.length === 0) {
return;
}
let p, p1, p2, dist, len, found, group, mindist, t1, t2,
marked = 0,
start = 0,
skip = false,
lastIndex = -1,
opt = options || {},
near = opt.near || false,
fast = opt.fast || false,
fill = opt.fill >= 0 ? opt.fill : fillMult;
fill = opt.fill >= 0 ? opt.fill : fillMult,
thinDist = near ? thinWall * 3 : thinWall;
while (lines && marked < lines.length) {
found = false;
group = null;
found = false;
mindist = Infinity;
// use next nearest line strategy
if (near)
for (i=0; i<lines.length; i += 2) {
t1 = lines[i];
if (t1.del) {
continue;
}
t2 = lines[i+1];
let d1 = t1.distToSq2D(startPoint);
let d2 = t2.distToSq2D(startPoint);
if (d1 < mindist || d2 < mindist) {
if (d2 < d1) {
p2 = t1;
p1 = t2;
} else {
p1 = t1;
p2 = t2;
}
mindist = Math.min(d1, d2);
lastIndex = i;
}
}
// use next index line strategy
// order all points by distance to last point
if (!near)
for (i=start; i<lines.length; i += 2) {
p = lines[i];
if (p.del) continue;
if (p.del) {
continue;
}
if (group === null && p.index > lastIndex) {
group = p.index;
}
if (group !== null) {
if (p.index !== group) break;
if (p.index !== group) {
break;
}
if (p.index % 2 === 0) {
t1 = lines[i];
t2 = lines[i+1];
@ -712,7 +746,7 @@
}
// go back to start and try again
if (!found) {
if (!near && !found) {
if (start === 0 && lastIndex === -1) {
console.log('infinite loop', lines, {
marked, options, i, group, start, lastIndex,
@ -725,11 +759,11 @@
continue;
}
dist = startPoint.distTo2D(p1);
len = p1.distTo2D(p2);
dist = startPoint.distToSq2D(p1);
len = p1.distToSq2D(p2);
// go back to start when dist > retractDist
if (!fast && !skip && dist > retractDist) {
if (!near && !fast && !skip && dist > retractDist) {
skip = true;
start = 0;
lastIndex = -1;
@ -747,7 +781,7 @@
// and segment length is less than thinWall then
// just extrude to midpoint of next segment. this is
// to avoid shaking the printer to death.
if (dist <= thinWall && len <= thinWall) {
if (dist <= thinDist && len <= thinDist) {
p2 = p1.midPointTo(p2);
addOutput(preout, p2, fill * (dist / thinWall), fillSpeed, extruder);
} else {
@ -762,7 +796,7 @@
}
// bridge ends of fill when they're close together
if (dist < thinWall) {
if (dist < thinDist) {
addOutput(preout, p1, fill, fillSpeed, extruder);
} else {
addOutput(preout, p1, 0, moveSpeed, extruder);
@ -854,10 +888,13 @@
outputTraces(poly);
});
// output inner polygons
// output outer polygons
if (dir === -1)
outputTraces([].appendAll(next.innerTraces() || []));
// output thin fill
outputFills(next.thin_fill, {near: true});
// then output solid and sparse fill
outputFills(next.fill_lines);
outputSparse(next.fill_sparse, sparseMult);

View file

@ -296,28 +296,53 @@
};
/**
* given two arrays of points (lines), eliminate intersections of the second
* to the first, then return a unified array.
* given an array of arrays of points (lines), eliminate intersections
* between groups, then return a unified array of shortest non-intersects.
*
* @param {Point[]} r1
* @param {Point[]} r2
* @returns {Point[]}
*/
function cullIntersections(r1, r2) {
if (!(r1 && r2 && r1.length && r2.length)) return;
let valid = r2.slice();
outer: for (let i=0; i<r1.length; i += 2) {
for (let j=0; j<r2.length; j += 2) {
if (UTIL.intersect(r1[i], r1[i+1], r2[j], r2[j+1], BASE.key.SEGINT)) continue outer;
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])});
}
valid.push(r1[i]);
valid.push(r1[i+1]);
return lns;
}
// add index to point for fill order storeSettingsToServer
for (let k=0; k<valid.length; k++) {
valid[k].index = Infinity;
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));
}
return valid.length > 2 ? valid : [];
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 : [];
}
/**
@ -353,10 +378,6 @@
top.inner = [];
let last = [], z = top.poly.getZ();
if (opt.thin) {
top.thin_fill = [];
}
if (count) {
// permit offset of 0 for laser and drag knife
if (offset1 === 0 && count === 1) {
@ -364,51 +385,29 @@
top.traces = last;
} else {
if (opt.thin) {
// thin wall offset strategy
let on1s2 = offset1 * 2,
on2s2 = offsetN * 2;
POLY.expand2(
top_poly, // reference polygon(s)
-offset1, // first inset distance
-offsetN, // subsequent inset distnace
top.traces, // accumulator array
count, // numberof insets to perform
// on each new offset trace ...
function(polys, countNow) {
last = polys;
// mark each poly with depth (offset #) starting at 0
polys.forEach(function(p) {
p.depth = count - countNow;
if (p.inner) p.inner.forEach(function(pi) {
// use negative offset for inners
pi.depth = -(count - countNow);
});
});
},
// thin wall probe
// p1 = original polys
// p2 = collected polys
// diff = circularity difference
// dist = over-expansion distance
function(p1, p2, diff, dist) {
if (p2) {
// nth offset
let pall = POLY.nest(POLY.flatten([].appendAll(p1).appendAll(p2)).clone()),
pnew1 = POLY.expand(pall, -dist, z, null, 1),
r1 = fillArea(pnew1, 45, offsetN, [], dist / 2, on2s2),
r2 = fillArea(pnew1, 135, offsetN, [], dist / 2, on2s2),
rall = top.thin_fill.appendAll(cullIntersections(r1, r2));
} else {
// first offset
let pall = POLY.nest(POLY.flatten([].appendAll(p1).appendAll(p2)).clone()),
pnew1 = POLY.expand(pall, -dist, z, null, 1),
r1 = fillArea(pnew1, 45, offsetN, [], 0, on1s2),
r2 = fillArea(pnew1, 135, offsetN, [], 0, on1s2),
rall = top.thin_fill.appendAll(cullIntersections(r1, r2));
}
// top.thinner.appendAll(pnew1).appendAll(pnew2);
},
z);
top.thin_fill = [];
let oso = {z, count, gaps: [], outs: [], minArea: 0.05};
POLY.offset(top_poly, [-offset1, -offsetN], oso);
oso.outs.forEach((polys, i) => {
polys.forEach(p => {
p.depth = i;
top.traces.push(p);
});
});
// 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});
// polys.forEach(p => { slice.solids.trimmed.push(p); });
top.thin_fill.appendAll(cullIntersections(
fillArea(polys, 45, off/2, [], 0.01, off*2),
fillArea(polys, 135, off/2, [], 0.01, off*2),
// fillArea(polys, 90, off, [], 0.05, off*4),
// fillArea(polys, 180, off, [], 0.05, off*4),
));
});
} else {
// standard wall offsetting strategy
POLY.expand(
@ -796,7 +795,7 @@
// create empty filled line array for each top
tops.forEach(function(top) {
top.fill_lines = top.thin_fill || [];
top.fill_lines = [];
let tofill = [],
angfill = [];
trims.forEach(function(solid) {
@ -825,24 +824,6 @@
return true;
};
/**
* fill thin areas, if present
* @return {boolean} true if filled, false if not
*/
PRO.doThinFill = function(spacing, angle) {
this.tops.forEach(function(top) {
if (top.thin_fill && top.thin_fill.length > 0) {
if (top.fill_lines) {
top.fill_lines.appendAll(top.thin_fill)
} else {
top.fill_lines = top.thin_fill;
}
}
});
return true;
};
/**
* calculate external overhangs requiring support
* this is done bottom-up