From 07213ddc790748f326c91098dcb81fc4b994c510 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Sun, 21 Sep 2025 16:24:33 -0400 Subject: [PATCH] annotate thin shells for output ordering --- src/geo/polygon.js | 8 ++++++ src/kiri/core/slice.js | 1 + src/kiri/mode/fdm/post.js | 51 ++++++++++++++++++++++++++---------- src/kiri/mode/fdm/prepare.js | 11 +++++--- src/kiri/mode/fdm/slice.js | 10 +++++-- 5 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/geo/polygon.js b/src/geo/polygon.js index e5a3d9a6..f81ebbb6 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -1104,6 +1104,14 @@ export class Polygon { return this.first().isEqual(this.last()); } + closeIf(dist = 1) { + if (this.first().distTo2D(this.last()) <= dist) { + return this.setClosed(); + } else { + return this.setOpen(); + } + } + fixClosed() { if (this.appearsClosed()) { this.points.pop(); diff --git a/src/kiri/core/slice.js b/src/kiri/core/slice.js index 6982d2cf..d684d046 100644 --- a/src/kiri/core/slice.js +++ b/src/kiri/core/slice.js @@ -146,6 +146,7 @@ class Slice { let top = new Top(data.poly); top.thin_fill = data.thin_fill?.map(p => newPoint(p.x,p.y,p.z)); top.thin_wall = data.thin_wall; + top.thin_sort = data.thin_sort; top.fill_lines = data.fill_lines; top.fill_sparse = data.fill_sparse; top.fill_off = data.fill_off; diff --git a/src/kiri/mode/fdm/post.js b/src/kiri/mode/fdm/post.js index 742df723..40d3ebcc 100644 --- a/src/kiri/mode/fdm/post.js +++ b/src/kiri/mode/fdm/post.js @@ -177,7 +177,7 @@ function thin_type_3(params) { let debugExtrusion = false; let shells = top.shells; // only used for debug - let trace = top.thin_wall = []; + let traces = top.thin_wall = []; let midR = offsetN * 0.5; // nominal extrusion width let minR = midR * 0.75; // smallest interior fill or single wall width @@ -199,11 +199,11 @@ function thin_type_3(params) { i === 0 ? midR : midR * 1.25, i === 0 ? maxR : maxF, { - shell: 0, + shell: i + 1, lines, polys, remain, - trace, + traces, brute: false, showMedialAxis: false, showChainIntersect: false @@ -228,7 +228,12 @@ function thin_type_3(params) { if (lines.length) top.thin_fill = lines; if (polys.length) shells.push(...polys); - return { trace, last: remain }; + // create shell depth annotation that will survive serialization + top.thin_sort = traces.map(a => a.shell); + + console.log({ this: (self.kiri_minion?.name ?? self.kiri_worker), traces }); + + return { traces, last: remain }; } // trace a single extrusion line around the inside of the noodle poly @@ -243,7 +248,7 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) { lines = [], polys = [], remain = [], - trace = [], + traces = [], shell = 0 } = opt; @@ -447,7 +452,9 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) { if (poly.parent) { inner.push(nupoly); } - let fpo; + let ltpo; + let trace = []; + trace.shell = sno; poly.segment(minR).forEachSegment((p1, p2) => { let np1 = { x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2 }; let len = pointDist(p1, p2); @@ -489,7 +496,7 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) { // let rad_step = ((pop[0] * sstep) | 0) / sstep; // for single wall place it exactly on medial axis nupoly.push(new Point(min.x, min.y)); - trace.push(op = { x: min.x, y: min.y, r: mr, sno }) + trace.push(op = { x: min.x, y: min.y, r: mr }) } else { // radius rounded to nearest step let rad_step = ((pop[0] * sstep) | 0) / sstep; @@ -499,8 +506,7 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) { trace.push(op = { x: np1.x + dy * off, y: np1.y - dx * off, - r: rad_step, - sno + r: rad_step }) // trace inset by full diameter nupoly.push(new Point(np1.x + dy * off * 2, np1.y - dx * off * 2)); @@ -508,13 +514,29 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) { if (odd && !mp1.claimed) { mp1.claimed = true; } - // store first trace out for this poly to repeat at the end - fpo = fpo ?? trace.peek(); + // detect trace jump and start new trace + if (ltpo && pointDist(ltpo, trace.peek()) > maxR) { + // start new trace removing jump point + // and adding that to the new trace + traces.push(trace); + let nutrace = [ trace.pop() ]; + nutrace.shell = sno; + // close last trace if endpoints near enough + if (pointDist(ltpo, trace[0] <= maxR)) { + trace.push(trace[0]); + } + trace = nutrace; + } + // update last trace point out + ltpo = trace.peek(); } }); // repeat first point of trace (if close enough to be closed) - if (fpo && pointDist(fpo, trace.peek()) <= maxR) { - trace.push(fpo); + if (trace[0] && pointDist(trace[0], trace.peek()) <= maxR) { + trace.push(trace[0]); + } + if (trace.length) { + traces.push(trace); } } @@ -534,10 +556,11 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) { return p.clean().simplify(); }).flat() ).filter(p => p.area() >= minArea); + remain.push(...ret); } - return { lines, polys, remain, trace }; + return { lines, polys, remain, traces }; } /** diff --git a/src/kiri/mode/fdm/prepare.js b/src/kiri/mode/fdm/prepare.js index 4069e7a0..03f5f6cd 100644 --- a/src/kiri/mode/fdm/prepare.js +++ b/src/kiri/mode/fdm/prepare.js @@ -1089,9 +1089,14 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) { } } - function outputAdaptiveWalls(thin, opt = {}) { + function outputAdaptiveWalls(thin, sort, opt = {}) { + // restore thin shell order annotation and sort on it + thin.forEach((a,i) => a.shell = sort[i]); + thin.sort((a,b) => (a.shell - b.shell) * shellOrder); + // perform nearest poly to poly algorithm on shells let np; - for (let pt of thin) { + for (let trace of thin) + for (let pt of trace) { let { lastPoint, lastEmit } = print; let { x, y, r } = pt; np = new Point(x, y, z); @@ -1459,7 +1464,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) { if (top.traces) outputTraces(top.traces); // thin wall v3 - if (top.thin_wall) outputAdaptiveWalls(top.thin_wall); + if (top.thin_wall) outputAdaptiveWalls(top.thin_wall, top.thin_sort); // innermost shells let inner = next.innerShells() || []; diff --git a/src/kiri/mode/fdm/slice.js b/src/kiri/mode/fdm/slice.js index 62c5d7ca..291dcf24 100644 --- a/src/kiri/mode/fdm/slice.js +++ b/src/kiri/mode/fdm/slice.js @@ -1003,11 +1003,17 @@ function doRender(slice, isSynth, params, opt = {}) { if (isThin && top.thin_wall?.length) output .setLayer("walls", Color.thin) - .addPolys(top.thin_wall.map(p => newPolygon().centerCircle(p, p.r, 12).setZ(slice.z)), vopt({ offset, height })); + .addPolys(top.thin_wall.map(a => a.map(p => newPolygon().centerCircle(p, p.r, 12).setZ(slice.z))).flat(), vopt({ offset, height })); if (!isThin && top.thin_wall?.length) output .setLayer("walls", Color.thin) - .addPolys(top.thin_wall.map(p => newPolygon().centerCircle(p, p.r, 3).setZ(slice.z)), vopt({ offset, height })); + .addPolys(top.thin_wall.map(a => + a.length === 1 ? + newPolygon().centerCircle(a[0], a[0].r, 6) : + newPolygon(a.map(p => newPoint(p.x, p.y))) + .setZ(slice.z) + .closeIf(height) + ), vopt({ offset, height })); if (top.fill_lines?.length) output .setLayer("solid fill", isSynth ? Color.support : Color.fill)