diff --git a/js/kiri-conf.js b/js/kiri-conf.js index 432ed5bc..7f291d9a 100644 --- a/js/kiri-conf.js +++ b/js/kiri-conf.js @@ -298,14 +298,9 @@ let gs_kiri_conf = exports; p:{ processName: "default", slaSlice: 0.05, - slaSupportEnable: false, - slaSupportDensity: 0.5, - slaSupportSize: 2, - slaSupportArea: 1, - slaSupportSpan: 2, - slaSolidLayers: 5, - slaBottomLayers: 5, - slaTopLayers: 5 + slaBottoms: 5, + slaSolids: 5, + slaTops: 5 } }, cam:{ diff --git a/js/kiri-driver-laser.js b/js/kiri-driver-laser.js index ccab6ca9..564c682f 100644 --- a/js/kiri-driver-laser.js +++ b/js/kiri-driver-laser.js @@ -40,7 +40,9 @@ let gs_kiri_laser = exports; return ondone("invalid slice height"); } - SLICER.sliceWidget(widget, {height: proc.laserSliceHeight, single: proc.laserSliceSingle}, function(slices) { + SLICER.sliceWidget(widget, { + height: proc.laserSliceHeight, single: proc.laserSliceSingle + }, function(slices) { widget.slices = slices; slices.forEach(function(slice, index) { slice.doShells(1, -proc.laserOffset); diff --git a/js/kiri-driver-sla.js b/js/kiri-driver-sla.js index 44e98646..2ae67a45 100644 --- a/js/kiri-driver-sla.js +++ b/js/kiri-driver-sla.js @@ -36,33 +36,48 @@ let gs_kiri_sla = exports; let process = settings.process, device = settings.device; - SLICER.sliceWidget(widget, { height: sliceHeight }, onSliceDone, onSliceUpdate); - - function onSliceUpdate(update) { - return onupdate(0.0 + update * 0.5); + // calculate % complete and call onupdate() + function doupdate(slices, index, from, to, msg) { + onupdate(0.5 + (from + ((index / slices.length) * (to - from))) * 0.5, msg); } - function onSliceDone(slices) { + // for each slice, performe a function and call doupdate() + function forSlices(slices, from, to, fn, msg) { + slices.forEach(function(slice) { + fn(slice); + doupdate(slices, slice.index, from, to, msg) + }); + } + + SLICER.sliceWidget(widget, { + height: process.slaSlice || 0.05 + }, function(slices) { widget.slices = slices; - - if (!slices) return; - - // calculate % complete and call onupdate() - function doupdate(index, from, to, msg) { - onupdate(0.5 + (from + ((index/slices.length) * (to-from))) * 0.5, msg); - } - - // for each slice, performe a function and call doupdate() - function forSlices(from, to, fn, msg) { - slices.forEach(function(slice) { - fn(slice); - doupdate(slice.index, from, to, msg) - }); - } - - // report slicing complete + // reset for solids and support projections + slices.forEach(function(slice) { + slice.invalidateFill(); + slice.invalidateSolids(); + slice.invalidateSupports(); + slice.isSolidFill = false; + }); + forSlices(slices, 0.0, 0.2, (slice) => { + slice.doShells(1, 0); + // slice.tops.forEach(top => { top.solids = [] }); + }, "slice"); + forSlices(slices, 0.2, 0.4, (slice) => { + slice.doDiff(0.00001, 0.005); + }, "delta"); + forSlices(slices, 0.4, 0.5, (slice) => { + slice.projectFlats(process.slaSolids || 5); + slice.projectBridges(process.slaSolids || 5); + }, "project"); + forSlices(slices, 0.5, 0.6, (slice) => { + slice.doSolidsFill(undefined, undefined, 0.00001); + }, "solid"); ondone(); - } + }, function(update) { + return onupdate(0.0 + update * 0.5); + }); }; /** diff --git a/js/kiri-init.js b/js/kiri-init.js index 192bb964..e9db5d7d 100644 --- a/js/kiri-init.js +++ b/js/kiri-init.js @@ -32,12 +32,15 @@ var gs_kiri_init = exports; DEG = Math.PI/180, ALL = [MODES.FDM, MODES.LASER, MODES.CAM, MODES.SLA], CAM = [MODES.CAM], + SLA = [MODES.SLA], FDM = [MODES.FDM], + FDM_SLA = [MODES.FDM,MODES.SLA], FDM_CAM = [MODES.CAM,MODES.FDM], FDM_LASER = [MODES.LASER,MODES.FDM], + FDM_LASER_SLA = [MODES.LASER,MODES.FDM,MODES.SLA], CAM_LASER = [MODES.LASER,MODES.CAM], - LASER = [MODES.LASER], GCODE = [MODES.FDM, MODES.LASER, MODES.CAM], + LASER = [MODES.LASER], CATALOG = API.catalog, platform = API.platform, selection = API.selection, @@ -1271,14 +1274,14 @@ var gs_kiri_init = exports; layers: UC.setGroup($("layers")), layerOutline: UC.newBoolean(LANG.la_olin, onLayerToggle, {modes:LOCAL ? ALL : FDM_LASER}), - layerTrace: UC.newBoolean(LANG.la_trce, onLayerToggle, {modes:FDM_LASER}), + layerTrace: UC.newBoolean(LANG.la_trce, onLayerToggle, {modes:FDM_LASER_SLA}), layerFacing: UC.newBoolean(LANG.la_face, onLayerToggle, {modes:CAM}), layerRough: UC.newBoolean(LANG.la_ruff, onLayerToggle, {modes:CAM}), layerFinish: UC.newBoolean(LANG.la_fini, onLayerToggle, {modes:CAM}), layerFinishX: UC.newBoolean(LANG.la_finx, onLayerToggle, {modes:CAM}), layerFinishY: UC.newBoolean(LANG.la_finy, onLayerToggle, {modes:CAM}), - layerDelta: UC.newBoolean(LANG.la_dlta, onLayerToggle, {modes:FDM}), - layerSolid: UC.newBoolean(LANG.la_slds, onLayerToggle, {modes:FDM}), + layerDelta: UC.newBoolean(LANG.la_dlta, onLayerToggle, {modes:FDM_SLA}), + layerSolid: UC.newBoolean(LANG.la_slds, onLayerToggle, {modes:FDM_SLA}), layerFill: UC.newBoolean(LANG.la_fill, onLayerToggle, {modes:FDM}), layerSparse: UC.newBoolean(LANG.la_sprs, onLayerToggle, {modes:FDM}), layerSupport: UC.newBoolean(LANG.la_sprt, onLayerToggle, {modes:FDM}), @@ -1382,7 +1385,7 @@ var gs_kiri_init = exports; outputRaftSpacing: UC.newInput(LANG.fr_spac_s, {title:LANG.fr_spac_l, convert:UC.toFloat, bound:UC.bound(0.0,3.0), modes:FDM}), outputRaft: UC.newBoolean(LANG.enable, onBooleanClick, {title:LANG.fr_nabl_l, modes:FDM}), - output: UC.newGroup(LANG.ou_menu), + output: UC.newGroup(LANG.ou_menu, null, {modes:GCODE}), outputTileSpacing: UC.newInput(LANG.ou_spac_c, {title:LANG.ou_spac_l, convert:UC.toInt, modes:LASER}), outputTileScaling: UC.newInput(LANG.ou_scal_s, {title:LANG.ou_scal_l, convert:UC.toInt, bound:UC.bound(0.1,100), modes:LASER}), outputLaserPower: UC.newInput(LANG.ou_powr_s, {title:LANG.ou_powr_l, convert:UC.toInt, bound:UC.bound(1,100), modes:LASER}), @@ -1429,7 +1432,14 @@ var gs_kiri_init = exports; antiBacklash: UC.newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, bound:UC.bound(0,3), convert:UC.toInt, modes:FDM, expert:true}), // detectThinWalls: UC.newBoolean("thin wall fill", onBooleanClick, {title: "detect and fill thin openings\nbetween shells walls", modes:FDM, expert:true}) gcodePauseLayers: UC.newInput(LANG.ag_paws_s, {title:LANG.ag_paws_l, modes:FDM, expert:true}), - outputLayerRetract: UC.newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l, modes:FDM, expert:true}) + outputLayerRetract: UC.newBoolean(LANG.ad_lret_s, onBooleanClick, {title:LANG.ad_lret_l, modes:FDM, expert:true}), + + // SLA + slaProc: UC.newGroup('slaproc', null, {modes:SLA}), + slaSlice: UC.newInput('slice height', {title:'slice height', convert:UC.toFloat, modes:SLA}), + slaBottoms: UC.newInput('bottom layers', {title:'bottom layers', convert:UC.toInt, modes:SLA}), + slaSolids: UC.newInput('solid layers', {title:'solid layers', convert:UC.toInt, modes:SLA}), + slaTops: UC.newInput('top layers', {title:'bottom layers', convert:UC.toInt, modes:SLA}), }); if (!LOCAL) { @@ -1548,27 +1558,6 @@ var gs_kiri_init = exports; UI.layers.style.left = ev.target.getBoundingClientRect().left + 'px'; }; - // $('rot_x').onkeypress = (ev) => { - // if (ev.key === 'Enter') { - // API.selection.rotate(parseFloat(ev.target.value)*DEG,0,0); - // ev.target.value = 0; - // } - // }; - // - // $('rot_y').onkeypress = (ev) => { - // if (ev.key === 'Enter') { - // API.selection.rotate(0,parseFloat(ev.target.value)*DEG,0); - // ev.target.value = 0; - // } - // }; - // - // $('rot_z').onkeypress = (ev) => { - // if (ev.key === 'Enter') { - // API.selection.rotate(0,0,parseFloat(ev.target.value)*DEG); - // ev.target.value = 0; - // } - // }; - UI.modelOpacity.onchange = UI.modelOpacity.onclick = function(ev) { API.widgets.opacity(parseInt(UI.modelOpacity.value)/100); }; diff --git a/js/kiri-slice.js b/js/kiri-slice.js index 5835a2b6..c9d79083 100644 --- a/js/kiri-slice.js +++ b/js/kiri-slice.js @@ -2,14 +2,14 @@ "use strict"; -var gs_kiri_slice = exports; +let gs_kiri_slice = exports; (function() { if (!self.kiri) self.kiri = {}; if (self.kiri.Slice) return; - var KIRI = self.kiri, + let KIRI = self.kiri, FILL = KIRI.fill, BASE = self.base, UTIL = BASE.util, @@ -108,7 +108,7 @@ var gs_kiri_slice = exports; * the outline shell that the fill touches. */ Top.prototype.innerTraces = function() { - var traces = this.traces, + let traces = this.traces, array = []; if (traces) traces.forEach(function(p) { if (p.inner) array.appendAll(p.inner); @@ -117,7 +117,7 @@ var gs_kiri_slice = exports; }; Top.prototype.clone = function(deep) { - var top = new Top(this.poly.clone(deep)); + let top = new Top(this.poly.clone(deep)); return top; }; @@ -142,7 +142,7 @@ var gs_kiri_slice = exports; * returns a cloned slice the option of a deep clone on the top polys */ PRO.clone = function(deep) { - var from = this, + let from = this, slice = newSlice(from.z, from.view); from.tops.forEach(function(top) { slice.addTop(top.poly.clone(deep)); @@ -203,7 +203,7 @@ var gs_kiri_slice = exports; * @param {Polygon} poly to merge into a top */ PRO.mergeTop = function(poly) { - var scope = this, + let scope = this, tops = scope.tops, union, i; for (i=0; i 2 ? valid : []; @@ -425,11 +425,11 @@ var gs_kiri_slice = exports; * @param {Obejct} options */ PRO.doShells = function(count, offset1, offsetN, fillOffset, options) { - var slice = this, + let slice = this, opt = options || {}; slice.tops.forEach(function(top) { - var top_poly = [ top.poly ]; + let top_poly = [ top.poly ]; if (slice.index === 0) { // console.log({slice_top_0: top_poly, count}); @@ -442,7 +442,7 @@ var gs_kiri_slice = exports; // top.thinner = []; top.traces = []; top.inner = []; - var last = [], + let last = [], z = top.poly.getZ(); if (opt.thin) { @@ -456,7 +456,7 @@ var gs_kiri_slice = exports; top.traces = last; } else { if (opt.thin) { - var on1s2 = offset1 * 2, + let on1s2 = offset1 * 2, on2s2 = offsetN * 2; POLY.expand2( top_poly, @@ -480,14 +480,14 @@ var gs_kiri_slice = exports; function(p1, p2, diff, dist) { if (p2) { // nth offset - var pall = POLY.nest(POLY.flatten([].appendAll(p1).appendAll(p2)).clone()), + 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 - var pall = POLY.nest(POLY.flatten([].appendAll(p1).appendAll(p2)).clone()), + 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), @@ -537,7 +537,7 @@ var gs_kiri_slice = exports; * @param {number} renderMode */ PRO.renderShells = function(renderMode) { - var scope = this, + let scope = this, layers = scope.layers, layer = layers.trace, process = KIRI.driver.CAM.process; @@ -633,7 +633,7 @@ var gs_kiri_slice = exports; * Runs in client. Generate solid lines in the correct view layer. */ PRO.renderSolidFill = function() { - var layer = this.layers.fill, + let layer = this.layers.fill, render; layer.clear(); @@ -661,7 +661,7 @@ var gs_kiri_slice = exports; this.isSparseFill = false; if (this.tops.length === 0 || density === 0.0 || this.isSolidFill) return; - var scope = this, + let scope = this, tops = scope.tops, down = scope.down, clib = self.ClipperLib, @@ -752,7 +752,7 @@ var gs_kiri_slice = exports; * Runs in client. Generate sparse lines in the correct view layer. */ PRO.renderSparseFill = function() { - var layer = this.layers.sparse; + let layer = this.layers.sparse; layer.clear(); @@ -771,18 +771,19 @@ var gs_kiri_slice = exports; /** * Find difference between fill inset poly on two adjacent layers. * Used to calculate bridges, flats and then solid projections. + * 'expand' is used for top offsets in SLA mode */ - PRO.doDiff = function(minArea) { + PRO.doDiff = function(minArea, expand) { if (!this.down) return; - var top = this, + let top = this, bottom = this.down; top.bridges = null; bottom.flats = null; - var topInner = top.gatherInner([]), - bottomInner = bottom.gatherInner([]), + let topInner = expand ? top.gatherTopPolys([]) : top.gatherInner([]), + bottomInner = expand ? bottom.gatherTopPolys([]) : bottom.gatherInner([]), bridges = [], flats = []; @@ -795,15 +796,22 @@ var gs_kiri_slice = exports; POLY.subtract(topInner, bottomInner, bridges, flats, this.z, minArea); - top.bridges = bridges; - bottom.flats = flats; + if (expand) { + top.bridges = []; + bottom.flats = []; + POLY.expand(bridges, expand, top.z, top.bridges, 1); + POLY.expand(flats, expand, top.z, bottom.flats, 1); + } else { + top.bridges = bridges; + bottom.flats = flats; + } }; /** * Runs in client. Generate polygon lines in the correct view layer. */ PRO.renderDiff = function() { - var scope = this, + let scope = this, layers = scope.layers, bridgeLayer = layers.bridge, flatLayer = layers.flat, @@ -858,18 +866,19 @@ var gs_kiri_slice = exports; */ PRO.doSolidsFill = function(spacing, angle, minArea) { - var minarea = minArea || 1, + let minarea = minArea || 1, scope = this, tops = scope.tops, solids = scope.solids, - unioned = POLY.union(solids.poly); + unioned = POLY.union(solids.poly), + isSLA = (spacing === undefined && angle === undefined); if (solids.length === 0) return false; if (unioned.length === 0) return false; - var masks, + let masks, trims = [], - inner = scope.gatherInner([]); + inner = isSLA ? scope.gatherTopPolys([]) : scope.gatherInner([]); // trim each solid to the inner bounds unioned.forEach(function(p) { @@ -897,7 +906,7 @@ var gs_kiri_slice = exports; if (top.poly.overlaps(solid)) { if (!solid.parent || solid.parent.area() > top.poly.area()) { if (solid.areaDeep() < minarea) { - // console.log({cull_solid:solid,area:solid.areaDeep()}); + // console.log({i:scope.index,cull_solid:solid,area:solid.areaDeep()}); return; } solid.parent = top.poly; @@ -907,10 +916,13 @@ var gs_kiri_slice = exports; }); }); + // for SLA to bypass line infill + if (isSLA) return true; + // create empty filled line array for each top tops.forEach(function(top) { top.fill_lines = top.thin_fill || []; - var tofill = [], + let tofill = [], angfill = []; trims.forEach(function(solid) { if (solid.parent === top.poly) { @@ -957,7 +969,7 @@ var gs_kiri_slice = exports; }; PRO.renderSolidOutlines = function() { - var layer = this.layers.solid, + let layer = this.layers.solid, trimmed = this.solids.trimmed; layer.clear(); @@ -980,7 +992,7 @@ var gs_kiri_slice = exports; * @param {number} gap layers between supports and part */ PRO.doSupport = function(minOffset, maxBridge, expand, minArea, pillarSize, offset, gap) { - var min = minArea || 0.1, + let min = minArea || 0.1, size = (pillarSize || 2), slice = this, mergeDist = size * 3, // pillar merge dist @@ -996,7 +1008,7 @@ var gs_kiri_slice = exports; // skip support detection for bottom layer if (!slice.down) return; - var traces = POLY.flatten(slice.gatherTraces([])), + let traces = POLY.flatten(slice.gatherTraces([])), fill = slice.gatherFillLines([]), points = [], down = slice.down, @@ -1006,10 +1018,10 @@ var gs_kiri_slice = exports; // check if point is supported by layer below function checkSupported(point) { // skip points close to other support points - for (var i=0; i= maxBridge) { - var slope = p1.slopeTo(p2).factor(1/dist), + let slope = p1.slopeTo(p2).factor(1/dist), segs = Math.floor(dist / maxBridge) + 1, seglen = dist / segs; while (i < segs) { @@ -1039,7 +1051,7 @@ var gs_kiri_slice = exports; trace.forEachSegment(function(p1, p2) { checkLine(p1, p2, true) }); }); - var supports = []; + let supports = []; // add offset solids to supports (or fill depending) fill.forEachPair(function(p1,p2) { checkLine(p1, p2, false) }); @@ -1048,7 +1060,7 @@ var gs_kiri_slice = exports; // skip the rest if no points or supports if (!(points.length || supports.length)) return; - var pillars = []; + let pillars = []; // TODO project points down instead of unioned pillars // TODO merge point/rect into hull of next nearest (up to maxBridge/2 away) @@ -1072,11 +1084,11 @@ var gs_kiri_slice = exports; // constrain support poly to top polys supports = POLY.trimTo(supports, trimTo); - var depth = 0; + let depth = 0; while (down && supports.length > 0) { down.supports = down.supports || []; - var trimmed = [], + let trimmed = [], culled = []; // clip supports to shell offsets @@ -1109,7 +1121,7 @@ var gs_kiri_slice = exports; */ PRO.doSupportFill = function(linewidth, density, minArea) { // return; - var slice = this, + let slice = this, supports = slice.supports, nsB = [], nsC = [], @@ -1135,7 +1147,7 @@ var gs_kiri_slice = exports; if (supports) supports.forEach(function (poly) { // angle based on width/height ratio - var angle = (poly.bounds.width() / poly.bounds.height() > 1) ? 90 : 0, + let angle = (poly.bounds.width() / poly.bounds.height() > 1) ? 90 : 0, // calculate fill density spacing = linewidth * (1 / density), offsets = []; @@ -1154,7 +1166,7 @@ var gs_kiri_slice = exports; * */ PRO.renderSupport = function() { - var slice = this, + let slice = this, layer = slice.layers.support, supports = slice.supports; @@ -1176,7 +1188,7 @@ var gs_kiri_slice = exports; * @return {Object} */ PRO.findClosestPointTo = function(target) { - var min, find; + let min, find; this.tops.forEach(function(top) { find = top.poly.findClosestPointTo(target); @@ -1201,15 +1213,15 @@ var gs_kiri_slice = exports; * @returns {?Point} */ function lineCrossesPoly(p1, p2, poly, minDist2) { - var ip; + let ip; if (Array.isArray(poly)) { - for (var i=0; i