diff --git a/src/kiri-mode/cam/export.js b/src/kiri-mode/cam/export.js index 1de3989f..5f3a1d5e 100644 --- a/src/kiri-mode/cam/export.js +++ b/src/kiri-mode/cam/export.js @@ -22,6 +22,8 @@ CAM.export = function(print, online) { const widget = print.widgets[0]; if (!widget) return; + console.log(print); + const { settings } = print; const { device, tools } = settings; @@ -164,6 +166,7 @@ CAM.export = function(print, online) { } } + function moveTo(out, opt = {}) { let laser = out.type === 'laser'; let newpos = out.point; @@ -251,18 +254,23 @@ CAM.export = function(print, online) { points--; return; } + console.log(out) + let gn; + if ( out.emit >=0 && out.emit <= 3) gn = `G${out.emit}`; + else throw new Error(`malformed emit type ${out.emit}`); let speed = out.speed, - gn = speed ? 'G1' : 'G0', - nl = (compact_output && lastGn === gn) ? [] : [gn], - dx = opt.dx || newpos.x - pos.x, - dy = opt.dy || newpos.y - pos.y, - dz = opt.dz || newpos.z - pos.z, - da = newpos.a != pos.a, - maxf = dz ? maxZd : maxXYd, - feed = Math.min(speed || maxf, maxf), - dist = Math.sqrt(dx * dx + dy * dy + dz * dz), - newFeed = feed && feed !== pos.f; + arc = out.emit == 2 || out.emit == 3, + nl = (compact_output && lastGn === gn) ? [] : [gn], + dx = opt.dx || newpos.x - pos.x, + dy = opt.dy || newpos.y - pos.y, + dz = opt.dz || newpos.z - pos.z, + da = newpos.a != pos.a, + maxf = dz ? maxZd : maxXYd, + feed = Math.min(speed || maxf, maxf), + dist = Math.sqrt(dx * dx + dy * dy + dz * dz), + newFeed = feed && feed !== pos.f; + // drop dup points (all deltas are 0) if (!(dx || dy || dz || da)) { diff --git a/src/kiri-mode/cam/prepare.js b/src/kiri-mode/cam/prepare.js index 34f726ff..d858da17 100644 --- a/src/kiri-mode/cam/prepare.js +++ b/src/kiri-mode/cam/prepare.js @@ -7,6 +7,7 @@ // dep: geo.polygons // dep: kiri.render // dep: kiri-mode.cam.driver +// dep: kiri.print // use: kiri-mode.cam.ops gapp.register("kiri-mode.cam.prepare", (root, exports) => { @@ -18,6 +19,7 @@ const { CAM } = driver; const POLY = polygons; + /** * DRIVER PRINT CONTRACT * @@ -43,18 +45,21 @@ CAM.prepare = async function(widall, settings, update) { 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); - }, { - thin: true, - print: 0, - move: 0x557799, - speed: false, - moves: true, - other: "moving", - action: "milling", - maxspeed: settings.process.camFastFeed || 6000 - }); + return render.path( + output, + (progress, layer) => { + update(0.75 + progress * 0.25, "render", layer); + }, { + thin: true, + print: 0, + move: 0x557799, + speed: false, + moves: true, + other: "moving", + action: "milling", + maxspeed: settings.process.camFastFeed || 6000 + } + ); }; function prepEach(widget, settings, print, firstPoint, update) { @@ -258,11 +263,12 @@ function prepEach(widget, settings, print, firstPoint, update) { /** * @param {Point} point - * @param {number} emit (0=move, !0=filament emit/laser on/cut mode) + * @param {number} emit (0=move, 1=/laser on/cut mode, 2/3= G2/G3 arc) * @param {number} [speed] feed/plunge rate in mm/min * @param {number} [tool] tool number */ - function layerPush(point, emit, speed, tool, type) { + function layerPush(point, emit, speed, tool, options) { + const { type, radius, arcPoints } = options ?? {}; const dz = (point && lastPush && lastPush.point) ? point.z - lastPush.point.z : 0; if (dz < 0 && speed > plungeRate) { speed = plungeRate; @@ -292,9 +298,9 @@ function prepEach(widget, settings, print, firstPoint, update) { if (lasering.flat) { point.z = (stock && stock.z ? stock.z : wztop) + lasering.flatz; } - print.addOutput(layerOut, point, power, speed, tool, 'laser'); + print.addOutput(layerOut, point, power, speed, tool, {type:'laser'}); } else { - print.addOutput(layerOut, point, emit, speed, tool, type); + print.addOutput(layerOut, point, emit, speed, tool, {type, radius, arcPoints}); } lastPush = { point, emit, speed, tool }; return point; @@ -321,12 +327,15 @@ function prepEach(widget, settings, print, firstPoint, update) { function camOut(point, cut,opts) { let { - radius = null, + radius = 0, clockwise = true, + arcPoints = [], moveLen = toolDiamMove, factor = 1, } = opts ?? {} + const isArc = radius > 0; + point = point.clone(); point.x += wmx; point.y += wmy; @@ -360,7 +369,7 @@ function prepEach(widget, settings, print, firstPoint, update) { cut ? 1 : 0, rate, tool, - "lerp" + {type:"lerp"}, ); } } @@ -378,7 +387,7 @@ function prepEach(widget, settings, print, firstPoint, update) { let deltaXY = lastPoint.distTo2D(point), deltaZ = point.z - lastPoint.z, absDeltaZ = Math.abs(deltaZ), - isMove = !cut; + isMove = !(cut || isArc); // drop points too close together if (!isLathe && deltaXY < 0.001 && point.z === lastPoint.z) { @@ -460,15 +469,32 @@ function prepEach(widget, settings, print, firstPoint, update) { } } - // todo synthesize move speed from feed / plunge accordingly - layerOut.mode = currentOp; - layerOut.spindle = spindle; - lastPoint = layerPush( - point, - cut ? 1 : 0, - rate, - tool - ); + + if(isArc) { + console.log("arc recieved"); + layerPush( + point, + clockwise ? 2 : 3, + rate, + tool, + { + radius, + arcPoints + } + ); + }else{ + // for g1 moves + + // TODO: synthesize move speed from feed / plunge accordingly + layerOut.mode = currentOp; + layerOut.spindle = spindle; + lastPoint = layerPush( + point, + cut ? 1 : 0, + rate, + tool + ); + } } /** @@ -768,7 +794,7 @@ function prepEach(widget, settings, print, firstPoint, update) { // if (deem || depm || desp || dc > arcDist || cc.r < arcMin || cc.r > arcMax || dist > cc.r) { if ( desp || dc * arcQ.center.length / arcQ.rSum > arcDist || dist > cc.r || cc.r > arcMax || radFault ) { // let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault]; - console.log("point off the arc,",structuredClone(arcQ)); + // console.log("point off the arc,",structuredClone(arcQ)); if (arcQ.length === 4) { // not enough points for an arc, drop first point and recalc center camOut(arcQ.shift(),1); @@ -856,10 +882,9 @@ function prepEach(widget, settings, print, firstPoint, update) { // XYR form // let pre = `${clockwise? 'G2' : 'G3'} X${to.x.toFixed(decimals)} Y${to.y.toFixed(decimals)} R${cc.r.toFixed(decimals)} `; - camOut(to,1,{radius:cc.r, clockwise}); + camOut(to,1,{radius:cc.r, clockwise, arcPoints:[...arcQ]}); // XYIJ form // let pre = `${clockwise? 'G2' : 'G3'} X${to.x.toFixed(decimals)} Y${to.y.toFixed(decimals)} I${(cc.x - pos.x).toFixed(decimals)} J${(cc.y - pos.y).toFixed(decimals)} E${emit.toFixed(decimals)}`; - // let add = pos.f !== from.speedMMM ? ` E${from.speedMMM}` : ''; // append(`${pre}${add} ; merged=${cl-1} len=${dist.toFixed(decimals)} cp=${cc.x.round(2)},${cc.y.round(2)}`); } else { diff --git a/src/kiri-mode/fdm/prepare.js b/src/kiri-mode/fdm/prepare.js index 7236542e..42ffa6cd 100644 --- a/src/kiri-mode/fdm/prepare.js +++ b/src/kiri-mode/fdm/prepare.js @@ -739,7 +739,7 @@ FDM.prepare = async function(widgets, settings, update) { // outside brim if (firstLayerBrim && !brimHalf) { print.addOutput(tmpout, newPoint(maxx + b, y, z), 0, outputSeekrate, tool); - print.addOutput(tmpout, newPoint(maxx + g, y, z), emit, firstLayerRate, tool).retract = true; + print.addOutput(tmpout, newPoint(maxx + g, y, z), emit, firstLayerRate, tool,{retract: true}) } // inside brim if (firstLayerBrimIn && pads.length > 1) { @@ -752,20 +752,20 @@ FDM.prepare = async function(widgets, settings, update) { if (x1 - x0 > bi * 2) { // over 2x brim so emit two segments print.addOutput(tmpout, newPoint(x1 - g, y, z), 0, outputSeekrate, tool); - print.addOutput(tmpout, newPoint(x1 - bi, y, z), emit, firstLayerRate, tool).retract = true; + print.addOutput(tmpout, newPoint(x1 - bi, y, z), emit, firstLayerRate, tool, {retract: true}); print.addOutput(tmpout, newPoint(x0 + bi, y, z), 0, outputSeekrate, tool); - print.addOutput(tmpout, newPoint(x0 + g, y, z), emit, firstLayerRate, tool).retract = true; + print.addOutput(tmpout, newPoint(x0 + g, y, z), emit, firstLayerRate, tool, {retract: true}); } else if (x1 - x0 > bi / 3) { // over 1/3rd brim length emit single segment print.addOutput(tmpout, newPoint(x1 - g, y, z), 0, outputSeekrate, tool); - print.addOutput(tmpout, newPoint(x0 + g, y, z), emit, firstLayerRate, tool).retract = true; + print.addOutput(tmpout, newPoint(x0 + g, y, z), emit, firstLayerRate, tool, {retract: true}); } } } // outside brim if (firstLayerBrim) { print.addOutput(tmpout, newPoint(minx - b, y, z), 0, outputSeekrate, tool); - print.addOutput(tmpout, newPoint(minx - g, y, z), emit, firstLayerRate, tool).retract = false; + print.addOutput(tmpout, newPoint(minx - g, y, z), emit, firstLayerRate, tool, {retract: true}); } // when there is a printing move between layers // inject non-printing move to after brim to prevent erroneous extrusion diff --git a/src/kiri/print.js b/src/kiri/print.js index 985fe277..bc1237fd 100644 --- a/src/kiri/print.js +++ b/src/kiri/print.js @@ -42,7 +42,8 @@ class Print { this.widget = widget; } - addOutput(array, point, emit, speed, tool, type) { + addOutput(array, point, emit, speed, tool, opts) { + const { type, retract, radius, arcPoints} = opts ?? {}; let { lastPoint, lastEmit, lastOut } = this; // drop duplicates (usually intruced by FDM bisections) if (lastPoint && point && type !== 'lerp') { @@ -55,10 +56,15 @@ class Print { // if (emit && emit < 1) console.log(emit); this.lastPoint = point; this.lastEmit = emit; - this.lastOut = lastOut = new Output(point, emit, speed, tool, type || this.nextType); + this.lastOut = lastOut = new Output(point, emit, speed, tool, { + type: type ?? this.nextType, + radius, + arcPoints, + }); if (tool !== undefined) { this.tools[tool] = true; } + lastOut.retract = retract; lastOut.widget = this.widget; array.push(lastOut); this.nextType = undefined; @@ -554,12 +560,31 @@ class Print { } class Output { - constructor(point, emit, speed, tool, type, radius) { + /** + * Construct a new output element. + * + * in cam, emit is the G code number (G0, G1, G2, G3) + * + * @param {Point} point point to emit, with x, y, and z properties + * @param {number} emit emit (feed for printers, power for lasers, cut for cam) + * @param {number} speed speed in mm/min + * @param {number} tool tool id + * @param {Object} options options object + * @param {string} [options.type] type of point + * @param {number} [options.radius] radius of arc + * @param {Point[]} [options.arcPoints] point based approximation of arc + */ + constructor(point, emit, speed, tool, options) { + + const { type, radius, arcPoints } = (options ?? {}); + //speed, tool, type, radius this.point = point; // point to emit this.emit = emit; // emit (feed for printers, power for lasers, cut for cam) this.speed = speed; this.tool = tool; this.type = type; + this.radius = radius; + this.arcPoints = arcPoints; // this.where = new Error().stack.split("\n"); } diff --git a/src/kiri/render.js b/src/kiri/render.js index 20ef141d..20a1a65a 100644 --- a/src/kiri/render.js +++ b/src/kiri/render.js @@ -44,6 +44,13 @@ function rate_to_color(rate, max) { } }; + /** + * Generate visual representation of a gcode program. + * @param {Output[][]} levels - Array of arrays containing `Output` objects. + * @param {function} update - Called with a completion percentage and the rendered layer. + * @param {{tools: Object, flat: Boolean, thin: Boolean, speed: Boolean, lineWidth: Number, toolMode: Boolean, z: Number, action: String, other: String}} [opts] - Optional parameters. + * @returns {Promise.kiri.Layers[]} - Array of layers. + */ async function path(levels, update, opts = {}) { levels = levels.filter(level => level.length); if (levels.length === 0) { @@ -183,7 +190,8 @@ async function path(levels, update, opts = {}) { return; } - if (lastOut) { + if (lastOut) { // if last outpoint is defined + if (arrowAll || lastOut.emit !== out.emit) { heads.push({p1: lastOutPoint, p2: outPoint}); } @@ -193,7 +201,29 @@ async function path(levels, update, opts = {}) { // Math.abs(op.y - lp.y), // Math.abs(op.z - lp.z)); // if (moved < 0.0001) return; - if (out.emit) { + if( is_cam() && (out.emit == 2 || out.emit == 3 )) { + // cam arc emit + console.log('cam arc render woohoo!',out); + + // checks if a new poly should be started + if (!lastOut.emit || (ckspeed && out.speed !== lastOut.speed) || lastEnd) { + current = newPolygon().setOpen(); + current.push(lastOutPoint); + current.color = color(out); + pushPrint(out.tool, current); + } + + out.arcPoints.forEach(p => { + current.push(p); + }) + + current.push(outPoint); + + } else if (out.emit) { + // explicity G1 in CAM mode + // just a non-move in other modes + + // checks if a new poly should be started if (!lastOut.emit || (ckspeed && out.speed !== lastOut.speed) || lastEnd) { current = newPolygon().setOpen(); current.push(lastOutPoint);