From bdf131109c2471a3be45762bb396d58f63ca2b94 Mon Sep 17 00:00:00 2001 From: bakedpotatolord Date: Fri, 23 May 2025 21:59:32 -0600 Subject: [PATCH] get first version of the arc que operational --- src/kiri-mode/cam/prepare.js | 294 ++++++++++++++++++++++------------- 1 file changed, 182 insertions(+), 112 deletions(-) diff --git a/src/kiri-mode/cam/prepare.js b/src/kiri-mode/cam/prepare.js index 99ccec03..d2d44193 100644 --- a/src/kiri-mode/cam/prepare.js +++ b/src/kiri-mode/cam/prepare.js @@ -11,7 +11,7 @@ gapp.register("kiri-mode.cam.prepare", (root, exports) => { const { base, kiri } = root; -const { paths, polygons, newPoint } = base; +const { paths, polygons, newPoint, util} = base; const { tip2tipEmit, poly2polyEmit } = paths; const { driver, render } = kiri; const { CAM } = driver; @@ -312,7 +312,7 @@ function prepEach(widget, settings, print, firstPoint, update) { /** * emit a cut or move operation from the current location to a new location * @param {Point} point destination for move - * @param {1|2} cut 1/true = cut, 0/false = move + * @param {1|0|boolean} cut 1/true = cut, 0/false = move * @param {number} opts.radius arc radius; truthy values for arc move * @param {boolean} opts.clockwise arc direction * @param {number} opts.moveLen typically = tool diameter used to trigger terrain detection @@ -699,115 +699,182 @@ function prepEach(widget, settings, print, firstPoint, update) { */ function polyEmit(poly, index, count, fromPoint) { - console.log('polyEmit', poly, index, count); + const arcDist = 1, + arcRes = 2, //2 degs max + arcMax = Infinity; // no max arc radius - // if (fromPoint && out.emit) { - // if (arcDist) { - // let rec = {e:out.emit, x, y, z, dist, emitPerMM, speedMMM}; - // arcQ.push(rec); - // let deem = false; // do arcQ[0] and rec have differing emit values? - // let depm = false; // do arcQ[0] and rec have differing emit speeds? - // let desp = false; // do arcQ[0] and rec have differing move speeds? - // if (arcQ.length > 1) { - // let el = arcQ.length; - // deem = arcQ[0].e !== rec.e; - // depm = arcQ[0].emitPerMM !== rec.emitPerMM; - // desp = arcQ[0].speedMMM !== rec.speedMMM; - // } - // // ondebug({arcQ}); - // if (arcQ.length > 2) { - // let el = arcQ.length; - // let e1 = arcQ[0]; // first in arcQ - // let e2 = arcQ[Math.floor(el/2)]; // mid in arcQ - // let e3 = arcQ[el-1]; // last in arcQ - // let e4 = arcQ[el-2]; // second last in arcQ - // let e5 = arcQ[el-3]; // third last in arcQ - // let cc = util.center2d(e1, e2, e3, 1); // find center - // let lr = util.center2d(e3, e4, e5, 1); // find local radius - // let dc = 0; + // console.log('polyEmit', poly, index, count); - // let radFault = false; - // if (lr) { - // let angle = 2 * Math.asin(dist/(2*lr.r)); - // radFault = Math.abs(angle) > Math.PI * 2 / arcRes; // enforce arcRes(olution) - // // if (arcQ.center) { - // // arcQ.rSum = arcQ.center.reduce( function (t, v) { return t + v.r }, 0 ); - // // let avg = arcQ.rSum / arcQ.center.length; - // // radFault = radFault || Math.abs(avg - lr.r) / avg > arcDev; // eliminate sharps and flats when local rad is out of arcDev(iation) - // // } - // } else { - // radFault = true; - // } + let arcQ = []; - // if (cc) { - // if ([cc.x,cc.y,cc.z,cc.r].hasNaN()) { - // console.log({cc, e1, e2, e3}); - // } - // if (arcQ.length === 3) { - // arcQ.center = [ cc ]; - // arcQ.xSum = cc.x; - // arcQ.ySum = cc.y; - // arcQ.rSum = cc.r; - // } else { - // // check center point delta - // arcQ.xSum = arcQ.center.reduce( function (t, v) { return t + v.x }, 0 ); - // arcQ.ySum = arcQ.center.reduce( function (t, v) { return t + v.y }, 0 ); - // arcQ.rSum = arcQ.center.reduce( function (t, v) { return t + v.r }, 0 ); - // let dx = cc.x - arcQ.xSum / arcQ.center.length; - // let dy = cc.y - arcQ.ySum / arcQ.center.length; - // dc = Math.sqrt(dx * dx + dy * dy); - // } + function arcExport(point,lastp){ - // // if new point is off the arc - // // if (deem || depm || desp || dc > arcDist || cc.r < arcMin || cc.r > arcMax || dist > cc.r) { - // if (deem || depm || desp || dc * arcQ.center.length / arcQ.rSum > arcDist || dist > cc.r || cc.r > arcMax || radFault || !arcValid()) { - // // let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault]; - // if (arcQ.length === 4) { - // // not enough points for an arc, drop first point and recalc center - // emitQrec(arcQ.shift()); - // let tc = util.center2d(arcQ[0], arcQ[1], arcQ[2], 1); - // // the new center is invalid as well. drop the first point - // if (!tc) { - // emitQrec(arcQ.shift()); - // } else { - // arcQ.center = [ tc ]; - // let angle = 2 * Math.asin(arcQ[1].dist/(2*tc.r)); - // if (Math.abs(angle) > Math.PI * 2 / arcRes) { // enforce arcRes on initial angle - // emitQrec(arcQ.shift()); - // } - // } - // } else { - // // enough to consider an arc, emit and start new arc - // let defer = arcQ.pop(); - // drainQ(); - // // re-add point that was off the last arc - // arcQ.push(defer); - // } - // } else { - // // new point is on the arc - // arcQ.center.push(cc); - // } - // } else { - // // drainQ on invalid center - // drainQ(); - // } - // } - // } else { - // // emitMM = emitPerMM * out.emit * dist; - // emitMM = extrudeMM(dist, emitPerMM, out.emit); - // moveTo({x:x, y:y, e:emitMM}, speedMMM); - // emitted += emitMM; - // } - // } else { - // drainQ(); - // moveTo({x:x, y:y}, seekMMM); - // // TODO disabling out of plane z moves until a better mechanism - // // can be built that doesn't rely on computed zpos from layer heights... - // // when making z moves (like polishing) allow slowdown vs fast seek - // // let moveSpeed = (lastp && lastp.z !== z) ? speedMMM : seekMMM; - // // moveTo({x:x, y:y, z:z}, moveSpeed); - // } + let dist = lastp? point.distTo2D(lastp) : 0; + if (lastp) { + if (arcDist) { + let rec = Object.assign(point,{dist}); + arcQ.push(rec); + let desp = false; // do arcQ[0] and rec have differing move speeds? + if (arcQ.length > 1) { + let el = arcQ.length; + desp = arcQ[0].speedMMM !== rec.speedMMM; + } + // ondebug({arcQ}); + if (arcQ.length > 2) { + let el = arcQ.length; + let e1 = arcQ[0]; // first in arcQ + let e2 = arcQ[Math.floor(el/2)]; // mid in arcQ + let e3 = arcQ[el-1]; // last in arcQ + let e4 = arcQ[el-2]; // second last in arcQ + let e5 = arcQ[el-3]; // third last in arcQ + let cc = util.center2d(e1, e2, e3, 1); // find center + let lr = util.center2d(e3, e4, e5, 1); // find local radius + let dc = 0; + + let radFault = false; + if (lr) { + let angle = 2 * Math.asin(dist/(2*lr.r)); + radFault = Math.abs(angle) > Math.PI * 2 / arcRes; // enforce arcRes(olution) + // if (arcQ.center) { + // arcQ.rSum = arcQ.center.reduce( function (t, v) { return t + v.r }, 0 ); + // let avg = arcQ.rSum / arcQ.center.length; + // radFault = radFault || Math.abs(avg - lr.r) / avg > arcDev; // eliminate sharps and flats when local rad is out of arcDev(iation) + // } + } else { + radFault = true; + } + + if (cc) { + if ([cc.x,cc.y,cc.z,cc.r].hasNaN()) { + console.log({cc, e1, e2, e3}); + } + if (arcQ.length === 3) { + arcQ.center = [ cc ]; + arcQ.xSum = cc.x; + arcQ.ySum = cc.y; + arcQ.rSum = cc.r; + } else { + // check center point delta + arcQ.xSum = arcQ.center.reduce( function (t, v) { return t + v.x }, 0 ); + arcQ.ySum = arcQ.center.reduce( function (t, v) { return t + v.y }, 0 ); + arcQ.rSum = arcQ.center.reduce( function (t, v) { return t + v.r }, 0 ); + let dx = cc.x - arcQ.xSum / arcQ.center.length; + let dy = cc.y - arcQ.ySum / arcQ.center.length; + dc = Math.sqrt(dx * dx + dy * dy); + } + + // if new point is off the arc + // 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)); + if (arcQ.length === 4) { + // not enough points for an arc, drop first point and recalc center + camOut(arcQ.shift(),1); + let tc = util.center2d(arcQ[0], arcQ[1], arcQ[2], 1); + // the new center is invalid as well. drop the first point + if (!tc) { + camOut(arcQ.shift(),1); + } else { + arcQ.center = [ tc ]; + let angle = 2 * Math.asin(arcQ[1].dist/(2*tc.r)); + if (Math.abs(angle) > Math.PI * 2 / arcRes) { // enforce arcRes on initial angle + camOut(arcQ.shift(),1); + } + } + } else { + // enough to consider an arc, emit and start new arc + let defer = arcQ.pop(); + drainQ(); + // re-add point that was off the last arc + arcQ.push(defer); + } + } else { + // new point is on the arc + arcQ.center.push(cc); + } + } else { + // drainQ on invalid center + drainQ(); + } + } + } else { + // emitMM = emitPerMM * out.emit * dist; + emitMM = extrudeMM(dist, emitPerMM, out.emit); + camOut({x, y, e:emitMM}, true,); + emitted += emitMM; + } + } else { + // if no last point, emit and set + drainQ(); + camOut({x, y},1 ); + // TODO disabling out of plane z moves until a better mechanism + // can be built that doesn't rely on computed zpos from layer heights... + // when making z moves (like polishing) allow slowdown vs fast seek + // let moveSpeed = (lastp && lastp.z !== z) ? speedMMM : seekMMM; + // moveTo({x:x, y:y, z:z}, moveSpeed); + } + return point; + } + + + + function drainQ() { + + console.log("drainQ called",structuredClone(arcQ)); + + if (!arcDist) { + return; + } + if (arcQ.length > 4) { + // ondebug({arcQ}); + let vec1 = new THREE.Vector2(arcQ[1].x - arcQ[0].x, arcQ[1].y - arcQ[0].y); + let vec2 = new THREE.Vector2(arcQ.center[0].x - arcQ[0].x, arcQ.center[0].y - arcQ[0].y); + let clockwise = vec1.cross(vec2) < 0 ? 'G2' : 'G3'; + let from = arcQ[0]; + let to = arcQ.peek(); + arcQ.xSum = arcQ.center.reduce( (t, v) => t + v.x , 0 ); + arcQ.ySum = arcQ.center.reduce( (t, v) => t + v.y , 0 ); + arcQ.rSum = arcQ.center.reduce( (t, v) => t + v.r , 0 ); + let cl = arcQ.center.length; + let cc; + + let angle = util.thetaDiff( + Math.atan2((from.y - arcQ.ySum / cl), (from.x - arcQ.xSum / cl)), + Math.atan2((to.y - arcQ.ySum / cl), (to.x - arcQ.xSum / cl)), + clockwise + ); + + if (Math.abs(angle) <= 3 * Math.PI / 4) { + cc = util.center2pr(from, to, arcQ.rSum / cl, !clockwise); + } + + if (!cc) { + cc = {x:arcQ.xSum/cl, y:arcQ.ySum/cl, z:arcQ[0].z, r:arcQ.rSum/cl}; + } + + // first arc point + camOut(from,true); + // rest of arc to final point + + // 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}); + // 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 { + //if q too short, emit as lines + for (let rec of arcQ) { + camOut(rec,1); + } + } + arcQ.length = 0; + arcQ.center = undefined; + } @@ -815,19 +882,22 @@ function prepEach(widget, settings, print, firstPoint, update) { // scale speed of first cutting poly since it engages the full bit let scale = ((isRough || isPocket) && count === 1) ? engageFactor : 1; - if (easeDown && poly.isClosed()) { - last = generateEaseDown((point, pidx, points, offset)=>{ - - camOut(point.clone(), 1, {factor:scale}); + if (easeDown && poly.isClosed()) { //if doing ease-down + last = generateEaseDown((point, )=>{ //generate ease-down points + camOut(point.clone(), 1, {factor:scale}); // and pass them to camOut }, poly, fromPoint, easeAngle); } - console.log(last,last) poly.forEachPoint(function(point, pidx, points, offset) { + arcExport(point,last) + // camOut(point.clone(), offset !== 0, {factor:scale}); last = point; - camOut(point.clone(), offset !== 0, {factor:scale}); }, poly.isClosed(), last); + // console.log("at end of arcExport",structuredClone(arcQ)); + drainQ(); + // console.log("at end of arcExport",structuredClone(arcQ)); + newLayer(); return last;