From 3e33e3459809f944db929ff3a26bf7ef6669c7f7 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 20 Feb 2023 16:33:04 -0500 Subject: [PATCH] add lathe leave stock option, fix lathe progression between layers --- notes.md | 1 + src/kiri-mode/cam/client.js | 6 +++ src/kiri-mode/cam/export.js | 5 ++- src/kiri-mode/cam/ops.js | 78 ++++++++++++++++++++++++++---------- src/kiri-mode/cam/prepare.js | 8 ++-- src/kiri-mode/cam/topo4.js | 14 +++++-- 6 files changed, 81 insertions(+), 31 deletions(-) diff --git a/notes.md b/notes.md index db8a9081..4b64d198 100644 --- a/notes.md +++ b/notes.md @@ -68,6 +68,7 @@ # CAM +* `B` roughing leave stock should also increase Z slightly (sqrt(2) * leave) * `B` rapid moves should be max of terrain zmax and last cut layer height (roughing) * `B` in indexed stock mode, stock should rotate with indexing * `B` feed rate for next tool set before tool change (push/pop feed rates?) diff --git a/src/kiri-mode/cam/client.js b/src/kiri-mode/cam/client.js index a5986cc1..cd29cfb4 100644 --- a/src/kiri-mode/cam/client.js +++ b/src/kiri-mode/cam/client.js @@ -1348,6 +1348,8 @@ CAM.init = function(kiri, api) { angle: 'camLatheAngle', rate: 'camLatheSpeed', tolerance: 'camTolerance', + filter: 'camContourFilter', + leave: 'camContourLeave', axis: 'X' }).inputs = { tool: UC.newSelect(LANG.cc_tool, {}, "tools"), @@ -1355,9 +1357,13 @@ CAM.init = function(kiri, api) { sep: UC.newBlank({class:"pop-sep"}), spindle: UC.newInput(LANG.cc_spnd_s, {title:LANG.cc_spnd_l, convert:UC.toInt, show:hasSpindle}), rate: UC.newInput(LANG.cc_feed_s, {title:LANG.cc_feed_l, convert:UC.toInt, units:true}), + sep: UC.newBlank({class:"pop-sep"}), step: UC.newInput(LANG.cc_sovr_s, {title:LANG.cc_sovr_l, convert:UC.toFloat, bound:UC.bound(0.01,10.0)}), angle: UC.newInput(LANG.cc_sang_s, {title:LANG.cc_sang_l, convert:UC.toFloat, bound:UC.bound(0.01,90.0)}), + sep: UC.newBlank({class:"pop-sep"}), tolerance: UC.newInput(LANG.ou_toll_s, {title:LANG.ou_toll_l, convert:UC.toFloat, bound:UC.bound(0,10.0), units:true, round:4}), + leave: UC.newInput(LANG.cf_leav_s, {title:LANG.cf_leav_l, convert:UC.toFloat, bound:UC.bound(0,100)}), + // filter: UC.newRow([ UC.newButton(LANG.filter, contourFilter) ], {class:"ext-buttons f-row"}) }; createPopOp('trace', { diff --git a/src/kiri-mode/cam/export.js b/src/kiri-mode/cam/export.js index 397228d4..95ed0bee 100644 --- a/src/kiri-mode/cam/export.js +++ b/src/kiri-mode/cam/export.js @@ -255,13 +255,14 @@ CAM.export = function(print, online) { 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)) { + if (!(dx || dy || dz || da)) { return; } @@ -285,7 +286,7 @@ CAM.export = function(print, online) { runbox.max.z = Math.max(runbox.max.z, pos.z); nl.append(space).append("Z").append(add0(pos.z * factor)); } - if (newpos.a !== pos.a) { + if (da) { pos.a = newpos.a; nl.append(space).append("A").append(add0(pos.a * factor)); } diff --git a/src/kiri-mode/cam/ops.js b/src/kiri-mode/cam/ops.js index 1741ebcd..2eeb6e14 100644 --- a/src/kiri-mode/cam/ops.js +++ b/src/kiri-mode/cam/ops.js @@ -749,6 +749,30 @@ class OpOutline extends CamOp { } } +function createFilter(op) { + let filter = slices => slices; + if (op.filter) { + try { + const obj = eval(`( ${op.filter.join('\n')} )`); + let idx = 0; + if (obj && obj.slices) { + const nadd = []; + filter = function(slices) { + for (let slice of slices) { + if (obj.slices(slice, idx++)) { + nadd.push(slice); + } + } + return nadd; + }; + } + } catch (e) { + console.log('filter parse error', e, op.filter); + } + } + return filter; +} + class OpContour extends CamOp { constructor(state, op) { super(state, op); @@ -757,26 +781,7 @@ class OpContour extends CamOp { async slice(progress) { let { op, state } = this; let { addSlices } = state; - let filter = slices => slices; - if (op.filter) { - try { - const obj = eval(`( ${op.filter.join('\n')} )`); - let idx = 0; - if (obj && obj.slices) { - const nadd = []; - filter = function(slices) { - for (let slice of slices) { - if (obj.slices(slice, idx++)) { - nadd.push(slice); - } - } - return nadd; - }; - } - } catch (e) { - console.log('filter parse error', e, op.filter); - } - } + let filter = createFilter(op); // we need topo for safe travel moves when roughing and outlining // not generated when drilling-only. then all z moves use bounds max. // also generates x and y contouring when selected @@ -869,6 +874,7 @@ class OpLathe extends CamOp { async slice(progress) { let { op, state } = this; let { addSlices } = state; + let filter = createFilter(op); this.topo = await CAM.Topo4({ op, @@ -877,6 +883,7 @@ class OpLathe extends CamOp { progress(pct, msg); }, ondone: (slices) => { + slices = filter(slices); this.slices = slices; addSlices(slices, false); } @@ -893,6 +900,7 @@ class OpLathe extends CamOp { let toolDiam = new CAM.Tool(settings, op.tool).fluteDiameter(); let stepover = toolDiam * op.step * 2; + let rez = topo.resolution; setTool(op.tool, op.rate, op.plunge); setSpindle(op.spindle); @@ -906,16 +914,42 @@ class OpLathe extends CamOp { continue; } + let last; for (let path of slice.camLines) { + let latent; path.forEachPoint((point, pidx) => { - camOut(point.clone(), pidx > 0, stepover); + if (last) { + const dz = Math.abs(last.z - point.z); + if (dz < rez) { + // latent point should still be included in + // preview b/c arcs would look like straight lines + latent = point.clone(); + return; + } + if (latent) { + camOut(latent, true, stepover); + latent = undefined; + } + } + camOut(last = point.clone(), pidx > 0, stepover); }, false); + if (latent) { + camOut(latent, true, stepover); + } } newLayer(); } - setPrintPoint(printPoint); + // move to safe height and reset A axis + let last = ops.lastPoint(); + let amax = Math.round(last.a / 360) * 360; + camOut(last = last.clone().setZ(zmax), 0); + camOut(last = last.clone().setA(amax), 0); + newLayer(); + ops.addGCode("G92A0"); + + setPrintPoint(last); } } diff --git a/src/kiri-mode/cam/prepare.js b/src/kiri-mode/cam/prepare.js index 0710ee53..652cd151 100644 --- a/src/kiri-mode/cam/prepare.js +++ b/src/kiri-mode/cam/prepare.js @@ -100,6 +100,7 @@ function prepEach(widget, settings, print, firstPoint, update) { layerOut = [], printPoint, isNewMode, + isLathe, tool, toolType, toolDiam, @@ -327,7 +328,7 @@ function prepEach(widget, settings, print, firstPoint, update) { isMove = !cut; // drop points too close together - if (deltaXY < 0.001 && point.z === lastPoint.z) { + if (!isLathe && deltaXY < 0.001 && point.z === lastPoint.z) { // console.trace(["drop dup",lastPoint,point]); return; } @@ -346,7 +347,7 @@ function prepEach(widget, settings, print, firstPoint, update) { // new pos for plunge calc deltaXY = 0; } - } else if (isMove && ['lathe'].contains(currentOp.type)) { + } else if (isMove && isLathe) { if (point.z > lastPoint.z) { layerPush(lastPoint.clone().setZ(point.z), 0, 0, tool.getNumber()); } else if (point.z < lastPoint.z) { @@ -392,7 +393,7 @@ function prepEach(widget, settings, print, firstPoint, update) { } // set new plunge rate - if (!lasering && deltaZ < -tolerance && currentOp.type !== 'lathe') { + if (!lasering && deltaZ < -tolerance && !isLathe) { let threshold = Math.min(deltaXY / 2, absDeltaZ), modifier = threshold / absDeltaZ; if (synthPlunge && threshold && modifier && deltaXY > tolerance) { @@ -465,6 +466,7 @@ function prepEach(widget, settings, print, firstPoint, update) { setTolerance(0); nextIsMove = true; currentOp = op.op; + isLathe = currentOp.type === 'lathe'; let weight = op.weight(); newLayer(op.op); op.prepare(ops, (progress, message) => { diff --git a/src/kiri-mode/cam/topo4.js b/src/kiri-mode/cam/topo4.js index 4720e727..2ae0a228 100644 --- a/src/kiri-mode/cam/topo4.js +++ b/src/kiri-mode/cam/topo4.js @@ -72,6 +72,7 @@ class Topo4 { this.maxo = tool.profileDim.maxo * resolution; this.diam = tool.fluteDiameter(); this.zoff = widget.track.top || 0; + this.leave = op.leave || 0; onupdate(0, "lathe"); @@ -90,6 +91,8 @@ class Topo4 { onupdate(1, "lathe"); ondone(lathe); // ondone([...slices, ...lathe]); + + return this; } async slice(onupdate) { @@ -300,7 +303,7 @@ class Topo4 { } async latheWorker(onupdate) { - const { sliced, tool, zoff } = this; + const { sliced, tool, zoff, leave } = this; const rota = this.angle * DEG2RAD; const steps = (Math.PI * 2) / rota; @@ -335,7 +338,8 @@ class Topo4 { for (let rec of recs) { const { degrees, heights } = rec; [...heights].group(3).forEach((a,i) => { - paths[i].camLines[0].push( newPoint(a[0], a[1], a[2]).setA(degrees) ); + // progress each path 360 degrees to prevent A rolling backwards + paths[i].camLines[0].push( newPoint(a[0], a[1], a[2] + leave).setA(degrees+ i * -360) ); }); } @@ -345,7 +349,9 @@ class Topo4 { console.log('empty', slice); continue; } - slice.camLines[0].push(poly.points[0].clone()); + // repeat first point 360 degrees progressed + const repeat = poly.points[0]; + slice.camLines[0].push(repeat.clone().setA(repeat.a - 360)); slice.output() .setLayer("lathe", { line: 0xffff00 }) .addPoly(poly.clone().applyRotations().move({ z: -zoff, x:0, y:0 })); @@ -407,7 +413,7 @@ function rotatePoints(lines, rot) { new THREE.BufferAttribute(lines, 3).applyMatrix4(rot); } -CAM.Topo4 = async function(opt) { +CAM.Topo4 = function(opt) { return new Topo4().generate(opt); };