diff --git a/src/kiri/minion.js b/src/kiri/minion.js index fd265f27..06945325 100644 --- a/src/kiri/minion.js +++ b/src/kiri/minion.js @@ -6,7 +6,11 @@ let BASE = self.base, KIRI = self.kiri, UTIL = BASE.util, POLY = BASE.polygons, - CODEC = KIRI.codec; + CODEC = KIRI.codec, + clib = self.ClipperLib, + ctyp = clib.ClipType, + ptyp = clib.PolyType, + cfil = clib.PolyFillType; // catch clipper alerts and convert to console messages self.alert = function(o) { @@ -56,6 +60,23 @@ const funcs = { reply({ fill: arr }, [ arr.buffer ]); }, + clip: data => { + let clip = new clib.Clipper(); + let ctre = new clib.PolyTree(); + let clips = []; + + clip.AddPaths(data.lines, ptyp.ptSubject, false); + clip.AddPaths(data.polys, ptyp.ptClip, true); + + if (clip.Execute(ctyp.ctIntersection, ctre, cfil.pftNonZero, cfil.pftEvenOdd)) { + for (let node of ctre.m_AllPolys) { + clips.push(CODEC.encode(POLY.fromClipperNode(node, data.z))); + } + } + + reply({ clips }); + }, + bad: data => { reply({error: "invalid command"}); } diff --git a/src/kiri/worker.js b/src/kiri/worker.js index 359a097e..57a1180a 100644 --- a/src/kiri/worker.js +++ b/src/kiri/worker.js @@ -34,10 +34,13 @@ if (concurrent) { // for concurrent operations const minwork = KIRI.minions = { + concurrent, + union: function(polys, minarea) { return new Promise((resolve, reject) => { if (concurrent === 0 || polys.length * 2 < concurrent) { resolve(POLY.union(polys, 0, true)); + return; } let polyper = Math.ceil(polys.length / concurrent); let running = 0; @@ -64,6 +67,7 @@ KIRI.minions = { return new Promise((resolve, reject) => { if (concurrent === 0) { resolve(POLY.fillArea(polys, angle, spacing, [], minLen, maxLen)); + return; } minwork.queue({ cmd: "fill", @@ -83,6 +87,30 @@ KIRI.minions = { }); }, + clip: function(slice, polys, lines) { + return new Promise((resolve, reject) => { + if (concurrent === 0) { + reject("concurrent clip unavaiable"); + } + minwork.queue({ + cmd: "clip", + polys: POLY.toClipper(polys), + lines: lines.map(a => a.map(p => p.toClipper())), + z: slice.z + }, data => { + let polys = KIRI.codec.decode(data.clips); + for (let top of slice.tops) { + for (let poly of polys) { + if (poly.isInside(top.poly)) { + top.fill_sparse.push(poly); + } + } + } + resolve(polys); + }); + }); + }, + queue: function(work, ondone) { minionq.push({work, ondone}); minwork.kick(); diff --git a/src/mode/fdm/slice.js b/src/mode/fdm/slice.js index 3bca6b7c..41bbbea8 100644 --- a/src/mode/fdm/slice.js +++ b/src/mode/fdm/slice.js @@ -82,7 +82,8 @@ fillOffset = lineWidth * fillOffsetMult, sliceFillAngle = spro.sliceFillAngle, supportDensity = spro.sliceSupportDensity, - beltfact = Math.cos(Math.PI/4); + beltfact = Math.cos(Math.PI/4), + doConcurrent = ctrl.danger && KIRI.minions.concurrent; isFlat = ctrl.lineType === "flat"; isThin = !isFlat && ctrl.lineType === "line"; @@ -263,7 +264,7 @@ slice.solids = []; }); - let promises = ctrl.danger ? [] : undefined; + let promises = doConcurrent ? [] : undefined; // create shells and diff inner fillable areas forSlices(0.0, 0.15, slice => { let params = slice.params = getRangeParameters(settings, slice.index); @@ -298,10 +299,8 @@ danger: ctrl.danger }, promises); }, "shells"); - if (promises) { await Promise.all(promises); - // console.log('resolved', promises.length); } // just the top/bottom special solid layers @@ -429,23 +428,28 @@ projectFlats(slice, solidLayers); projectBridges(slice, solidLayers); }, "project solids"); + let promises = doConcurrent ? [] : undefined; forSlices(0.35, 0.5, slice => { - let params = slice.params;// getRangeParameters(settings, slice.index); + let params = slice.params; let first = slice.index === 0; let solidWidth = params.sliceFillWidth || 1; let spaceMult = first ? params.firstLayerLineMult || 1 : 1; let fillSpace = fillSpacing * spaceMult * solidWidth; - doSolidsFill(slice, fillSpace, sliceFillAngle, minSolid); + doSolidsFill(slice, fillSpace, sliceFillAngle, minSolid, promises); sliceFillAngle += 90.0; }, "fill solids"); - await fillResolve(); + if (promises) { + console.log({fill_awaiting: promises.length}); + await Promise.all(promises); + } } // sparse layers only present when non-vase mose and sparse % > 0 if (!isSynth) { let lastType; + let promises = doConcurrent ? [] : undefined; forSlices(0.5, 0.7, slice => { - let params = slice.params;// getRangeParameters(settings, slice.index); + let params = slice.params; if (vaseMode || !params.sliceFillSparse) { return; } @@ -460,13 +464,18 @@ bounds: widget.getBoundingBox(), height: sliceHeight, type: newType, - cache: params._range !== true && lastType === newType + cache: params._range !== true && lastType === newType, + promises }); lastType = newType; }, "infill"); + if (promises) { + console.log({infill_awaiting: promises.length}); + await Promise.all(promises); + } } else if (isSynth) { forSlices(0.5, 0.7, slice => { - let params = slice.params;// getRangeParameters(settings, slice.index); + let params = slice.params; let density = params.sliceSupportDensity; if (density) for (let top of slice.tops) { @@ -491,7 +500,9 @@ } let mark = Date.now(); // shadow = POLY.union(alltops, 0.1, true); - shadow = await KIRI.minions.union(alltops, 0.1); + shadow = doConcurrent ? + await KIRI.minions.union(alltops, 0.1) : + POLY.union(alltops, 0.1, true); // console.log({unioned_in: Date.now() - mark}); if (spro.sliceSupportExtra) { shadow = POLY.offset(shadow, spro.sliceSupportExtra); @@ -511,7 +522,7 @@ // render if not explicitly disabled if (render) { forSlices(0.9, 1.0, slice => { - let params = slice.params;// getRangeParameters(settings, slice.index); + let params = slice.params; doRender(slice, isSynth, params, ctrl.devel); }, "render"); } @@ -803,7 +814,7 @@ * Take output from pluggable sparse infill algorithm and clip to * the bounds of the top polygons and their inner solid areas. */ - function doSparseLayerFill(slice, options) { + function doSparseLayerFill(slice, options = {}) { let process = options.process, spacing = options.spacing, // spacing space between fill lines density = options.density, // density of infill 0.0 - 1.0 @@ -950,6 +961,11 @@ return; } + if (options.promises) { + options.promises.push(KIRI.minions.clip(slice, polys, lines)); + return; + } + lines = lines.map(a => a.map(p => p.toClipper())); clip.AddPaths(lines, ptyp.ptSubject, false); clip.AddPaths(POLY.toClipper(polys), ptyp.ptClip, true); @@ -1026,7 +1042,7 @@ * fill projected areas and store line data * @return {boolean} true if filled, false if not */ - function doSolidsFill(slice, spacing, angle, minArea) { + function doSolidsFill(slice, spacing, angle, minArea, fillQ) { let minarea = minArea || 1, tops = slice.tops, @@ -1103,33 +1119,28 @@ } }); if (tofill.length > 0) { - fillAsync(tofill, angle, spacing, newfill); + doFillArea(fillQ, tofill, angle, spacing, newfill); top.fill_lines_norm = {angle:angle,spacing:spacing}; } if (angfill.length > 0) { top.fill_lines_ang = {spacing:spacing,list:[],poly:[]}; angfill.forEach(function(af) { - fillAsync([af], af.fillang.angle + 45, spacing, newfill); + doFillArea(fillQ, [af], af.fillang.angle + 45, spacing, newfill); top.fill_lines_ang.list.push(af.fillang.angle + 45); top.fill_lines_ang.poly.push(af.clone()); }); } - // top.fill_lines.appendAll(newfill); }); return true; }; - let fillQ = []; - - function fillAsync(polys, angle, spacing, output, minLen, maxLen) { - fillQ.push(KIRI.minions.fill(polys, angle, spacing, output, minLen, maxLen)); - } - - async function fillResolve() { - // console.log({resolving: fillQ.length}); - await Promise.all(fillQ); - fillQ.length = 0; + function doFillArea(fillQ, polys, angle, spacing, output, minLen, maxLen) { + if (fillQ) { + fillQ.push(KIRI.minions.fill(polys, angle, spacing, output, minLen, maxLen)); + } else { + POLY.fillArea(polys, angle, spacing, output, minLen, maxLen); + } } /**