move easeDown to prepare function
This commit is contained in:
parent
7fff915392
commit
f703f0cef3
2 changed files with 230 additions and 91 deletions
|
|
@ -1188,74 +1188,7 @@ class Polygon {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ease down along the polygonal path.
|
|
||||||
*
|
|
||||||
* 1. Travel from fromPoint to closest point on polygon, to rampZ above that that point,
|
|
||||||
* 2. ease-down starts, following the polygonal path, decreasing Z at a fixed slope until target Z is hit,
|
|
||||||
* 3. then the rest of the path is completed and repeated at target Z until touchdown point is reached.
|
|
||||||
* 4. this function should probably move to CAM prepare since it's only called from there
|
|
||||||
*/
|
|
||||||
forEachPointEaseDown(fn, fromPoint, degrees = 45) {
|
|
||||||
let index = this.findClosestPointTo(fromPoint).index,
|
|
||||||
fromZ = fromPoint.z,
|
|
||||||
offset = 0,
|
|
||||||
points = this.points,
|
|
||||||
length = points.length,
|
|
||||||
touch = -1, // first point to touch target z
|
|
||||||
targetZ = points[0].z,
|
|
||||||
dist2next,
|
|
||||||
last,
|
|
||||||
next,
|
|
||||||
done;
|
|
||||||
|
|
||||||
// Slope for computations.
|
|
||||||
const slope = Math.tan((degrees * Math.PI) / 180);
|
|
||||||
// Z height above polygon Z from which to start the ease-down.
|
|
||||||
// Machine will travel from "fromPoint" to "nearest point x, y, z' => with z' = point z + rampZ",
|
|
||||||
// then start the ease down along path.
|
|
||||||
const rampZ = 2.0;
|
|
||||||
while (true) {
|
|
||||||
next = points[index % length];
|
|
||||||
if (last && next.z < fromZ) {
|
|
||||||
// When "in Ease-Down" (ie. while target Z not yet reached) - follow path while slowly decreasing Z.
|
|
||||||
let deltaZ = fromZ - next.z;
|
|
||||||
dist2next = last.distTo2D(next);
|
|
||||||
let deltaZFullMove = dist2next * slope;
|
|
||||||
|
|
||||||
if (deltaZFullMove > deltaZ) {
|
|
||||||
// Too long: easing along full path would overshoot depth, synth intermediate point at target Z.
|
|
||||||
//
|
|
||||||
// XXX: please check my super basic trig - this should follow from `last` to `next` up until the
|
|
||||||
// intersect at the target Z distance.
|
|
||||||
fn(last.followTo(next, dist2next * deltaZ / deltaZFullMove).setZ(next.z), offset++);
|
|
||||||
} else {
|
|
||||||
// Ok: execute full move at desired slope.
|
|
||||||
next = next.clone().setZ(fromZ - deltaZFullMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
fromZ = next.z;
|
|
||||||
} else if (offset === 0 && next.z < fromZ) {
|
|
||||||
// First point, move to rampZ height above next.
|
|
||||||
let deltaZ = fromZ - next.z;
|
|
||||||
fromZ = next.z + Math.min(deltaZ, rampZ)
|
|
||||||
next = next.clone().setZ(fromZ);
|
|
||||||
}
|
|
||||||
last = next;
|
|
||||||
fn(next, offset++);
|
|
||||||
if ((index % length) === touch) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (touch < 0 && next.z <= targetZ) {
|
|
||||||
// Save touch-down index so as to be able to "complete" the full cut at target Z,
|
|
||||||
// i.e. keep following the path loop until the touch down point is reached again.
|
|
||||||
touch = ((index + length) % length);
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
forEachPoint(fn, close, start) {
|
forEachPoint(fn, close, start) {
|
||||||
let index = start || 0,
|
let index = start || 0,
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
if (lift) camOut(point.clone().setZ(point.z + lift), 0);
|
if (lift) camOut(point.clone().setZ(point.z + lift), 0);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
camOut(point.clone().setZ(zmax));
|
camOut(point.clone().setZ(zmax),0);
|
||||||
newLayer();
|
newLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,12 +311,22 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* emit a cut or move operation from the current location to a new location
|
* emit a cut or move operation from the current location to a new location
|
||||||
* @param {*} point destination for move
|
* @param {Point} point destination for move
|
||||||
* @param {*} cut 1/true = cut, 0/false = move
|
* @param {1|2} cut 1/true = cut, 0/false = move
|
||||||
* @param {*} moveLen typically = tool diameter used to trigger terrain detection
|
* @param {number} opts.radius arc radius; truthy values for arc move
|
||||||
* @param {*} factor speed scale factor
|
* @param {boolean} opts.clockwise arc direction
|
||||||
|
* @param {number} opts.moveLen typically = tool diameter used to trigger terrain detection
|
||||||
|
* @param {number} opts.factor speed scale factor
|
||||||
*/
|
*/
|
||||||
function camOut(point, cut, moveLen = toolDiamMove, factor = 1) {
|
function camOut(point, cut,opts) {
|
||||||
|
|
||||||
|
let {
|
||||||
|
radius = null,
|
||||||
|
clockwise = true,
|
||||||
|
moveLen = toolDiamMove,
|
||||||
|
factor = 1,
|
||||||
|
} = opts ?? {}
|
||||||
|
|
||||||
point = point.clone();
|
point = point.clone();
|
||||||
point.x += wmx;
|
point.x += wmx;
|
||||||
point.y += wmy;
|
point.y += wmy;
|
||||||
|
|
@ -463,6 +473,7 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* output an array of slices that form a pocket
|
* output an array of slices that form a pocket
|
||||||
|
* used by rough and pocket ops, does not support arcs
|
||||||
*
|
*
|
||||||
* @param {Slice[]} top-down Z stack of slices
|
* @param {Slice[]} top-down Z stack of slices
|
||||||
* @param {*} opts
|
* @param {*} opts
|
||||||
|
|
@ -494,7 +505,7 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) {
|
printPoint = poly2polyEmit(polys, printPoint, function(poly, index, count) {
|
||||||
poly.forEachPoint(function(point, pidx, points, offset) {
|
poly.forEachPoint(function(point, pidx, points, offset) {
|
||||||
// scale speed of first cutting poly since it engages the full bit
|
// 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,{ factor: count === 1 ? engageFactor : 1});
|
||||||
}, poly.isClosed(), index);
|
}, poly.isClosed(), index);
|
||||||
}, { swapdir: false });
|
}, { swapdir: false });
|
||||||
newLayer();
|
newLayer();
|
||||||
|
|
@ -520,6 +531,71 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
printPoint = depthRoughPath(printPoint, 0, outs, otops, polyEmit, false, easeDown);
|
printPoint = depthRoughPath(printPoint, 0, outs, otops, polyEmit, false, easeDown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Ease down along the polygonal path.
|
||||||
|
*
|
||||||
|
* 1. Travel from fromPoint to closest point on polygon, to rampZ above that that point,
|
||||||
|
* 2. ease-down starts, following the polygonal path, decreasing Z at a fixed slope until target Z is hit,
|
||||||
|
*/
|
||||||
|
|
||||||
|
function generateEaseDown(fn,poly, fromPoint, degrees = 45){
|
||||||
|
let index = poly.findClosestPointTo(fromPoint).index,
|
||||||
|
fromZ = fromPoint.z,
|
||||||
|
offset = 0,
|
||||||
|
points = poly.points,
|
||||||
|
length = points.length,
|
||||||
|
touch = -1, // first point to touch target z
|
||||||
|
targetZ = points[0].z,
|
||||||
|
dist2next,
|
||||||
|
last,
|
||||||
|
next,
|
||||||
|
done;
|
||||||
|
|
||||||
|
// Slope for computations.
|
||||||
|
const slope = Math.tan((degrees * Math.PI) / 180);
|
||||||
|
// Z height above polygon Z from which to start the ease-down.
|
||||||
|
// Machine will travel from "fromPoint" to "nearest point x, y, z' => with z' = point z + rampZ",
|
||||||
|
// then start the ease down along path.
|
||||||
|
const rampZ = 2.0;
|
||||||
|
while (true) {
|
||||||
|
next = points[index % length];
|
||||||
|
if (last && next.z < fromZ) {
|
||||||
|
// When "in Ease-Down" (ie. while target Z not yet reached) - follow path while slowly decreasing Z.
|
||||||
|
let deltaZ = fromZ - next.z;
|
||||||
|
dist2next = last.distTo2D(next);
|
||||||
|
let deltaZFullMove = dist2next * slope;
|
||||||
|
|
||||||
|
if (deltaZFullMove > deltaZ) {
|
||||||
|
// Too long: easing along full path would overshoot depth, synth intermediate point at target Z.
|
||||||
|
//
|
||||||
|
// XXX: please check my super basic trig - this should follow from `last` to `next` up until the
|
||||||
|
// intersect at the target Z distance.
|
||||||
|
fn(last.followTo(next, dist2next * deltaZ / deltaZFullMove).setZ(next.z), offset++);
|
||||||
|
} else {
|
||||||
|
// Ok: execute full move at desired slope.
|
||||||
|
next = next.clone().setZ(fromZ - deltaZFullMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
fromZ = next.z;
|
||||||
|
} else if (offset === 0 && next.z < fromZ) {
|
||||||
|
// First point, move to rampZ height above next.
|
||||||
|
let deltaZ = fromZ - next.z;
|
||||||
|
fromZ = next.z + Math.min(deltaZ, rampZ)
|
||||||
|
next = next.clone().setZ(fromZ);
|
||||||
|
}
|
||||||
|
last = next;
|
||||||
|
fn(next, offset++);
|
||||||
|
if (touch < 0 && next.z <= targetZ) {
|
||||||
|
// Save touch-down index so as to be able to "complete" the full cut at target Z,
|
||||||
|
// i.e. keep following the path loop until the touch down point is reached again.
|
||||||
|
touch = ((index + length) % length);
|
||||||
|
break; //break after touch
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return touch;
|
||||||
|
}
|
||||||
|
|
||||||
// coming from a previous widget, use previous last point
|
// coming from a previous widget, use previous last point
|
||||||
lastPoint = firstPoint;
|
lastPoint = firstPoint;
|
||||||
|
|
@ -603,26 +679,156 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
newLayer();
|
newLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare
|
|
||||||
// sliceOutput
|
|
||||||
// depthRoughPath
|
|
||||||
// poly2polyEmit
|
/**
|
||||||
// roughTopEmit
|
* Output a single polygon as gcode. The polygon is walked in either the
|
||||||
// poly2polyEmit
|
* clockwise or counter-clockwise direction depending on the winding of the
|
||||||
|
* polygon. The first point of the polygon is assumed to be the starting
|
||||||
|
* point, and the last point is assumed to be the ending point. If the
|
||||||
|
* polygon is closed, the starting and ending points are the same. The
|
||||||
|
* function will automatically output a rapid move to the first point of
|
||||||
|
* the polygon if that point is not the current position.
|
||||||
|
*
|
||||||
|
* @param {Polygon} poly - the polygon to output
|
||||||
|
* @param {number} index - the index of the polygon in its containing array
|
||||||
|
* @param {number} count - the total number of polygons in the array
|
||||||
|
* @param {Point} fromPoint - the point to rapid move from
|
||||||
|
* @returns {Point} - the last point of the polygon
|
||||||
|
*/
|
||||||
function polyEmit(poly, index, count, fromPoint) {
|
function polyEmit(poly, index, count, fromPoint) {
|
||||||
|
|
||||||
|
console.log('polyEmit', poly, index, count);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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 (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 last = null;
|
let last = null;
|
||||||
// scale speed of first cutting poly since it engages the full bit
|
// 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) ? engageFactor : 1;
|
||||||
|
|
||||||
if (easeDown && poly.isClosed()) {
|
if (easeDown && poly.isClosed()) {
|
||||||
last = poly.forEachPointEaseDown(function(point, offset) {
|
last = generateEaseDown((point, pidx, points, offset)=>{
|
||||||
camOut(point.clone(), offset > 0, undefined, scale);
|
|
||||||
}, fromPoint, easeAngle);
|
camOut(point.clone(), 1, {factor:scale});
|
||||||
} else {
|
}, poly, fromPoint, easeAngle);
|
||||||
poly.forEachPoint(function(point, pidx, points, offset) {
|
}
|
||||||
last = point;
|
console.log(last,last)
|
||||||
camOut(point.clone(), offset !== 0, undefined, scale);
|
|
||||||
}, poly.isClosed(), index);
|
poly.forEachPoint(function(point, pidx, points, offset) {
|
||||||
}
|
last = point;
|
||||||
|
camOut(point.clone(), offset !== 0, {factor:scale});
|
||||||
|
}, poly.isClosed(), last);
|
||||||
|
|
||||||
|
|
||||||
newLayer();
|
newLayer();
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
@ -715,7 +921,7 @@ function prepEach(widget, settings, print, firstPoint, update) {
|
||||||
print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool);
|
print.addOutput(lastLayer, printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log("prepare output", newOutput);
|
||||||
// replace output single flattened layer with all points
|
// replace output single flattened layer with all points
|
||||||
print.output = newOutput;
|
print.output = newOutput;
|
||||||
return printPoint;
|
return printPoint;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue