From 54d92ad5e83de4147cc561f2ff94ab65416ff28f Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Thu, 5 Nov 2020 15:14:20 -0500 Subject: [PATCH] checkpoint cam printSetup/prepare refactor --- app.js | 1 + src/mode/cam/driver.js | 586 +--------------------------------------- src/mode/cam/prepare.js | 572 +++++++++++++++++++++++++++++++++++++++ src/mode/cam/topo.js | 1 - 4 files changed, 574 insertions(+), 586 deletions(-) create mode 100644 src/mode/cam/prepare.js diff --git a/app.js b/app.js index 056e17fd..9f5ac835 100644 --- a/app.js +++ b/app.js @@ -307,6 +307,7 @@ const script = { "mode/cam/driver", "mode/cam/tool", "mode/cam/topo", + "mode/cam/prepare", "mode/laser/driver", "kiri/widget", "kiri/print", diff --git a/src/mode/cam/driver.js b/src/mode/cam/driver.js index b5bbfff5..ee13c01e 100644 --- a/src/mode/cam/driver.js +++ b/src/mode/cam/driver.js @@ -15,7 +15,7 @@ init, slice, sliceRender, - printSetup, + // printSetup, // src/mode/cam/prepare.js printExport, printRender }, @@ -63,77 +63,6 @@ }); } - /** - * return tool Z clearance height for a line segment movement path - */ - function getZClearPath(terrain, x1, y1, x2, y2, z, zadd, off, over) { - let maxz = z; - let check = []; - for (let i=0; i p.intersections(p1, p2, true)).flat(); - if (int.length) { - maxz = Math.max(maxz, data.z + zadd + over); - continue; - } - - let s1 = p1.slopeTo(p2).toUnit().normal(); - let s2 = p2.slopeTo(p1).toUnit().normal(); - let pa = p1.projectOnSlope(s1, off); - let pb = p2.projectOnSlope(s1, off); - int = data.tops.map(p => p.intersections(pa, pb, true)).flat(); - if (int.length) { - maxz = Math.max(maxz, data.z + zadd + over); - continue; - } - pa = p1.projectOnSlope(s2, off); - pb = p2.projectOnSlope(s2, off); - int = data.tops.map(p => p.intersections(pa, pb, true)).flat(); - if (int.length) { - maxz = Math.max(maxz, data.z + zadd + over); - continue; - } - } - return maxz; - } - - /** - * call out to slicer - */ - function doSlicing(widget, options, ondone, onupdate) { - SLICER.sliceWidget(widget, options, ondone, onupdate); - } - - /** - * @param {Slice[]} slices - * @param {number} z position - * - * return slice closest to specified z - */ - function closestSliceToZ(slices, z) { - let selected = null, - distance = Infinity, - nextdist; - slices.forEach(function(slice) { - nextdist = Math.abs(slice.z - z); - if (nextdist < distance) { - selected = slice; - distance = nextdist; - } - }); - return selected; - } - /** * Find top paths to trace when using ball and taper mills * in waterline outlining and tracing modes. @@ -735,519 +664,6 @@ * @param {Number} [index] into widget array * @param {Object} [firstPoint] starting point */ - function printSetup(print, update, index, firstPoint) { - let widgetIndex = index || 0, - widgetArray = print.widgets, - widgetCount = widgetArray.length, - widget = widgetArray[widgetIndex]; - - if (widgetIndex >= widgetCount || !widget) return; - - let settings = print.settings, - device = settings.device, - process = settings.process, - stock = settings.stock, - outer = settings.bounds, - outerz = outer.max.z, - slices = widget.slices, - bounds = widget.getCamBounds(settings), - boundsz = bounds.max.z, - hasStock = process.camStockOffset || (process.camStockZ && process.camStockX && process.camStockY), - startCenter = process.outputOriginCenter, - alignTop = settings.controller.alignTop, - zclear = (process.camZClearance || 1), - zmax_outer = hasStock ? stock.z + zclear : outerz + zclear, - ztOff = process.camZTopOffset, - zadd = hasStock ? stock.z - boundsz : alignTop ? outerz - boundsz : 0, - zmax = outerz + zclear, - wmpos = widget.mesh.position, - wmx = wmpos.x, - wmy = wmpos.y, - originx = startCenter ? 0 : hasStock ? -stock.x / 2 : bounds.min.x, - originy = startCenter ? 0 : hasStock ? -stock.y / 2 : bounds.min.y, - origin = newPoint(originx + wmx, originy + wmy, zmax), - output = print.output, - modes = CPRO, - easeDown = process.camEaseDown, - depthFirst = process.camDepthFirst, - tolerance = process.camTolerance, - drillDown = process.camDrillDown, - drillLift = process.camDrillLift, - drillDwell = process.camDrillDwell, - newOutput = widgetIndex === 0 ? [] : print.output, - layerOut = [], - printPoint, - isNewMode, - tool, - toolDiam, - toolDiamMove, - feedRate, - plungeRate, - lastTool, - lastMode, - lastPoint, - nextIsMove = true, - spindle = 0, - spindleMax = device.spindleMax, - addOutput = print.addOutput, - tip2tipEmit = print.tip2tipEmit, - poly2polyEmit = print.poly2polyEmit, - poly2polyDepthFirstEmit = print.poly2polyDepthFirstEmit, - maxToolDiam = widget.maxToolDiam, - terrain = widget.terrain.map(data => { - return { - z: data.z, - tops: data.tops, - }; - }); - - function newLayer() { - if (layerOut.length < 2) { - return; - } - newOutput.push(layerOut); - layerOut = []; - layerOut.spindle = spindle; - } - - function setTool(toolID, feed, plunge) { - if (toolID !== lastTool) { - tool = new CAM.Tool(settings, toolID); - toolDiam = tool.fluteDiameter(); - toolDiamMove = toolDiam; // TODO validate w/ multiple models - lastTool = toolID; - } - feedRate = feed; - plungeRate = plunge; - } - - function emitDrills(polys) { - polys = polys.slice(); - for (;;) { - let closestDist = Infinity, - closestI, - closest = null, - dist; - - for (let i=0; i down * 2) { - points.push(point.clone()); - point.z -= down; - remain -= down; - } else if (remain < down) { - points.push(point.clone()); - point.z -= remain; - points.push(point.clone()); - break; - } else { - points.push(point.clone()); - point.z -= remain / 2; - points.push(point.clone()); - point.z -= remain / 2; - points.push(point.clone()); - break; - } - } - points.forEach(function(point, index) { - camOut(point, 1); - if (index > 0 && index < points.length - 1) { - if (dwell) camDwell(dwell); - if (lift) camOut(point.clone().setZ(point.z + lift), 0); - } - }) - camOut(point.clone().setZ(zmax)); - newLayer(); - } - - /** - * @param {Point} point - * @param {number} emit (0=move, !0=filament emit/laser on/cut mode) - * @param {number} [speed] speed - * @param {number} [tool] tool - */ - function layerPush(point, emit, speed, tool) { - layerOut.mode = lastMode; - addOutput(layerOut, point, emit, speed, tool); - } - - function camDwell(time) { - layerPush( - null, - 0, - time, - tool.getNumber() - ); - } - - function camOut(point, cut) { - point = point.clone(); - point.x += wmx; - point.y += wmy; - point.z += zadd; - - if (nextIsMove) { - cut = 0; - nextIsMove = false; - } - let rate = feedRate; - - if (!lastPoint) { - let above = point.clone().setZ(zmax + zadd); - // before first point, move cutting head to point above it - layerPush(above, 0, 0, tool.getNumber()); - // then set that as the lastPoint - lastPoint = above; - } - - let deltaXY = lastPoint.distTo2D(point), - deltaZ = point.z - lastPoint.z, - absDeltaZ = Math.abs(deltaZ), - isMove = !cut; - // drop points too close together - if (deltaXY < 0.001 && point.z === lastPoint.z) { - console.trace(["drop dup",lastPoint,point]); - return; - } - if (isMove && deltaXY <= toolDiamMove) { - // convert short planar moves to cuts - if (absDeltaZ <= tolerance) { - cut = 1; - isMove = false; - } else if (deltaZ <= -tolerance) { - // move over before descending - layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool.getNumber()); - // new pos for plunge calc - deltaXY = 0; - } - } //else (TODO verify no else here b/c above could change isMove) - // move over things - if ((deltaXY > toolDiam || (deltaZ > toolDiam && deltaXY > tolerance)) && (isMove || absDeltaZ >= tolerance)) { - let maxz = getZClearPath( - terrain, - lastPoint.x,// - wmx, - lastPoint.y,// - wmy, - point.x,// - wmx, - point.y,// - wmy, - Math.max(point.z, lastPoint.z), - zadd, - maxToolDiam/2, - zclear - ) + ztOff, - mustGoUp = Math.max(maxz - point.z, maxz - lastPoint.z) >= tolerance, - clearz = maxz; - // up if any point between higher than start/outline - if (mustGoUp) { - clearz = maxz + zclear; - layerPush(lastPoint.clone().setZ(clearz), 0, 0, tool.getNumber()); - } - // over to point above where we descend to - if (mustGoUp || point.z < maxz) { - layerPush(point.clone().setZ(clearz), 0, 0, tool.getNumber()); - // new pos for plunge calc - deltaXY = 0; - } - } - // synth new plunge rate - if (deltaZ <= -tolerance) { - let threshold = Math.min(deltaXY / 2, absDeltaZ), - modifier = threshold / absDeltaZ; - if (threshold && modifier && deltaXY > tolerance) { - // use modifier to speed up long XY move plunge rates - rate = Math.round(plungeRate + ((feedRate - plungeRate) * modifier)); - } else { - rate = plungeRate; - } - // console.log({deltaZ: deltaZ, deltaXY: deltaXY, threshold:threshold, modifier:modifier, rate:rate, plungeRate:plungeRate}); - } - - // todo synthesize move speed from feed / plunge accordingly - layerPush( - point, - cut ? 1 : 0, - rate, - tool.getNumber() - ); - lastPoint = point; - layerOut.spindle = spindle; - } - - // coming from a previous widget, use previous last point - lastPoint = firstPoint; - - // make top start offset configurable - printPoint = firstPoint || origin; - - // accumulated data for depth-first optimiztions - let depthData = { - rough: [], - outline: [], - roughDiam: 0, - outlineDiam: 0, - contourx: [], - contoury: [], - layer: 0, - drill: [] - }; - - // todo first move into positon - slices.forEach(function(slice, sliceIndex) { - depthData.layer++; - isNewMode = slice.camMode != lastMode; - lastMode = slice.camMode; - nextIsMove = true; - if (isNewMode) depthData.layer = 0; - - switch (slice.camMode) { - case modes.LEVEL: - setTool(process.camRoughTool, process.camRoughSpeed, 0); - spindle = Math.min(spindleMax, process.camRoughSpindle); - slice.tops.forEach(function(top) { - if (!top.traces) return; - let polys = []; - top.traces.forEach(function (poly) { - polys.push(poly); - if (poly.inner) { - poly.inner.forEach(function(inner) { - polys.push(inner); - }) - } - }); - // set winding specified in output - POLY.setWinding(polys, process.camConventional, false); - printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) { - poly.forEachPoint(function(point, pidx, points, offset) { - camOut(point.clone(), offset !== 0); - }, true, index); - }); - newLayer(); - }); - break; - case modes.ROUGH: - case modes.OUTLINE: - let dir = process.camConventional; - if (slice.camMode === modes.ROUGH) { - setTool(process.camRoughTool, process.camRoughSpeed, process.camRoughPlunge); - spindle = Math.min(spindleMax, process.camRoughSpindle); - depthData.roughDiam = toolDiam; - } else { - setTool(process.camOutlineTool, process.camOutlineSpeed, process.camOutlinePlunge); - spindle = Math.min(spindleMax, process.camOutlineSpindle); - depthData.outlineDiam = toolDiam; - if (!process.camOutlinePocket) { - dir = !dir; - } - } - // todo find closest next trace/trace-point - slice.tops.forEach(function(top) { - if (!top.poly) return; - if (!top.traces) return; - let polys = [], t = [], c = []; - POLY.flatten(top.traces, top.inner || []).forEach(function (poly) { - let child = poly.parent; - if (depthFirst) poly = poly.clone(true); - if (child) c.push(poly); else t.push(poly); - poly.layer = depthData.layer; - polys.push(poly); - }); - // set cut direction on outer polys - POLY.setWinding(t, dir); - // set cut direction on inner polys - POLY.setWinding(c, !dir); - if (depthFirst) { - (slice.camMode === modes.ROUGH ? depthData.rough : depthData.outline).append(polys); - } else { - printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) { - poly.forEachPoint(function(point, pidx, points, offset) { - camOut(point.clone(), offset !== 0); - }, poly.isClosed(), index); - }); - newLayer(); - } - }); - break; - case modes.CONTOUR_X: - case modes.CONTOUR_Y: - if (isNewMode || !printPoint) { - // force start at lower left corner - printPoint = newPoint(bounds.min.x,bounds.min.y,zmax); - } - setTool(process.camContourTool, process.camContourSpeed, process.camFastFeedZ); - spindle = Math.min(spindleMax, process.camContourSpindle); - depthData.outlineDiam = toolDiam; - // todo find closest next trace/trace-point - slice.tops.forEach(function(top) { - if (!top.traces) return; - let polys = [], poly, emit; - top.traces.forEach(function (poly) { - if (depthFirst) poly = poly.clone(true); - polys.push({first:poly.first(), last:poly.last(), poly:poly}); - }); - if (depthFirst) { - (slice.camMode === modes.CONTOUR_X ? depthData.contourx : depthData.contoury).appendAll(polys); - } else { - printPoint = tip2tipEmit(polys, printPoint, function(el, point, count) { - poly = el.poly; - if (poly.last() === point) poly.reverse(); - poly.forEachPoint(function(point, pidx) { - camOut(point.clone(), pidx > 0); - }, false); - return lastPoint; - }); - newLayer(); - } - }); - break; - case modes.DRILL: - setTool(process.camDrillTool, process.camDrillDownSpeed, process.camDrillDownSpeed); - // drilling is always depth-first - slice.tops.forEach(function(top) { - if (!top.traces) return; - depthData.drill.appendAll(top.traces); - }); - break; - } - update(sliceIndex / slices.length); - }); - - // act on accumulated layer data - if (depthFirst) { - // roughing depth first - if (depthData.rough.length > 0) { - lastMode = CPRO.ROUGH; - setTool(process.camRoughTool, process.camRoughSpeed, process.camRoughPlunge); - spindle = Math.min(spindleMax, process.camRoughSpindle); - printPoint = poly2polyDepthFirstEmit(depthData.rough, printPoint, function(poly, index, count, fromPoint) { - let last = null; - if (easeDown && poly.isClosed()) { - last = poly.forEachPointEaseDown(function(point, offset) { - camOut(point.clone(), offset > 0); - }, fromPoint); - } else { - poly.forEachPoint(function(point, pidx, points, offset) { - camOut(point.clone(), offset !== 0); - }, poly.isClosed(), index); - } - newLayer(); - return last; - }, depthData.roughDiam * process.camRoughOver * 1.01); - } - // outline depth first - if (depthData.outline.length > 0) { - lastMode = CPRO.OUTLINE; - setTool(process.camOutlineTool, process.camOutlineSpeed, process.camOutlinePlunge); - spindle = Math.min(spindleMax, process.camOutlineSpindle); - printPoint = poly2polyDepthFirstEmit(depthData.outline, printPoint, function(poly, index, count, fromPoint) { - let last = null; - if (easeDown && poly.isClosed()) { - last = poly.forEachPointEaseDown(function(point, offset) { - camOut(point.clone(), offset > 0); - }, fromPoint); - } else { - poly.forEachPoint(function(point, pidx, points, offset) { - camOut(point.clone(), offset !== 0); - last = point; - }, poly.isClosed(), index); - } - newLayer(); - return last; - }, depthData.outlineDiam * 0.01); - } - // two modes for deferred outlining: x then y or combined - if (process.camContourCurves) { - lastMode = CPRO.CONTOUR_X; - setTool(process.camContourTool, process.camContourSpeed, process.camContourPlunge); - spindle = Math.min(spindleMax, process.camContourSpindle); - // combined deferred contour x and y outlining - let contourxy = [].appendAll(depthData.contourx).appendAll(depthData.contoury); - printPoint = tip2tipEmit(contourxy, printPoint, function(el, point, count) { - let poly = el.poly; - if (poly.last() === point) { - poly.reverse(); - } - poly.forEachPoint(function(point, pidx) { - camOut(point.clone(), pidx > 0); - }, false); - newLayer(); - return lastPoint; - }); - } else { - setTool(process.camContourTool, process.camContourSpeed, process.camContourPlunge); - spindle = Math.min(spindleMax, process.camContourSpindle); - // deferred contour x outlining - if (depthData.contourx.length > 0) { - lastMode = CPRO.CONTOUR_X; - // force start at lower left corner - // printPoint = newPoint(bounds.min.x,bounds.min.y,zmax); - printPoint = tip2tipEmit(depthData.contourx, printPoint, function(el, point, count) { - let poly = el.poly; - if (poly.last() === point) poly.reverse(); - poly.forEachPoint(function(point, pidx) { - camOut(point.clone(), pidx > 0); - }, false); - newLayer(); - return lastPoint; - }); - } - // deferred contour y outlining - if (depthData.contoury.length > 0) { - lastMode = CPRO.CONTOUR_Y; - // force start at lower left corner - printPoint = tip2tipEmit(depthData.contoury, printPoint, function(el, point, count) { - let poly = el.poly; - if (poly.last() === point) poly.reverse(); - poly.forEachPoint(function(point, pidx) { - camOut(point.clone(), pidx > 0); - }, false); - newLayer(); - return lastPoint; - }); - } - } - } - - // drilling is always depth first, and always output last (change?) - if (depthData.drill.length > 0) { - lastMode = CPRO.DRILL; - setTool(process.camDrillTool, process.camDrillDownSpeed, process.camDrillDownSpeed); - emitDrills(depthData.drill); - } - - // last layer/move is to zmax - // injected into the last layer generated - if (lastPoint) - addOutput(newOutput[newOutput.length-1], printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool.getNumber()); - - // replace output single flattened layer with all points - print.output = newOutput; - - if (widgetIndex + 1 < widgetCount) { - printSetup(print, update, widgetIndex + 1, printPoint); - } - }; /** * @returns {Array} gcode lines diff --git a/src/mode/cam/prepare.js b/src/mode/cam/prepare.js new file mode 100644 index 00000000..aaa712b4 --- /dev/null +++ b/src/mode/cam/prepare.js @@ -0,0 +1,572 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +"use strict"; + +(function() { + + let KIRI = self.kiri, + BASE = self.base, + POLY = BASE.polygons, + UTIL = BASE.util, + CAM = KIRI.driver.CAM, + PRO = CAM.process, + newPoint = BASE.newPoint; + + CAM.printSetup = function(print, update, index, firstPoint) { + let widgetIndex = index || 0, + widgetArray = print.widgets, + widgetCount = widgetArray.length, + widget = widgetArray[widgetIndex]; + + if (widgetIndex >= widgetCount || !widget) return; + + let settings = print.settings, + device = settings.device, + process = settings.process, + stock = settings.stock, + outer = settings.bounds, + outerz = outer.max.z, + slices = widget.slices, + bounds = widget.getCamBounds(settings), + boundsz = bounds.max.z, + hasStock = process.camStockOffset || (process.camStockZ && process.camStockX && process.camStockY), + startCenter = process.outputOriginCenter, + alignTop = settings.controller.alignTop, + zclear = (process.camZClearance || 1), + zmax_outer = hasStock ? stock.z + zclear : outerz + zclear, + ztOff = process.camZTopOffset, + zadd = hasStock ? stock.z - boundsz : alignTop ? outerz - boundsz : 0, + zmax = outerz + zclear, + wmpos = widget.mesh.position, + wmx = wmpos.x, + wmy = wmpos.y, + originx = startCenter ? 0 : hasStock ? -stock.x / 2 : bounds.min.x, + originy = startCenter ? 0 : hasStock ? -stock.y / 2 : bounds.min.y, + origin = newPoint(originx + wmx, originy + wmy, zmax), + output = print.output, + easeDown = process.camEaseDown, + depthFirst = process.camDepthFirst, + tolerance = process.camTolerance, + drillDown = process.camDrillDown, + drillLift = process.camDrillLift, + drillDwell = process.camDrillDwell, + newOutput = widgetIndex === 0 ? [] : print.output, + layerOut = [], + printPoint, + isNewMode, + tool, + toolDiam, + toolDiamMove, + feedRate, + plungeRate, + lastTool, + lastMode, + lastPoint, + nextIsMove = true, + spindle = 0, + spindleMax = device.spindleMax, + addOutput = print.addOutput, + tip2tipEmit = print.tip2tipEmit, + poly2polyEmit = print.poly2polyEmit, + poly2polyDepthFirstEmit = print.poly2polyDepthFirstEmit, + maxToolDiam = widget.maxToolDiam, + terrain = widget.terrain.map(data => { + return { + z: data.z, + tops: data.tops, + }; + }); + + function newLayer() { + if (layerOut.length < 2) { + return; + } + newOutput.push(layerOut); + layerOut = []; + layerOut.spindle = spindle; + } + + function setTool(toolID, feed, plunge) { + if (toolID !== lastTool) { + tool = new CAM.Tool(settings, toolID); + toolDiam = tool.fluteDiameter(); + toolDiamMove = toolDiam; // TODO validate w/ multiple models + lastTool = toolID; + } + feedRate = feed; + plungeRate = plunge; + } + + function emitDrills(polys) { + polys = polys.slice(); + for (;;) { + let closestDist = Infinity, + closestI, + closest = null, + dist; + + for (let i=0; i down * 2) { + points.push(point.clone()); + point.z -= down; + remain -= down; + } else if (remain < down) { + points.push(point.clone()); + point.z -= remain; + points.push(point.clone()); + break; + } else { + points.push(point.clone()); + point.z -= remain / 2; + points.push(point.clone()); + point.z -= remain / 2; + points.push(point.clone()); + break; + } + } + points.forEach(function(point, index) { + camOut(point, 1); + if (index > 0 && index < points.length - 1) { + if (dwell) camDwell(dwell); + if (lift) camOut(point.clone().setZ(point.z + lift), 0); + } + }) + camOut(point.clone().setZ(zmax)); + newLayer(); + } + + /** + * @param {Point} point + * @param {number} emit (0=move, !0=filament emit/laser on/cut mode) + * @param {number} [speed] speed + * @param {number} [tool] tool + */ + function layerPush(point, emit, speed, tool) { + layerOut.mode = lastMode; + addOutput(layerOut, point, emit, speed, tool); + } + + function camDwell(time) { + layerPush( + null, + 0, + time, + tool.getNumber() + ); + } + + function camOut(point, cut) { + point = point.clone(); + point.x += wmx; + point.y += wmy; + point.z += zadd; + + if (nextIsMove) { + cut = 0; + nextIsMove = false; + } + let rate = feedRate; + + if (!lastPoint) { + let above = point.clone().setZ(zmax + zadd); + // before first point, move cutting head to point above it + layerPush(above, 0, 0, tool.getNumber()); + // then set that as the lastPoint + lastPoint = above; + } + + let deltaXY = lastPoint.distTo2D(point), + deltaZ = point.z - lastPoint.z, + absDeltaZ = Math.abs(deltaZ), + isMove = !cut; + // drop points too close together + if (deltaXY < 0.001 && point.z === lastPoint.z) { + console.trace(["drop dup",lastPoint,point]); + return; + } + if (isMove && deltaXY <= toolDiamMove) { + // convert short planar moves to cuts + if (absDeltaZ <= tolerance) { + cut = 1; + isMove = false; + } else if (deltaZ <= -tolerance) { + // move over before descending + layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool.getNumber()); + // new pos for plunge calc + deltaXY = 0; + } + } //else (TODO verify no else here b/c above could change isMove) + // move over things + if ((deltaXY > toolDiam || (deltaZ > toolDiam && deltaXY > tolerance)) && (isMove || absDeltaZ >= tolerance)) { + let maxz = getZClearPath( + terrain, + lastPoint.x,// - wmx, + lastPoint.y,// - wmy, + point.x,// - wmx, + point.y,// - wmy, + Math.max(point.z, lastPoint.z), + zadd, + maxToolDiam/2, + zclear + ) + ztOff, + mustGoUp = Math.max(maxz - point.z, maxz - lastPoint.z) >= tolerance, + clearz = maxz; + // up if any point between higher than start/outline + if (mustGoUp) { + clearz = maxz + zclear; + layerPush(lastPoint.clone().setZ(clearz), 0, 0, tool.getNumber()); + } + // over to point above where we descend to + if (mustGoUp || point.z < maxz) { + layerPush(point.clone().setZ(clearz), 0, 0, tool.getNumber()); + // new pos for plunge calc + deltaXY = 0; + } + } + // synth new plunge rate + if (deltaZ <= -tolerance) { + let threshold = Math.min(deltaXY / 2, absDeltaZ), + modifier = threshold / absDeltaZ; + if (threshold && modifier && deltaXY > tolerance) { + // use modifier to speed up long XY move plunge rates + rate = Math.round(plungeRate + ((feedRate - plungeRate) * modifier)); + } else { + rate = plungeRate; + } + // console.log({deltaZ: deltaZ, deltaXY: deltaXY, threshold:threshold, modifier:modifier, rate:rate, plungeRate:plungeRate}); + } + + // todo synthesize move speed from feed / plunge accordingly + layerPush( + point, + cut ? 1 : 0, + rate, + tool.getNumber() + ); + lastPoint = point; + layerOut.spindle = spindle; + } + + // coming from a previous widget, use previous last point + lastPoint = firstPoint; + + // make top start offset configurable + printPoint = firstPoint || origin; + + // accumulated data for depth-first optimiztions + let depthData = { + rough: [], + outline: [], + roughDiam: 0, + outlineDiam: 0, + contourx: [], + contoury: [], + layer: 0, + drill: [] + }; + + // todo first move into positon + slices.forEach(function(slice, sliceIndex) { + depthData.layer++; + isNewMode = slice.camMode != lastMode; + lastMode = slice.camMode; + nextIsMove = true; + if (isNewMode) depthData.layer = 0; + + switch (slice.camMode) { + case PRO.LEVEL: + setTool(process.camRoughTool, process.camRoughSpeed, 0); + spindle = Math.min(spindleMax, process.camRoughSpindle); + slice.tops.forEach(function(top) { + if (!top.traces) return; + let polys = []; + top.traces.forEach(function (poly) { + polys.push(poly); + if (poly.inner) { + poly.inner.forEach(function(inner) { + polys.push(inner); + }) + } + }); + // set winding specified in output + POLY.setWinding(polys, process.camConventional, false); + printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) { + poly.forEachPoint(function(point, pidx, points, offset) { + camOut(point.clone(), offset !== 0); + }, true, index); + }); + newLayer(); + }); + break; + case PRO.ROUGH: + case PRO.OUTLINE: + let dir = process.camConventional; + if (slice.camMode === PRO.ROUGH) { + setTool(process.camRoughTool, process.camRoughSpeed, process.camRoughPlunge); + spindle = Math.min(spindleMax, process.camRoughSpindle); + depthData.roughDiam = toolDiam; + } else { + setTool(process.camOutlineTool, process.camOutlineSpeed, process.camOutlinePlunge); + spindle = Math.min(spindleMax, process.camOutlineSpindle); + depthData.outlineDiam = toolDiam; + if (!process.camOutlinePocket) { + dir = !dir; + } + } + // todo find closest next trace/trace-point + slice.tops.forEach(function(top) { + if (!top.poly) return; + if (!top.traces) return; + let polys = [], t = [], c = []; + POLY.flatten(top.traces, top.inner || []).forEach(function (poly) { + let child = poly.parent; + if (depthFirst) poly = poly.clone(true); + if (child) c.push(poly); else t.push(poly); + poly.layer = depthData.layer; + polys.push(poly); + }); + // set cut direction on outer polys + POLY.setWinding(t, dir); + // set cut direction on inner polys + POLY.setWinding(c, !dir); + if (depthFirst) { + (slice.camMode === PRO.ROUGH ? depthData.rough : depthData.outline).append(polys); + } else { + printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) { + poly.forEachPoint(function(point, pidx, points, offset) { + camOut(point.clone(), offset !== 0); + }, poly.isClosed(), index); + }); + newLayer(); + } + }); + break; + case PRO.CONTOUR_X: + case PRO.CONTOUR_Y: + if (isNewMode || !printPoint) { + // force start at lower left corner + printPoint = newPoint(bounds.min.x,bounds.min.y,zmax); + } + setTool(process.camContourTool, process.camContourSpeed, process.camFastFeedZ); + spindle = Math.min(spindleMax, process.camContourSpindle); + depthData.outlineDiam = toolDiam; + // todo find closest next trace/trace-point + slice.tops.forEach(function(top) { + if (!top.traces) return; + let polys = [], poly, emit; + top.traces.forEach(function (poly) { + if (depthFirst) poly = poly.clone(true); + polys.push({first:poly.first(), last:poly.last(), poly:poly}); + }); + if (depthFirst) { + (slice.camMode === PRO.CONTOUR_X ? depthData.contourx : depthData.contoury).appendAll(polys); + } else { + printPoint = tip2tipEmit(polys, printPoint, function(el, point, count) { + poly = el.poly; + if (poly.last() === point) poly.reverse(); + poly.forEachPoint(function(point, pidx) { + camOut(point.clone(), pidx > 0); + }, false); + return lastPoint; + }); + newLayer(); + } + }); + break; + case PRO.DRILL: + setTool(process.camDrillTool, process.camDrillDownSpeed, process.camDrillDownSpeed); + // drilling is always depth-first + slice.tops.forEach(function(top) { + if (!top.traces) return; + depthData.drill.appendAll(top.traces); + }); + break; + } + update(sliceIndex / slices.length); + }); + + // act on accumulated layer data + if (depthFirst) { + // roughing depth first + if (depthData.rough.length > 0) { + lastMode = PRO.ROUGH; + setTool(process.camRoughTool, process.camRoughSpeed, process.camRoughPlunge); + spindle = Math.min(spindleMax, process.camRoughSpindle); + printPoint = poly2polyDepthFirstEmit(depthData.rough, printPoint, function(poly, index, count, fromPoint) { + let last = null; + if (easeDown && poly.isClosed()) { + last = poly.forEachPointEaseDown(function(point, offset) { + camOut(point.clone(), offset > 0); + }, fromPoint); + } else { + poly.forEachPoint(function(point, pidx, points, offset) { + camOut(point.clone(), offset !== 0); + }, poly.isClosed(), index); + } + newLayer(); + return last; + }, depthData.roughDiam * process.camRoughOver * 1.01); + } + // outline depth first + if (depthData.outline.length > 0) { + lastMode = PRO.OUTLINE; + setTool(process.camOutlineTool, process.camOutlineSpeed, process.camOutlinePlunge); + spindle = Math.min(spindleMax, process.camOutlineSpindle); + printPoint = poly2polyDepthFirstEmit(depthData.outline, printPoint, function(poly, index, count, fromPoint) { + let last = null; + if (easeDown && poly.isClosed()) { + last = poly.forEachPointEaseDown(function(point, offset) { + camOut(point.clone(), offset > 0); + }, fromPoint); + } else { + poly.forEachPoint(function(point, pidx, points, offset) { + camOut(point.clone(), offset !== 0); + last = point; + }, poly.isClosed(), index); + } + newLayer(); + return last; + }, depthData.outlineDiam * 0.01); + } + // two modes for deferred outlining: x then y or combined + if (process.camContourCurves) { + lastMode = PRO.CONTOUR_X; + setTool(process.camContourTool, process.camContourSpeed, process.camContourPlunge); + spindle = Math.min(spindleMax, process.camContourSpindle); + // combined deferred contour x and y outlining + let contourxy = [].appendAll(depthData.contourx).appendAll(depthData.contoury); + printPoint = tip2tipEmit(contourxy, printPoint, function(el, point, count) { + let poly = el.poly; + if (poly.last() === point) { + poly.reverse(); + } + poly.forEachPoint(function(point, pidx) { + camOut(point.clone(), pidx > 0); + }, false); + newLayer(); + return lastPoint; + }); + } else { + setTool(process.camContourTool, process.camContourSpeed, process.camContourPlunge); + spindle = Math.min(spindleMax, process.camContourSpindle); + // deferred contour x outlining + if (depthData.contourx.length > 0) { + lastMode = PRO.CONTOUR_X; + // force start at lower left corner + // printPoint = newPoint(bounds.min.x,bounds.min.y,zmax); + printPoint = tip2tipEmit(depthData.contourx, printPoint, function(el, point, count) { + let poly = el.poly; + if (poly.last() === point) poly.reverse(); + poly.forEachPoint(function(point, pidx) { + camOut(point.clone(), pidx > 0); + }, false); + newLayer(); + return lastPoint; + }); + } + // deferred contour y outlining + if (depthData.contoury.length > 0) { + lastMode = PRO.CONTOUR_Y; + // force start at lower left corner + printPoint = tip2tipEmit(depthData.contoury, printPoint, function(el, point, count) { + let poly = el.poly; + if (poly.last() === point) poly.reverse(); + poly.forEachPoint(function(point, pidx) { + camOut(point.clone(), pidx > 0); + }, false); + newLayer(); + return lastPoint; + }); + } + } + } + + // drilling is always depth first, and always output last (change?) + if (depthData.drill.length > 0) { + lastMode = PRO.DRILL; + setTool(process.camDrillTool, process.camDrillDownSpeed, process.camDrillDownSpeed); + emitDrills(depthData.drill); + } + + // last layer/move is to zmax + // injected into the last layer generated + if (lastPoint) + addOutput(newOutput[newOutput.length-1], printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool.getNumber()); + + // replace output single flattened layer with all points + print.output = newOutput; + + if (widgetIndex + 1 < widgetCount) { + printSetup(print, update, widgetIndex + 1, printPoint); + } + }; + + /** + * return tool Z clearance height for a line segment movement path + */ + function getZClearPath(terrain, x1, y1, x2, y2, z, zadd, off, over) { + let maxz = z; + let check = []; + for (let i=0; i p.intersections(p1, p2, true)).flat(); + if (int.length) { + maxz = Math.max(maxz, data.z + zadd + over); + continue; + } + + let s1 = p1.slopeTo(p2).toUnit().normal(); + let s2 = p2.slopeTo(p1).toUnit().normal(); + let pa = p1.projectOnSlope(s1, off); + let pb = p2.projectOnSlope(s1, off); + int = data.tops.map(p => p.intersections(pa, pb, true)).flat(); + if (int.length) { + maxz = Math.max(maxz, data.z + zadd + over); + continue; + } + pa = p1.projectOnSlope(s2, off); + pb = p2.projectOnSlope(s2, off); + int = data.tops.map(p => p.intersections(pa, pb, true)).flat(); + if (int.length) { + maxz = Math.max(maxz, data.z + zadd + over); + continue; + } + } + return maxz; + } + +})(); diff --git a/src/mode/cam/topo.js b/src/mode/cam/topo.js index dd67dcf5..b8ca238c 100644 --- a/src/mode/cam/topo.js +++ b/src/mode/cam/topo.js @@ -293,7 +293,6 @@ onupdate(0.0 + (index/total) * 0.20, "topo slice"); }, genso: true }); - // ondone(slices.map(data => data.slice)); processSlices(slices.map(data => data.slice)); } }