diff --git a/src/kiri/mode/cam/client.js b/src/kiri/mode/cam/client.js index 245210b3..39529575 100644 --- a/src/kiri/mode/cam/client.js +++ b/src/kiri/mode/cam/client.js @@ -287,7 +287,7 @@ export function opRender() { oplist.forEach((rec, i) => { let title = ''; let clock = rec.type === '|'; - let label = clock ? `` : rec.mode ?? rec.type; + let label = clock ? `` : (rec.mode ? rec.type + ' ' + rec.mode : rec.type); let clazz = notime ? ["draggable", "notime"] : ["draggable"]; let notable = rec.note ? rec.note.split(' ').filter(v => v.charAt(0) === '#') : undefined; if (clock) { clazz.push('clock'); title = ` title="end of ops chain\ndrag/drop like an op\nops after this are disabled"` } diff --git a/src/kiri/mode/cam/op-contour.js b/src/kiri/mode/cam/op-contour.js index e0437079..9f5d7021 100644 --- a/src/kiri/mode/cam/op-contour.js +++ b/src/kiri/mode/cam/op-contour.js @@ -85,6 +85,7 @@ class OpContour extends CamOp { let { color, addSlices, settings, updateToolDiams } = state; let filter = createFilter(op, settings.origin, op.axis.toLowerCase()); let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter(); + updateToolDiams(toolDiam); // we need topo for safe travel moves when roughing and outlining // not generated when drilling-only. then all z moves use bounds max. @@ -107,6 +108,7 @@ class OpContour extends CamOp { contour: op, state: state }); + // computed if set to 0 this.tolerance = topo.tolerance; } @@ -116,10 +118,10 @@ class OpContour extends CamOp { let { settings } = state; let { process } = settings; - let { setTolerance, setTool, setSpindle, setPrintPoint } = ops; - let { camOut, newLayer, printPoint, lastPoint } = ops; - let { bounds, zmax } = ops; + let { setTolerance, setTool, setSpindle } = ops; + let { widget, camOut, newLayer, zmax } = ops; + let bounds = widget.getBoundingBox(); let toolDiam = this.toolDiam; let stepover = toolDiam * op.step * 2; let depthFirst = process.camDepthFirst; @@ -129,7 +131,7 @@ class OpContour extends CamOp { setSpindle(op.spindle); setTolerance(this.tolerance); - printPoint = newPoint(bounds.min.x, bounds.min.y, zmax); + let printPoint = newPoint(bounds.min.x, bounds.min.y, zmax); for (let slice of sliceOut) { // ignore debug slices @@ -158,20 +160,17 @@ class OpContour extends CamOp { } if (depthFirst) { - printPoint = tip2tipEmit(depthData, printPoint, function (el, point, count) { + tip2tipEmit(depthData, printPoint, function (el, point, count) { let poly = el.poly; if (poly.last() === point) { poly.reverse(); } poly.forEachPoint(function (point, pidx) { - camOut(point.clone().annotate({ slice: poly.slice }), pidx > 0, { moveLen: stepover }); + camOut(printPoint = point.clone().annotate({ slice: poly.slice }), pidx > 0, { moveLen: stepover }); }, false); newLayer(); - return lastPoint(); }); } - - setPrintPoint(printPoint); } } diff --git a/src/kiri/mode/cam/op-drill.js b/src/kiri/mode/cam/op-drill.js index ca495175..7ce27232 100644 --- a/src/kiri/mode/cam/op-drill.js +++ b/src/kiri/mode/cam/op-drill.js @@ -29,38 +29,42 @@ class OpDrill extends CamOp { if (!drill.selected) { return } + let slice = newSlice(0); if (op.mark) { // replace depth with single down peck drill.depth = op.down } + drill.zBottom = drill.z - drill.depth; // for thru holes, follow z thru when set if ((op.thru > 0)) { drill.zBottom -= op.thru; } + const poly = newPolygon() poly.points.push(newPoint(drill.x, drill.y, drill.z)) poly.points.push(newPoint(drill.x, drill.y, drill.zBottom)) - // poly.points.pop(); + slice.camTrace = { tool: op.tool, rate: op.feed, plunge: op.rate }; slice.camLines = [poly]; - slice.output() .setLayer(state.layername, { face: color, line: color }) .addPolys(slice.camLines); + addSlices(slice); sliceOut.push(slice); }); } prepare(ops, progress) { - let { op } = this; + let { op, sliceOut } = this; let { setTool, setSpindle, setDrill, emitDrills } = ops; + setTool(op.tool, undefined, op.rate); setDrill(op.down, op.lift, op.dwell); setSpindle(op.spindle); - emitDrills(this.sliceOut.map(slice => slice.camLines).flat()); + emitDrills(sliceOut.map(slice => slice.camLines).flat()); } } diff --git a/src/kiri/mode/cam/op-helical.js b/src/kiri/mode/cam/op-helical.js index 4d6e2719..be1aa20d 100644 --- a/src/kiri/mode/cam/op-helical.js +++ b/src/kiri/mode/cam/op-helical.js @@ -240,20 +240,13 @@ export class OpHelical extends CamOp { } async prepare(ops, progress) { - let { - setTool, - setSpindle, - polyEmit, - setPrintPoint, - getPrintPoint, - } = ops; + let { polyEmit, printPoint, setTool, setSpindle } = ops; let { tool, spindle, rate, feed } = this.op; setTool(tool, feed, rate); setSpindle(spindle); - let [polys] = this.sliceOut.map((s) => s.camLines); - let pp = getPrintPoint(); + let [ polys ] = this.sliceOut.map((s) => s.camLines); polys = polys.slice(); for (;;) { @@ -265,7 +258,7 @@ export class OpHelical extends CamOp { for (let i = 0; i < polys.length; i++) { if (!polys[i]) continue; - if ((dist = polys[i].first().distTo2D(pp)) < closestDist) { + if ((dist = polys[i].first().distTo2D(printPoint)) < closestDist) { closestDist = dist; closest = polys[i]; closestI = i; @@ -275,9 +268,7 @@ export class OpHelical extends CamOp { if (!closest) break; poly = polys[closestI] polys[closestI] = null; - pp = polyEmit(poly, 0, 1, poly.points[0]) + printPoint = polyEmit(poly, 0, 1, poly.points[0]) } - - setPrintPoint(pp); } } diff --git a/src/kiri/mode/cam/op-index.js b/src/kiri/mode/cam/op-index.js index c9fd55fa..136dd349 100644 --- a/src/kiri/mode/cam/op-index.js +++ b/src/kiri/mode/cam/op-index.js @@ -25,16 +25,12 @@ export class OpIndex extends CamOp { } prepare(ops, progress) { - const { lastPoint, zmax, zclear, camOut, stock, newLayer } = ops; - let last = lastPoint(); - if (last) { - // max point of stock corner radius when rotating (safe z when indexing) - const rzmax = (Math.max(stock.y, stock.z) * Math.sqrt(2)) / 2 + zclear; - const zmove = Math.max(rzmax, zmax); - // move above rotating stock - camOut(last = last.clone().setZ(zmove), 0); - // issue rotation command - camOut(last = last.clone().setZ(zmove).setA(this.degrees), 0); - } + const { zmax, zsafe, camOut } = ops; + // max point of stock corner radius when rotating (safe z when indexing) + const ztop = Math.max(zsafe, zmax); + // move above rotating stock + camOut(last = last.clone().setZ(ztop), 0); + // issue rotation command + camOut(last = last.clone().setZ(ztop).setA(this.degrees), 0); } } diff --git a/src/kiri/mode/cam/op-lathe.js b/src/kiri/mode/cam/op-lathe.js index 802db102..da0e451d 100644 --- a/src/kiri/mode/cam/op-lathe.js +++ b/src/kiri/mode/cam/op-lathe.js @@ -57,7 +57,7 @@ class OpLathe extends CamOp { let { op, state, slices, topo } = this; let { settings } = state; - let { setTool, setSpindle, setPrintPoint } = ops; + let { setTool, setSpindle } = ops; let { camOut, newLayer, printPoint } = ops; let { zmax } = ops; @@ -111,8 +111,6 @@ class OpLathe extends CamOp { // camOut(last = last.clone().setA(amax), 0); newLayer(); ops.addGCode([`G0 Z${zmax.round(2)}`, `G0 A${amax}`, "G92 A0"]); - - setPrintPoint(last); } } diff --git a/src/kiri/mode/cam/op-level.js b/src/kiri/mode/cam/op-level.js index 56ab44b1..4cac15c1 100644 --- a/src/kiri/mode/cam/op-level.js +++ b/src/kiri/mode/cam/op-level.js @@ -14,12 +14,12 @@ class OpLevel extends CamOp { async slice(progress) { let { op, state } = this; - let { settings, addSlices, updateToolDiams } = state; - let { zMax, ztOff, color, tshadow } = state; + let { addSlices, color, settings, tshadow, updateToolDiams, zMax, ztOff } = state; let { stock } = settings; let toolDiam = new Tool(settings, op.tool).fluteDiameter(); let stepOver = this.stepOver = toolDiam * op.step; + let wpos = state.widget.track.pos; let zTop = zMax + ztOff; let zBot = zTop - op.down; let zList = op.stepz ? util.lerp(zTop, zBot, op.stepz) : [ zBot ]; @@ -28,7 +28,7 @@ class OpLevel extends CamOp { let points = []; let clear = op.stock ? - [ newPolygon().centerRectangle({x:0,y:0,z:0}, stock.x + toolDiam/2, stock.y) ] : + [ newPolygon().centerRectangle({x:-wpos.x,y:-wpos.y,z:wpos.z}, stock.x + toolDiam/2, stock.y) ] : POLY.outer(POLY.offset(tshadow, toolDiam * (op.inset || 0))); POLY.fillArea(clear, 1090, stepOver, points); @@ -50,7 +50,7 @@ class OpLevel extends CamOp { prepare(ops, progress) { let { op, layers, stepOver } = this; - let { setTool, setSpindle, printPoint, setPrintPoint } = ops; + let { setTool, setSpindle, printPoint } = ops; let { newLayer, tip2tipEmit, camOut } = ops; setTool(op.tool, op.rate); @@ -68,7 +68,6 @@ class OpLevel extends CamOp { }); newLayer(); } - setPrintPoint(printPoint); } } diff --git a/src/kiri/mode/cam/op-outline.js b/src/kiri/mode/cam/op-outline.js index 81d89491..9169ccbe 100644 --- a/src/kiri/mode/cam/op-outline.js +++ b/src/kiri/mode/cam/op-outline.js @@ -231,24 +231,22 @@ class OpOutline extends CamOp { prepare(ops, progress) { let { op, state, sliceOut } = this; - let { setTool, setSpindle, setPrintPoint } = ops; - let { polyEmit, depthOutlinePath } = ops; - let { newLayer, printPoint } = ops; let { settings } = state; let { process } = settings; + let { depthOutlinePath, newLayer, polyEmit, printPoint, setTool, setSpindle } = ops; - let easeDown = process.camEaseDown; - let toolDiam = this.toolDiam; let cutdir = op.ov_conv; let depthFirst = process.camDepthFirst; let depthData = []; + let easeDown = process.camEaseDown; + let toolDiam = this.toolDiam; setTool(op.tool, op.rate, op.plunge); setSpindle(op.spindle); - // printpoint becomes NaN in engine mode. not sure why but this fixes it - if(Object.values(printPoint).some(v=>Number.isNaN(v))){ - printPoint = newPoint(0,0,0); + // printPoint becomes NaN in engine mode + if (Object.values(printPoint).some(v => Number.isNaN(v))) { + printPoint = newPoint(0, 0, 0); } for (let slice of sliceOut) { @@ -308,8 +306,6 @@ class OpOutline extends CamOp { printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, true, ease); } } - - setPrintPoint(printPoint); } } diff --git a/src/kiri/mode/cam/op-pocket.js b/src/kiri/mode/cam/op-pocket.js index cd90e684..4e1826e2 100644 --- a/src/kiri/mode/cam/op-pocket.js +++ b/src/kiri/mode/cam/op-pocket.js @@ -242,7 +242,7 @@ class OpPocket extends CamOp { prepare(ops, progress) { let { op, state, pockets } = this; - let { getPrintPoint , pocket, setTool, setSpindle, setTolerance } = ops; + let { pocket, setTool, setSpindle, setTolerance } = ops; let { process } = state.settings; setTool(op.tool, op.rate); @@ -255,36 +255,15 @@ class OpPocket extends CamOp { // eliminate empty pockets pockets = pockets.filter(p => p.length); - // pockets is an [ array of an [ array of slices ] ] - // each top level array is a pocket containing a [ z layer array of slices ] - // follow each pocket to the next closest one from previous exit - for (;;) { - let printPoint = getPrintPoint(); - let min = { - dist: Infinity, - pocket: undefined - }; - for (let pocket of pockets.filter(p => !p.used)) { - let poly = pocket[0].camLines.slice().sort((a,b) => b.area() - a.area())[0]; - if (!poly) continue; - let find = poly.findClosestPointTo(printPoint); - if (find.distance < min.dist) { - min.pocket = pocket; - min.dist = find.distance; - } - } - if (min.pocket) { - min.pocket.used = true; - pocket({ - cutdir: op.ov_conv, - depthFirst: process.camDepthFirst && !state.isIndexed, - easeDown: op.down && process.easeDown ? op.down : 0, - progress: (n,m) => progress(n/m, "pocket"), - slices: min.pocket - }); - } else { - break; - } + // naive output order + for (let slices of pockets) { + pocket({ + cutdir: op.ov_conv, + depthFirst: process.camDepthFirst && !state.isIndexed, + easeDown: op.down && process.easeDown ? op.down : 0, + progress: (n,m) => progress(n/m, "pocket"), + slices + }); } } } diff --git a/src/kiri/mode/cam/op-register.js b/src/kiri/mode/cam/op-register.js index 3905f4ec..3448d864 100644 --- a/src/kiri/mode/cam/op-register.js +++ b/src/kiri/mode/cam/op-register.js @@ -14,7 +14,7 @@ class OpRegister extends CamOp { async slice(progress) { let { op, state } = this; - let { settings, widget, bounds, addSlices, zMax, zThru, color } = state; + let { addSlices, bounds, color, settings, widget, zThru } = state; let { updateToolDiams } = state; let tool = new Tool(settings, op.tool); @@ -23,21 +23,22 @@ class OpRegister extends CamOp { updateToolDiams(tool.fluteDiameter()); - let { stock } = settings, - toolZ = widget.track.pos.z, - boundMinX = bounds.min.x, - boundMaxX = bounds.max.x, - boundMinY = bounds.min.y, - boundMaxY = bounds.max.y, - toolOffset = tool.fluteDiameter() * 2, - centerX = (boundMinX + boundMaxX) / 2, - centerY = (boundMinY + boundMaxY) / 2, - cutDepth = op.thru || zThru || 0, - pathPoints = [], - stockToSurfaceOffset = stock.z - bounds.max.z, - startZ = bounds.max.z + stockToSurfaceOffset + toolZ, - endZ = toolZ - cutDepth, - cutOffset = op.offset; + let { stock } = settings; + let { pos } = widget.track; + let toolZ = pos.z, + boundMinX = bounds.min.x, + boundMaxX = bounds.max.x, + boundMinY = bounds.min.y, + boundMaxY = bounds.max.y, + toolOffset = tool.fluteDiameter() * 2, + centerX = (boundMinX + boundMaxX) / 2, + centerY = (boundMinY + boundMaxY) / 2, + cutDepth = op.thru || zThru || 0, + pathPoints = [], + stockToSurfaceOffset = stock.z - bounds.max.z, + startZ = bounds.max.z + stockToSurfaceOffset + toolZ, + endZ = toolZ - cutDepth, + cutOffset = op.offset; if (!(stock.x && stock.y && stock.z)) { return; @@ -146,7 +147,7 @@ class OpRegister extends CamOp { prepare(ops, progress) { let { op } = this; - let { setTool, setSpindle, setDrill, emitDrills } = ops; + let { emitDrills, setDrill, setSpindle, setTool } = ops; if (op.axis === '-' || op.axis === '=') { setTool(op.tool, op.feed, op.rate); diff --git a/src/kiri/mode/cam/op-rough.js b/src/kiri/mode/cam/op-rough.js index f5c2177c..809c3d40 100644 --- a/src/kiri/mode/cam/op-rough.js +++ b/src/kiri/mode/cam/op-rough.js @@ -296,7 +296,7 @@ class OpRough extends CamOp { prepare(ops, progress) { let { op, state, sliceOut, camFaces } = this; - let { setTool, setSpindle, setPrintPoint, pocket, polyEmit, newLayer, printPoint } = ops; + let { setTool, setSpindle, pocket, polyEmit, newLayer, printPoint } = ops; let { settings } = state; let { process } = settings; @@ -329,7 +329,6 @@ class OpRough extends CamOp { } // output the roughing passes - setPrintPoint(printPoint); pocket({ cutdir, depthFirst, diff --git a/src/kiri/mode/cam/prepare.js b/src/kiri/mode/cam/prepare.js index 079cc378..2ee59cfe 100644 --- a/src/kiri/mode/cam/prepare.js +++ b/src/kiri/mode/cam/prepare.js @@ -18,28 +18,44 @@ const { toRadians } = util * @param {Number} [index] into widget array * @param {Object} [firstPoint] starting point */ -export async function cam_prepare(widall, settings, update) { - const widgets = widall.filter(w => !w.track.ignore && !w.meta.disabled); - const count = widgets.length; +export async function cam_prepare(widgets, settings, update) { + const active = widgets + .filter(w => !w.isSynth() && !w.track.ignore && !w.meta.disabled) + .filter(w => w?.camops.length) + ; + const count = active.length; const weight = 1 / count; - const print = self.kiri_worker.current.print = newPrint(settings, widgets); + const print = self.kiri_worker.current.print = newPrint(settings, active); + const { origin } = settings; + + // cam-specific storage print.output = []; - let point; - widgets.filter(w => !w.isSynth()).forEach((widget, index) => { - point = prepEach(widget, settings, print, point, (progress, msg) => { + // sort output by distance to origin + if (origin) { + let point = newPoint().move(origin); + active.sort((w0,w1) => + newPoint().move(w0.track.pos).distTo2D(point) - + newPoint().move(w1.track.pos).distTo2D(point) + ); + } + + let startPoint; + active.forEach((widget, index) => { + startPoint = prepare_one(widget, settings, print, startPoint, (progress, msg) => { update((index * weight + progress * weight) * 0.75, msg || "prepare"); }); }); + // prune empty levels const output = print.output.filter(level => Array.isArray(level)); - if (render) // allows it to run from CLI - return render.path( - output, - (progress, layer) => { - update(0.75 + progress * 0.25, "render", layer); - }, { + // compute path display + return render.path( + output, + (progress, layer) => { + update(0.75 + progress * 0.25, "render", layer); + }, { thin: true, print: 0, move: 0x557799, @@ -49,82 +65,75 @@ export async function cam_prepare(widall, settings, update) { action: "milling", maxspeed: settings.process.camFastFeed || 6000 } - ); + ); }; -export function prepEach(widget, settings, print, firstPoint, update) { - - if (widget.camops.length === 0 || widget.meta.disabled) return; +// process `prepare` paths for a single widget +export function prepare_one(widget, settings, print, firstPoint, update) { let { device, process } = settings, - isIndexed = process.camStockIndexed, - startCenter = process.camOriginCenter, - alignTop = settings.controller.alignTop, + { alignTop } = settings.controller, + { camArcEnabled, camArcResolution, camArcTolerance } = process, + { camDepthFirst, camEaseAngle, camEaseDown } = process, + { camForceZMax, camFullEngage, camInnerFirst, camOriginCenter } = process, + { camOriginOffX, camOriginOffY, camOriginOffZ } = process, + { camStockOffset, camStockIndexed, camZClearance } = process, stock = settings.stock || {}, - stockz = stock.z * (isIndexed ? 0.5 : 1), - outer = settings.bounds || widget.getPositionBox(), - outerz = outer.max.z, - zclear = (process.camZClearance || 1), - zmax_force = process.camForceZMax || false, - zmax_outer = stockz + zclear, - wztop = widget.track.top, - ztOff = stockz - wztop, + stockZ = stock.z * (camStockIndexed ? 0.5 : 1), + outer = settings.bounds || widget.getPositionBox(), // dup of bounds? + outerZ = outer.max.z, // dup of boundsZ? + stockZClear = stockZ + camZClearance, + widgetTrackTop = widget.track.top, + widgetTopToStock = stockZ - widgetTrackTop, bounds = widget.getBoundingBox(), - boundsz = isIndexed ? stock.z / 2 : bounds.max.z + ztOff, - zadd = !isIndexed ? stock.z - boundsz : alignTop ? outerz - boundsz : 0, - zmax = outerz + zclear + (process.camOriginOffZ || 0), - zsafe = isIndexed ? Math.hypot(bounds.dim.y, bounds.dim.z) / 2 + zclear : zmax, + boundsZ = camStockIndexed ? stock.z / 2 : bounds.max.z + widgetTopToStock, + zMax = outerZ + camZClearance + (camOriginOffZ || 0) + widgetTopToStock, + zSafe = camStockIndexed ? Math.hypot(bounds.dim.y, bounds.dim.z) / 2 + camZClearance : zMax, wmpos = widget.track.pos, wmx = wmpos.x, wmy = wmpos.y, - originx = (startCenter ? 0 : -stock.x / 2) + (process.camOriginOffX || 0), - originy = (startCenter ? 0 : -stock.y / 2) + (process.camOriginOffY || 0), - origin = newPoint(originx + wmx, originy + wmy, zmax), - easeDown = process.camEaseDown, - easeAngle = process.camEaseAngle, - depthFirst = process.camDepthFirst, - innerFirst = process.camInnerFirst, - engageFactor = process.camFullEngage, - arcTolerance = process.camArcTolerance, - arcRes = toRadians(process.camArcResolution), - arcEnabled = process.camArcEnabled && arcTolerance > 0 && arcRes > 0, - tolerance = 0, + wmz = !camStockIndexed ? stock.z - boundsZ : alignTop ? outerZ - boundsZ : 0, + originx = (camOriginCenter ? 0 : -stock.x / 2) + (camOriginOffX || 0), + originy = (camOriginCenter ? 0 : -stock.y / 2) + (camOriginOffY || 0), + origin = newPoint(originx, originy, zMax), + arcRadians = toRadians(camArcResolution), + arcEnabled = camArcEnabled && camArcTolerance > 0 && arcRadians > 0, + currentOp, drillDown = 0, drillLift = 0, drillDwell = 0, - lasering = false, - laserPower = 0, - newOutput = print.output || [], - layerOut = [], - printPoint, + feedRate, isPocket, isContour, isRough, isLathe, isIndex, + layerOut = [], + lastOp, + lastTool, + lastPush, + lasering = false, + laserPower = 0, + maxToolDiam = widget.maxToolDiam, + newOutput = print.output || [], + plungeRate = process.camFastFeedZ, + printPoint, tool, toolType, toolDiam, toolDiamMove, - plungeRate = process.camFastFeedZ, - feedRate, - lastOp, - lastTool, - lastPush, - lastPoint, - currentOp, nextIsMove = true, synthPlunge = false, spindle = 0, spindleMax = device.spindleMax, - maxToolDiam = widget.maxToolDiam, terrain = widget.terrain ? widget.terrain.map(data => { return { z: data.z, tops: data.tops, }; - }) : zmax; - + }) : zMax, + tolerance = 0; +console.log({ zSafe, stockZ, outerZ, boundsZ, wmz }); function newLayer(op) { if (layerOut.length || layerOut.mode) { newOutput.push(layerOut); @@ -158,10 +167,6 @@ export function prepEach(widget, settings, print, firstPoint, update) { } } - function setPrintPoint(point) { - ops.printPoint = printPoint = point; - } - function setSpindle(speed) { spindle = Math.min(speed, spindleMax); } @@ -209,7 +214,6 @@ export function prepEach(widget, settings, print, firstPoint, update) { if (!closest) return; polys[closestI] = null; - printPoint = closest.first(); emitDrill(closest, drillDown, drillLift, drillDwell); } } @@ -240,7 +244,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { break; } } - camOut(point.clone().setZ(zmax_outer), 0); + camOut(point.clone().setZ(stockZClear), 0); points.forEach(function (point, index) { camOut(point, 1); if (index > 0 && index < points.length - 1) { @@ -248,7 +252,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { if (lift) camOut(point.clone().setZ(point.z + lift), 0); } }) - camOut(point.clone().setZ(zmax_outer), 0); + camOut(point.clone().setZ(stockZClear), 0); newLayer(); } @@ -270,7 +274,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { let power = emit ? laserPower : 0; if (emit && lasering.adapt) { let { minz, maxz, minp, maxp, adaptrp } = lasering; - maxz = maxz || wztop; + maxz = maxz || widgetTrackTop; let deltaz = maxz - minz; let { z } = point; if (adaptrp) { @@ -288,13 +292,14 @@ export function prepEach(widget, settings, print, firstPoint, update) { } } if (lasering.flat) { - point.z = (stock && stock.z ? stock.z : wztop) + lasering.flatz; + point.z = (stock && stock.z ? stock.z : widgetTrackTop) + lasering.flatz; } print.addOutput(layerOut, point, power, speed, tool, { type: 'laser' }); } else { print.addOutput(layerOut, point, emit, speed, tool, { type, center, arcPoints }); } lastPush = { point, emit, speed, tool }; + printPoint = point ?? printPoint; return point; } @@ -316,8 +321,17 @@ export function prepEach(widget, settings, print, firstPoint, update) { return newPoint( p.x + wmx, p.y + wmy, - p.z + zadd - ).setA(p.a ?? lastPoint?.a).annotate({ slice: p.slice }); + p.z + wmz + ) + .setA(p.a ?? printPoint?.a) + .annotate({ slice: p.slice }); + } + + /** + * wrapper: translate point into workspace coordinates and call `camOut` + */ + function camOut(point, emit = 1, opts) { + _camOut(applyWidgetMovement(point), emit, opts); } /** @@ -329,7 +343,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { * @param {number} opts.moveLen typically = tool diameter used to trigger terrain detection * @param {number} opts.factor speed scale factor */ - function camOut(point, emit = 1, opts) { + function _camOut(point, emit, opts) { let lop = lastOp; lastOp = currentOp; @@ -337,17 +351,15 @@ export function prepEach(widget, settings, print, firstPoint, update) { // 1. move to safe z of current point preserving angle // 2. move to safe z of new point preserving old angle // 3. move to safe z of new point with new angle - if (lop !== currentOp && lastPoint) { - // compensate for applyWidgetMovement() applied to lastPoint - let lpo = lastPoint.clone().move({ x: -wmx, y: -wmy, z: -zadd }); - camOut(lpo.clone().setZ(zsafe).setA(lastPoint.a), 0); - camOut(point.clone().setZ(zsafe).setA(lastPoint.a), 0); - camOut(point.clone().setZ(zsafe), 0); + if (lop !== currentOp) { + _camOut(printPoint.clone().setZ(zSafe).setA(printPoint.a), 0); + _camOut(point.clone().setZ(zSafe).setA(printPoint.a), 0); + _camOut(point.clone().setZ(zSafe), 0); } if (lop?.type === 'index' && lop !== currentOp ) { // console.log('post index first point', point); - camOut(point.clone().setZ(lastPoint.z).setA(lastPoint.a), 1); + _camOut(point.clone().setZ(printPoint.z).setA(printPoint.a), 1); } let { @@ -361,9 +373,6 @@ export function prepEach(widget, settings, print, firstPoint, update) { const isArc = emit == 2 || emit == 3; const pointA = point.a; - // apply widget movement pos - point = applyWidgetMovement(point); - if (nextIsMove) { emit = 0; nextIsMove = false; @@ -372,13 +381,13 @@ export function prepEach(widget, settings, print, firstPoint, update) { let rate = feedRate * factor; // carry rotation forward when not overridden - if (lastPoint && pointA !== undefined && lastPoint.a !== undefined) { - let DA = lastPoint.a - pointA; - let MZ = Math.max(lastPoint.z, point.z) + if (pointA !== undefined && printPoint.a !== undefined) { + let DA = printPoint.a - pointA; + let MZ = Math.max(printPoint.z, point.z) // find arc length let AL = (Math.abs(DA) / 360) * (2 * Math.PI * MZ); if (AL >= 1) { - let lerp = base.util.lerp(lastPoint.a, pointA, 1); + let lerp = base.util.lerp(printPoint.a, pointA, 1); // create interpolated point set for rendering and animation // console.log({ DA, MZ, AL }, lerp.length); for (let a of lerp) { @@ -395,39 +404,31 @@ export function prepEach(widget, settings, print, firstPoint, update) { } } - // before first point, move cutting head to point above it - // then set that new point as the lastPoint - if (!lastPoint) { - let above = point.clone().setZ(stockz + zclear); - // let above = point.clone().setZ(zmax + zadd + ztOff); - lastPoint = layerPush(above, 0, 0, tool); - } - // measure deltas to last point in XY and Z - let deltaXY = lastPoint.distTo2D(point), - deltaZ = point.z - lastPoint.z, + let deltaXY = printPoint.distTo2D(point), + deltaZ = point.z - printPoint.z, absDeltaZ = Math.abs(deltaZ), hasDelta = deltaXY >= 0.001 && absDeltaZ >= 0, isMove = emit == 0; // when rapid pluge could cut thru stock, rapid to just above stock // then continue plunge as a plunge cut - if (deltaZ < 0 && lastPoint.z > stockz && point.z < stockz && emit === 0) { - // console.log('detected plunge cut as rapid move', lastPoint.z, stockz, point.z); - layerPush(point.clone().setZ(stockz + 1), 0, 0, tool); + if (deltaZ < 0 && printPoint.z > stockZ && point.z < stockZ && emit === 0) { + // console.log('detected plunge cut as rapid move', printPoint.z, stockZ, point.z); + layerPush(point.clone().setZ(stockZ + 1), 0, 0, tool); // change to cutting move for remainder of plunge emit = 1; isMove = false; } // drop points too close together - if (!isLathe && !isArc && deltaXY < 0.001 && point.z === lastPoint.z && point.a === lastPoint.a) { - // console.trace(["drop dup",lastPoint,point]); + if (!isLathe && !isArc && deltaXY < 0.001 && point.z === printPoint.z && point.a === printPoint.a) { + // console.trace(["drop dup",printPoint,point]); return; } // no jump moves in contour mode to adjacent slice points - let steady = lastPoint && currentOp.type === 'contour' && Math.abs(point.slice - lastPoint.slice) < 4; + let steady = currentOp.type === 'contour' && Math.abs(point.slice - printPoint.slice) < 4; // convert short planar moves to cuts in some cases if (hasDelta && !isArc && isMove && deltaXY <= moveLen && deltaZ <= 0 && !lasering) { @@ -439,15 +440,15 @@ export function prepEach(widget, settings, print, firstPoint, update) { isMove = false; } else if (deltaZ <= -tolerance) { // move over before descending - layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool); + layerPush(point.clone().setZ(printPoint.z), 0, 0, tool); // new pos for plunge calc deltaXY = 0; } } else if (isMove && isLathe) { - if (point.z > lastPoint.z) { - layerPush(lastPoint.clone().setZ(point.z), 0, 0, tool); - } else if (point.z < lastPoint.z) { - layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool); + if (point.z > printPoint.z) { + layerPush(printPoint.clone().setZ(point.z), 0, 0, tool); + } else if (point.z < printPoint.z) { + layerPush(point.clone().setZ(printPoint.z), 0, 0, tool); } } else if (isMove && !steady) { // for longer moves, check the terrain to see if we need to go up and over @@ -458,40 +459,40 @@ export function prepEach(widget, settings, print, firstPoint, update) { if (bigXY || bigZ || midZ) { let maxz = getZClearPath( terrain, - lastPoint.x - wmx, - lastPoint.y - wmy, - point.x - wmx, - point.y - wmy, - Math.max(point.z, lastPoint.z), - zadd, + printPoint.x, + printPoint.y, + point.x, + point.y, + Math.max(point.z, printPoint.z), + 0, // zadd, maxToolDiam / 2, - zclear + camZClearance ), - maxZdelta = Math.max(maxz - point.z, maxz - lastPoint.z), + maxZdelta = Math.max(maxz - point.z, maxz - printPoint.z), mustGoUp = maxZdelta >= tolerance, clearz = maxz; let zIsBelow = point.z <= maxz; - if (zmax_force) { - clearz = maxz = zmax + zadd; + if (camForceZMax) { + clearz = maxz = zMax;// + zadd; zIsBelow = true; } // up if any point between higher than start/outline, go up first if (mustGoUp || zIsBelow) { - const zClearance = clearz + (isIndexed ? 0 : ztOff); + const zClearance = clearz + (camStockIndexed ? 0 : widgetTopToStock); if (zIsBelow) { - layerPush(lastPoint.clone().setZ(zClearance), 0, 0, tool); + layerPush(printPoint.clone().setZ(zClearance), 0, 0, tool); } layerPush(point.clone().setZ(zClearance), 0, 0, tool); // new pos for plunge calc deltaXY = 0; // if plunge goes below stock, convert to cut - if (emit === 0 && point.z < stockz) { - lastPoint = layerPush(point.clone().setZ(stockz + 1), 0, 0, tool); + if (emit === 0 && point.z < stockZ) { + layerPush(point.clone().setZ(stockZ + 1), 0, 0, tool); emit = 1; } } } else if (isRough && deltaZ < 0) { - layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool); + layerPush(point.clone().setZ(printPoint.z), 0, 0, tool); } } @@ -526,7 +527,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { if (isArc) { layerOut.mode = currentOp; layerOut.spindle = spindle; - lastPoint = layerPush( + layerPush( point, clockwise ? 2 : 3, tmprate ?? rate, @@ -541,7 +542,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { // TODO: synthesize move speed from feed / plunge accordingly layerOut.mode = currentOp; layerOut.spindle = spindle; - lastPoint = layerPush( + layerPush( point, emit, tmprate ?? rate, @@ -581,10 +582,10 @@ export function prepEach(widget, settings, print, firstPoint, update) { depthData.push(polys); } else { // if not depth first, output the polys in slice order - printPoint = poly2polyEmit(polys, printPoint, function (poly, index, count) { + poly2polyEmit(polys, printPoint, function (poly, index, count) { poly.forEachPoint(function (point, pidx, points, offset) { // scale speed of first cutting poly since it engages the full bit - camOut(point.clone(), offset !== 0, undefined, count === 1 ? engageFactor : 1); + camOut(point.clone(), offset !== 0, undefined, count === 1 ? camFullEngage : 1); }, poly.isClosed(), index); }, { swapdir: false }); newLayer(); @@ -608,8 +609,8 @@ export function prepEach(widget, settings, print, firstPoint, update) { let otops = outs.map(level => { return POLY.nest(level.filter(poly => poly.depth === 0).clone()); }); - printPoint = depthRoughPath(printPoint, 0, ins, itops, polyEmit, false, easeDown); - printPoint = depthRoughPath(printPoint, 0, outs, otops, polyEmit, false, easeDown); + depthRoughPath(printPoint, 0, ins, itops, polyEmit, false, easeDown); + depthRoughPath(printPoint, 0, outs, otops, polyEmit, false, easeDown); } } /** @@ -677,73 +678,12 @@ export function prepEach(widget, settings, print, firstPoint, update) { return touch; } - // coming from a previous widget, use previous last point - lastPoint = firstPoint; - - // make top start offset configurable - printPoint = firstPoint || origin; - - let ops = { - addGCode, - bounds, - camOut, - depthOutlinePath, - depthRoughPath, - emitDrills, - emitTrace, - getPrintPoint() { return printPoint }, - lastPoint: () => { return lastPoint }, - newLayer, - pocket, - poly2polyEmit, - polyEmit, - printPoint, - setDrill, - setLasering, - setPrintPoint, - setSpindle, - setTolerance, - setTool, - stock, - tip2tipEmit, - zclear, - zmax, - zsafe, - }; - - let opSum = 0; - let opTot = widget.camops.map(op => op.weight()).reduce((a, v) => a + v); - - for (let op of widget.camops) { - setTolerance(0); - nextIsMove = true; - currentOp = op.op; - isIndex = currentOp.type === 'index'; - isLathe = currentOp.type === 'lathe'; - isRough = currentOp.type === 'rough'; - isPocket = currentOp.type === 'pocket'; - isContour = currentOp.type === 'contour' || (isPocket && currentOp.contour); - let weight = op.weight(); - newLayer(op.op); - op.prepare(ops, (progress, message) => { - update((opSum + (progress * weight)) / opTot, message || op.type(), message); - }); - opSum += weight; - if (tool && lastPoint) { - newLayer(); - if (!isIndex) { - layerPush(printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool); - newLayer(); - } - } - } - function emitTrace(slice) { let { tool, rate, plunge } = slice.camTrace; setTool(tool, rate, plunge); - printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit, { + poly2polyEmit(slice.camLines, printPoint, polyEmit, { swapdir: false, - weight: innerFirst + weight: camInnerFirst }); newLayer(); } @@ -765,11 +705,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { * @returns {Point} - the last point of the polygon */ function polyEmit(poly, index, count, fromPoint, ops) { - let { - cutFromLast, - } = ops ?? { - cutFromLast: false - }; + let { cutFromLast } = ops ?? { cutFromLast: false }; let arcQ = [], arcMax = Infinity, // no max arc radius lineTolerance = 0.001, // do not consider points under 0.001mm for arcs @@ -778,24 +714,21 @@ export function prepEach(widget, settings, print, firstPoint, update) { fromPoint = fromPoint || printPoint; arcQ.angle = [] - let lastPoint = fromPoint; - let startIndex = index; - - // console.log({poly, index, count, fromPoint, startIndex}) - // scale speed of first cutting poly since it engages the full bit - let scale = ((isRough || isPocket) && count === 1) ? engageFactor : 1; + let scale = ((isRough || isPocket) && count === 1) ? camFullEngage : 1; + let startIndex = index; + let polyLastPoint = fromPoint; // easeDown only allowed on closed polys (that we can continue around indefinitly) - if (easeDown && poly.isClosed()) { + if (camEaseDown && poly.isClosed()) { let closest = poly.findClosestPointTo(fromPoint); - lastPoint = closest.point; + polyLastPoint = closest.point; startIndex = closest.index; let last = generateEaseDown((point, offset) => { // generate ease-down points - if (offset == 0) camOut(point.clone(), 0, { factor: engageFactor }); + if (offset == 0) camOut(point.clone(), 0, { factor: camFullEngage }); camOut(point.clone(), 1, { factor: scale }); // and pass them to camOut - }, poly, fromPoint, easeAngle); - lastPoint = poly.points[last]; + }, poly, fromPoint, camEaseAngle); + polyLastPoint = poly.points[last]; startIndex = last; } @@ -806,11 +739,11 @@ export function prepEach(widget, settings, print, firstPoint, update) { // console.log({pointA, pointB, indexA, indexB,startIndex}) if (indexA == startIndex) { //if cutFromLast is true, emit a 1 for a cutting move - camOut(pointA.clone(), cutFromLast ? 1 : 0, { factor: engageFactor }); + camOut(pointA.clone(), cutFromLast ? 1 : 0, { factor: camFullEngage }); // if first point, move to and call export function if (arcEnabled) arcQ.push(pointA); } - lastPoint = arcExport(pointB, pointA); + polyLastPoint = arcExport(pointB, pointA); }, !poly.isClosed(), startIndex); // console.log("at end of arcExport",structuredClone(arcQ)); @@ -844,7 +777,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { let radFault = false; if (lr) { let angle = 2 * Math.asin(dist / (2 * lr.r)); - radFault = Math.abs(angle) > arcRes; // enforce arcRes(olution) + radFault = Math.abs(angle) > arcRadians; // enforce arcRadians(olution) } else { // console.log("too much angle") radFault = true; @@ -873,10 +806,10 @@ export function prepEach(widget, settings, print, firstPoint, update) { // check if first angles should have caused radFault let angle = toRadians(arcQ[0].slopeTo(cc).angleDiff(arcQ[1].slopeTo(cc)).angle) - radFault = Math.abs(angle) > arcRes + radFault = Math.abs(angle) > arcRadians if (radFault) { // if so, remove first point - console.log("secondary radfault,",structuredClone(arcQ),{angle,arcRes,a,b}) + console.log("secondary radfault,",structuredClone(arcQ),{angle,arcRadians,a,b}) camOut(arcQ.shift(), 1) } @@ -891,9 +824,9 @@ export function prepEach(widget, settings, print, firstPoint, update) { } // if new point is off the arc // if point is off-center, or too far from center, or too large of a radius - if (dc * arcQ.center.length / arcQ.rSum > arcTolerance || dist > cc.r || cc.r > arcMax || radFault || zFault) { - // let debug = [ dc * arcQ.center.length / arcQ.rSum > arcTolerance, dist > cc.r, cc.r > arcMax, radFault]; - // console.log("point off the arc,",structuredClone(arcQ),radFault,zFault,[dc * arcQ.center.length / arcQ.rSum > arcTolerance , dist > cc.r , cc.r > arcMax]); + if (dc * arcQ.center.length / arcQ.rSum > camArcTolerance || dist > cc.r || cc.r > arcMax || radFault || zFault) { + // let debug = [ dc * arcQ.center.length / arcQ.rSum > camArcTolerance, dist > cc.r, cc.r > arcMax, radFault]; + // console.log("point off the arc,",structuredClone(arcQ),radFault,zFault,[dc * arcQ.center.length / arcQ.rSum > camArcTolerance , dist > cc.r , cc.r > arcMax]); if (arcQ.length === 4) { // not enough points for an arc, drop first point and recalc center camOut(arcQ.shift(), 1); @@ -904,7 +837,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { } else { arcQ.center = [tc]; let angle = 2 * Math.asin(arcQ[1].dist / (2 * tc.r)); - if (Math.abs(angle) > arcRes) { // enforce arcRes on initial angle + if (Math.abs(angle) > arcRadians) { // enforce arcRadians on initial angle camOut(arcQ.shift(), 1); } } @@ -949,7 +882,7 @@ export function prepEach(widget, settings, print, firstPoint, update) { // console.trace("draining") let arcPreviewRes = 64 - if (!arcTolerance) { + if (!camArcTolerance) { return; } @@ -978,42 +911,36 @@ export function prepEach(widget, settings, print, firstPoint, update) { // generate circle // console.log("circle",{from, to,center}); to = forceCircle? from.clone().setZ(to.z) : from - - - let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center }) - .map(applyWidgetMovement); - + let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center }); // console.log({arcPoints}) - camOut(from, 1); camOut(to, gc, { center: center.sub(from), clockwise, arcPoints }); } else { //if a non-circle arc - let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center }) - .map(applyWidgetMovement); + let arcPoints = arcToPath(from, to, arcPreviewRes, { clockwise, center }); // console.log("arc") // first arc point camOut(from, 1); // rest of arc to final point camOut(to, gc, { center: center.sub(from), clockwise, arcPoints }); - lastPoint = to.clone(); + polyLastPoint = to.clone(); } } else { //if q too short, emit as lines for (let rec of arcQ) { camOut(rec, 1); } - lastPoint = arcQ.peek().clone(); + polyLastPoint = arcQ.peek().clone(); } arcQ.length = 0; arcQ.center = undefined; } - if (depthFirst) { + if (camDepthFirst) { newLayer(); } - return lastPoint; + return polyLastPoint; } function depthRoughPath(start, depth, levels, tops, emitter, fit, ease) { @@ -1093,26 +1020,82 @@ export function prepEach(widget, settings, print, firstPoint, update) { fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, !dir, ease); return fromPoint; }, { - weight: innerFirst, + weight: camInnerFirst, swapdir: false }); return start; } - // last layer/move is to zmax - // re-inject that point into the last layer generated - if (lastPoint && newOutput.length) { - let lastLayer = newOutput.filter(layer => Array.isArray(layer)).peek(); - if (Array.isArray(lastLayer)) { - printPoint = lastPoint.clone(); - if (printPoint.z < zmax_outer) printPoint.setZ(zmax_outer); - print.addOutput(lastLayer, printPoint, 0, 0, tool); - // print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool); + console.log({ prep: widget, firstPoint: firstPoint?.clone(), wmx, wmy, printPoint, origin: origin.clone() }); + + // coming from a previous widget, use previous last point as starting point + // make top start offset configurable + printPoint = firstPoint || origin; + + let ops = { + addGCode, + camOut, + depthOutlinePath, + depthRoughPath, + emitDrills, + emitTrace, + newLayer, + pocket, + poly2polyEmit, + polyEmit, + printPoint, + setDrill, + setLasering, + setSpindle, + setTolerance, + setTool, + tip2tipEmit, + widget, + zMax, + zSafe, + }; + + let opSum = 0; + let opTot = widget.camops.map(op => op.weight()).reduce((a, v) => a + v); + + for (let op of widget.camops) { + setTolerance(0); + nextIsMove = true; + currentOp = op.op; + isIndex = currentOp.type === 'index'; + isLathe = currentOp.type === 'lathe'; + isRough = currentOp.type === 'rough'; + isPocket = currentOp.type === 'pocket'; + isContour = currentOp.type === 'contour' || (isPocket && currentOp.contour); + let weight = op.weight(); + newLayer(op.op); + op.prepare(ops, (progress, message) => { + update((opSum + (progress * weight)) / opTot, message || op.type(), message); + }); + opSum += weight; + if (tool && printPoint) { + newLayer(); + if (!isIndex) { + layerPush(printPoint.clone().setZ(stockZClear), 0, 0, tool); + newLayer(); + } } } + + // last layer/move is to zMax + // re-inject that point into the last layer generated + if (printPoint && newOutput.length) { + let lastLayer = newOutput.filter(layer => Array.isArray(layer)).peek(); + if (Array.isArray(lastLayer)) { + if (printPoint.z < stockZClear) printPoint.setZ(stockZClear); + print.addOutput(lastLayer, printPoint, 0, 0, tool); + } + } + // console.log("prepare output", newOutput); // replace output single flattened layer with all points print.output = newOutput; + return printPoint; }; diff --git a/src/kiri/mode/cam/slice.js b/src/kiri/mode/cam/slice.js index 06174696..35fd16b4 100644 --- a/src/kiri/mode/cam/slice.js +++ b/src/kiri/mode/cam/slice.js @@ -296,7 +296,7 @@ export async function cam_slice(settings, widget, onupdate, ondone) { opTot += opList.peek().weight(); } - // determing # of steps and step weighting for progress bar + // determine # of steps and step weighting for progress bar for (let op of activeOps) { if (op.type === '|') { break;