diff --git a/src/dev/fdm/Creality.CR-30 b/src/dev/fdm/Creality.CR-30 index d2f8d687..a080edbd 100644 --- a/src/dev/fdm/Creality.CR-30 +++ b/src/dev/fdm/Creality.CR-30 @@ -53,7 +53,7 @@ "origin_center": false, "extrude_abs": true, "bed_belt": true, - "bed_width": 215, + "bed_width": 220, "bed_depth": 300, "build_height": 170 }, diff --git a/src/geo/base.js b/src/geo/base.js index 04b7dca8..0c0bfcff 100644 --- a/src/geo/base.js +++ b/src/geo/base.js @@ -15,7 +15,7 @@ function time() { return Date.now() } - function lerp(from, to, maxInc) { + function lerp(from, to, maxInc, incFrom) { let dir = Math.sign(to - from); let delta = Math.abs(to - from); let steps = Math.floor(delta / maxInc); @@ -25,7 +25,7 @@ steps++; per = delta / steps; } - let out = []; + let out = incFrom ? [from] : []; while (steps-- > 0) { from += per * dir; out.push(from); diff --git a/src/geo/polygon.js b/src/geo/polygon.js index 3235eb34..465c9a91 100644 --- a/src/geo/polygon.js +++ b/src/geo/polygon.js @@ -260,6 +260,182 @@ return Math.abs(diff); } + // generate center crossing point cloud + PRO.centers = function(step, z, min, max, opt = {}) { + let cloud = [], + bounds = this.bounds, + points = this.points, + length = points.length, + lines = opt.lines || false; + + for (let y of UTIL.lerp(bounds.miny + 0.01, bounds.maxy - 0.01, step, true)) { + let ints = []; + for (let i=0; i y) || + (p1.y > y && p2.y <= y) + ) ints.push([p1,p2]); + } + let cntr = []; + if (ints.length && ints.length % 2 === 0) { + for (let int of ints) { + let [p1, p2] = int; + if (p2.y < p1.y) { + let tp = p1; + p1 = p2; + p2 = tp; + } + let minx = Math.min(p1.x, p2.x); + let maxx = Math.max(p1.x, p2.x); + let miny = Math.min(p1.y, p2.y); + let maxy = Math.max(p1.y, p2.y); + let dx = p2.x - p1.x; + let dy = maxy - miny; + let pct = (y - miny) / dy; + let xpo = p1.x + pct * dx; + cntr.push(xpo); + } + } + cntr.sort((a, b) => { + return b - a; + }); + let lp, eo = 0; + for (let x of cntr) { + let p = BASE.newPoint(x, y, z); + if (eo++ % 2) { + let d = lp.distTo2D(p); + if (d >= min && d <= max) { + if (lines) { + cloud.push(lp); + cloud.push(p); + } else { + cloud.push(BASE.newPoint( + (lp.x + p.x) / 2, y, z + )); + } + } + } else { + lp = p; + } + } + } + + for (let x of UTIL.lerp(bounds.minx + 0.01, bounds.maxx - 0.01, step, true)) { + let ints = []; + for (let i=0; i x) || + (p1.x > x && p2.x <= x) + ) ints.push([p1,p2]); + } + let cntr = []; + if (ints.length && ints.length % 2 === 0) { + for (let int of ints) { + let [p1, p2] = int; + if (p2.x < p1.x) { + let tp = p1; + p1 = p2; + p2 = tp; + } + let minx = Math.min(p1.x, p2.x); + let maxx = Math.max(p1.x, p2.x); + let miny = Math.min(p1.y, p2.y); + let maxy = Math.max(p1.y, p2.y); + let dx = maxx - minx; + let dy = p2.y - p1.y; + let pct = (x - minx) / dx; + let ypo = p1.y + pct * dy; + cntr.push(ypo); + } + } + cntr.sort((a, b) => { + return b - a; + }); + let lp, eo = 0; + for (let y of cntr) { + let p = BASE.newPoint(x, y, z); + if (eo++ % 2) { + let d = lp.distTo2D(p); + if (d >= min && d <= max) { + if (lines) { + cloud.push(lp); + cloud.push(p); + } else { + cloud.push(BASE.newPoint( + x, (lp.y + p.y) / 2, z + )); + } + } + } else { + lp = p; + } + } + } + + if (lines) { + return cloud; + } + + let mindist = opt.mindist || step * 1.5; + + function build(poly) { + let lastp = poly.last(); + let minp; + let mind = Infinity; + for (let point of cloud) { + let dist = point.distTo2D(lastp); + if (dist < mindist && dist < mind) { + mind = dist; + minp = point; + } + } + if (minp) { + cloud = cloud.filter(p => p !== minp); + poly.push(minp); + return true; + } + return false; + } + + // join points into polys + let polys = []; + let poly = []; + while (cloud.length) { + if (poly.length === 0) { + poly = [ cloud.shift() ]; + polys.push(poly); + continue; + } + if (build(poly)) { + continue; + } + if (!poly.flip) { + poly.reverse(); + poly.flip = true; + continue; + } + if (poly.length) { + poly = []; + } else { + throw "whoop there it is"; + } + } + + return polys + .filter(poly => poly.length > 1) + .map(poly => { + let np = BASE.newPolygon().setOpen(); + for (let p of poly) { + np.push(p); + } + return np; + }); + }; + PRO.debur = function(dist) { if (this.len < 2) { return null; diff --git a/src/geo/polygons.js b/src/geo/polygons.js index 0413565a..4e710b0b 100644 --- a/src/geo/polygons.js +++ b/src/geo/polygons.js @@ -541,6 +541,11 @@ return opts.flat ? opts.outs : polys; } + /** + * progressive insetting that does inset + outset to debur as well + * as performing subtractive analysis between initial layer shell (ref) + * and last offset (cmp) to produce gap candidates (for thinfill) + */ function inset(polys, dist, count, z) { let total = count; let layers = []; diff --git a/src/kiri/init.js b/src/kiri/init.js index 58566445..2a0cdaec 100644 --- a/src/kiri/init.js +++ b/src/kiri/init.js @@ -2201,7 +2201,6 @@ SPACE.mouse.up((event, int) => { if (event.button === 2 && API.view.isArrange()) { - console.log({up: event, int}); let style = UI.context.style; style.display = 'flex'; style.left = `${event.clientX-3}px`; diff --git a/src/kiri/main.js b/src/kiri/main.js index eec825b6..7b3f9dc8 100644 --- a/src/kiri/main.js +++ b/src/kiri/main.js @@ -1002,14 +1002,15 @@ KIRI.client.sync(); KIRI.client.rotate(settings); + let segtimes = {}, + segNumber = 0, + errored = false, + startTime = Date.now(), + lastMsg; + // for each widget, slice forAllWidgets(function(widget) { - let segtimes = {}, - segNumber = 0, - errored = false, - startTime = Date.now(), - lastMsg, - camOrLaser = mode === 'CAM' || mode === 'LASER', + let camOrLaser = mode === 'CAM' || mode === 'LASER', stack = widget.stack = STACKS.create(widget.id, widget.mesh), factor = (widget.getVertices().count / defvert); @@ -1022,7 +1023,7 @@ if (sliced) { // update segment time if (lastMsg) { - segtimes[`${segNumber++}_${lastMsg}`] = mark - startTime; + segtimes[`${widget.id}_${segNumber++}_${lastMsg}`] = mark - startTime; } API.event.emit('slice', getMode()); } @@ -1048,7 +1049,7 @@ KIRI.client.unrotate(settings, () => { forAllWidgets(widget => { // on done - segtimes[`${segNumber++}_draw`] = widget.render(widget.stack); + segtimes[`${widget.id}_${segNumber++}_draw`] = widget.render(widget.stack); // rotate stack for belt beds if (widget.rotinfo) { widget.stack.obj.rotate(widget.rotinfo); @@ -1086,7 +1087,7 @@ if (msg && msg !== lastMsg) { let mark = Date.now(); if (lastMsg) { - segtimes[`${segNumber++}_${lastMsg}`] = mark - startTime; + segtimes[`${widget.id}_${segNumber++}_${lastMsg}`] = mark - startTime; } lastMsg = msg; startTime = mark; diff --git a/src/kiri/print.js b/src/kiri/print.js index b55057ad..b7b15056 100644 --- a/src/kiri/print.js +++ b/src/kiri/print.js @@ -604,10 +604,11 @@ last = null; } } + } else { + outputOrderClosest(poly, function(next) { + outputTraces(next, opt); + }, null); } - // outputOrderClosest(poly, function(next) { - // outputTraces(next, opt); - // }, null); } else { let finishShell = poly.depth === 0 && !firstLayer; startPoint = scope.polyPrintPath(poly, startPoint, preout, { @@ -887,20 +888,12 @@ let inner = next.innerShells() || []; // output inner polygons - if (shellOrder === 1) outputTraces(inner); + if (shellOrder === 1) outputTraces(inner, { sort: shellOrder }); outputTraces(next.shells, { sort: shellOrder }); - // sort perimeter polygon by length to go out-to-in or in-to-out - // this causes too much seeking when nesting splits into disconnected areas - // (next.shells || []).sort(function(a,b) { - // return a.perimeter() > b.perimeter() ? shellOrder : -shellOrder; - // }).forEach(function(poly, index) { - // outputTraces(poly); - // }); - // output outer polygons - if (shellOrder === -1) outputTraces(inner); + if (shellOrder === -1) outputTraces(inner, { sort: shellOrder }); // output thin fill outputFills(next.thin_fill, {near: true}); diff --git a/src/mode/fdm/slice.js b/src/mode/fdm/slice.js index b7b12bfc..29182c3d 100644 --- a/src/mode/fdm/slice.js +++ b/src/mode/fdm/slice.js @@ -21,7 +21,8 @@ shell: { check: 0x0077bb, face: 0x0077bb, line: 0x0077bb, opacity: 1 }, fill: { check: 0x00bb77, face: 0x00bb77, line: 0x00bb77, opacity: 1 }, infill: { check: 0x3322bb, face: 0x3322bb, line: 0x3322bb, opacity: 1 }, - support: { check: 0xaa5533, face: 0xaa5533, line: 0xaa5533, opacity: 1 } + support: { check: 0xaa5533, face: 0xaa5533, line: 0xaa5533, opacity: 1 }, + gaps: { check: 0xaa3366, face: 0xaa3366, line: 0xaa3366, opacity: 1 } }, PROTO = Object.clone(COLOR), bwcomp = (1 / Math.cos(Math.PI/4)), @@ -463,6 +464,10 @@ .setLayer("shells", isSynth ? COLOR.support : COLOR.shell) .addPolys(top.shells, vopt({ offset, height })); + if (top.gaps) output + .setLayer("gaps", COLOR.gaps) + .addPolys(top.gaps, vopt({ offset, height, thin: true })); + // if (isThin && debug) { // slice.output() // .setLayer('offset', { face: 0, line: 0x888888 }) @@ -573,19 +578,19 @@ } }); if (opt.danger && opt.thin) { top.thin_fill = []; + top.fill_sparse = []; let layers = POLY.inset(top_poly, offsetN, count, z); last = layers.last().mid; top.shells = layers.map(r => r.mid).flat(); - layers.map(r => r.gap).forEach((polys, i) => { - let off = offset1; - polys = POLY.offset(polys, -off * 0.8, {z, minArea: 0}); - if (polys.length) { - top.thin_fill.appendAll(cullIntersections( - fillArea(polys, 45, off, [], off/4, off*4), - fillArea(polys, 135, off, [], off/4, off*4), - )); - } - }); + top.gaps = layers.map(r => r.gap).flat(); + let off = offsetN; + let min = off * 0.9; + 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.thin_fill.appendAll(centers); + } } else if (opt.thin) { top.thin_fill = []; let oso = {z, count, gaps: [], outs: [], minArea: 0.05};