checkpoint fdm arc conversion
This commit is contained in:
parent
989997c9ee
commit
29e303b7f9
8 changed files with 106 additions and 341 deletions
|
|
@ -261,7 +261,9 @@ export const conf = {
|
|||
// process defaults FDM:Process
|
||||
p:{
|
||||
antiBacklash: 0,
|
||||
arcTolerance: 0,
|
||||
fdmArcEnabled: false,
|
||||
fdmArcResolution: 1,
|
||||
fdmArcTolerance: 0.005,
|
||||
firstLayerBedTemp: 0,
|
||||
firstLayerBeltBump: 0,
|
||||
firstLayerBeltLead: 3,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { arcToPath } from '../../geo/paths.js';
|
|||
import { consts } from './consts.js';
|
||||
import { createVM } from '../../moto/quickjs.js';
|
||||
import { newPoint } from '../../geo/point.js';
|
||||
import { newPolygon } from '../../geo/polygon.js';
|
||||
import { util } from '../../geo/base.js';
|
||||
|
||||
const { numOrDefault } = util;
|
||||
|
|
@ -42,7 +43,7 @@ class Print {
|
|||
* addOutput - add a new point to the output gcode array
|
||||
* @param {any[]} array - the output gcode array
|
||||
* @param {Point} point - the new point
|
||||
* @param {number} emit - the extrusion value
|
||||
* @param {number} emit - the extrusion value (G? value for cnc)
|
||||
* @param {number} speed - the feed rate
|
||||
* @param {string} tool - the tool id
|
||||
* @param {"lerp"|string} opts.type - the output type
|
||||
|
|
@ -52,7 +53,7 @@ class Print {
|
|||
* @return {Output} - the new output object
|
||||
*/
|
||||
addOutput(array, point, emit, speed, tool, opts) {
|
||||
const { type, retract, center, arcPoints} = opts ?? {};
|
||||
const { type, retract, center, arcPoints, clockwise, distance } = opts ?? {};
|
||||
let { lastPoint, lastEmit, lastOut } = this;
|
||||
let arc = emit == 2 || emit == 3;
|
||||
// drop duplicates (usually intruced by FDM bisections)
|
||||
|
|
@ -69,7 +70,9 @@ class Print {
|
|||
this.lastOut = lastOut = new Output(point, emit, speed, tool, {
|
||||
type: type ?? this.nextType,
|
||||
center,
|
||||
clockwise,
|
||||
arcPoints,
|
||||
distance
|
||||
});
|
||||
if (tool !== undefined) {
|
||||
this.tools[tool] = true;
|
||||
|
|
@ -130,10 +133,9 @@ class Print {
|
|||
perimeter = poly.perimeter(),
|
||||
close = !options.open,
|
||||
scarf = !poly.open ? (options.scarf ?? 0) : false,
|
||||
tool = options.tool,
|
||||
zmax = options.zmax,
|
||||
last = startPoint,
|
||||
first = true;
|
||||
first = true,
|
||||
{ arcOpts, tool, zmax } = options;
|
||||
|
||||
// if short, use calculated print speed based on sliding scale
|
||||
if (perimeter < process.outputShortPoly) {
|
||||
|
|
@ -193,6 +195,9 @@ class Print {
|
|||
}
|
||||
pp.push(...esp);
|
||||
scarf = true;
|
||||
} else if (arcOpts) {
|
||||
// annotate arc points
|
||||
pp = newPolygon(pp).detectArcs(arcOpts).points;
|
||||
}
|
||||
|
||||
// scarf manages its own close point
|
||||
|
|
@ -201,8 +206,34 @@ class Print {
|
|||
}
|
||||
|
||||
let lpo;
|
||||
let arcInfo;
|
||||
let arcDist = 0;
|
||||
for (let point of pp) {
|
||||
let { arc } = point;
|
||||
if (arc) {
|
||||
arcInfo = arc;
|
||||
// non-emit arc points get -1 for rendering
|
||||
scope.addOutput(output, point, -1, moveSpeed, tool);
|
||||
arcDist += last.distTo2D(point);
|
||||
last = point;
|
||||
continue;
|
||||
} else if (arcInfo) {
|
||||
arcDist += last.distTo2D(point);
|
||||
if (arcInfo.skip-- === 0) {
|
||||
// last point of arc is arcTo?
|
||||
arcInfo.distance = arcDist;
|
||||
scope.addOutput(output, point, shellMult, moveSpeed, tool, arcInfo);
|
||||
arcInfo = undefined;
|
||||
arcDist = 0;
|
||||
} else {
|
||||
// non-emit arc points get -1 for rendering
|
||||
scope.addOutput(output, point, -1, moveSpeed, tool);
|
||||
}
|
||||
last = point;
|
||||
continue;
|
||||
}
|
||||
if (point.skip && lpo?.skip) {
|
||||
// basic thin wall skips are converted to moves
|
||||
scope.addOutput(output, point, 0, moveSpeed, tool);
|
||||
} else if (first) {
|
||||
// if (point.skip) console.log({ skip: point });
|
||||
|
|
@ -746,15 +777,16 @@ class Output {
|
|||
*/
|
||||
constructor(point, emit, speed, tool, options) {
|
||||
|
||||
const { type, center, arcPoints } = (options ?? {});
|
||||
//speed, tool, type, center, arcPoints
|
||||
const { type, center, arcPoints, clockwise, distance } = (options ?? {});
|
||||
|
||||
this.point = point;
|
||||
this.emit = Number(emit); //convert bools into 0/1
|
||||
this.speed = speed;
|
||||
this.tool = tool;
|
||||
this.type = type;
|
||||
this.center = center;
|
||||
this.arcPoints = arcPoints;
|
||||
this.clockwise = clockwise;
|
||||
this.distance = distance;
|
||||
// this.where = new Error().stack.split("\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,10 +57,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
retSpeed = process.outputRetractSpeed * 60 || 1, // range
|
||||
retDwell = process.outputRetractDwell || 0, // range
|
||||
scarfZ = process.outputScarfLength ? true : false,
|
||||
arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0),
|
||||
arcRes = 20,
|
||||
arcDev = 0.5,
|
||||
arcMax = 40,
|
||||
originCenter = device.originCenter || bedRound,
|
||||
offset = originCenter ? {
|
||||
x: 0,
|
||||
|
|
@ -119,7 +115,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
bcos = Math.cos(Math.PI / 4),
|
||||
icos = 1 / bcos,
|
||||
inloops = 0,
|
||||
arcQ = [],
|
||||
minz = { x: Infinity, y: Infinity, z: Infinity },
|
||||
// lenghts of each filament (by nozzle) consumed
|
||||
segments = [];
|
||||
|
|
@ -411,7 +406,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
if (!retracted) {
|
||||
return;
|
||||
}
|
||||
drainQ();
|
||||
// console.log({engage:zhop});
|
||||
// when enabled, resume previous Z
|
||||
if (zhop && pos.z != zpos) moveTo({z:zpos}, seekMMM, "z-hop end");
|
||||
|
|
@ -445,8 +439,10 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
moveTo(savePos, rate, comment);
|
||||
}
|
||||
|
||||
function moveTo(newpos, rate, comment) {
|
||||
let o = [!rate && !newpos.e ? 'G0' : 'G1'];
|
||||
function moveTo(newpos, rate, comment, arc) {
|
||||
let o = arc ?
|
||||
[arc.clockwise ? 'G2' : 'G3'] :
|
||||
[!rate && !newpos.e ? 'G0' : 'G1'];
|
||||
let emit = { x: false, y: false, z: false };
|
||||
if (typeof newpos.x === 'number' && newpos.x !== pos.x) {
|
||||
pos.x = newpos.x;
|
||||
|
|
@ -473,6 +469,11 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
if (emit.x) o.append(axis.X).append(epos.x.toFixed(decimals));
|
||||
if (emit.y) o.append(axis.Y).append(epos.y.toFixed(decimals));
|
||||
if (emit.z) o.append(axis.Z).append(epos.z.toFixed(decimals));
|
||||
if (arc) {
|
||||
let { x, y } = arc.center;
|
||||
o.push(' I', (x + offset.x - pos.x).toFixed(decimals));
|
||||
o.push(' J', (y + offset.y - pos.y).toFixed(decimals));
|
||||
}
|
||||
if (debug) {
|
||||
if (emit.x) minz.x = Math.min(minz.x, epos.x);
|
||||
if (emit.y) minz.y = Math.min(minz.y, epos.y);
|
||||
|
|
@ -608,36 +609,39 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
// iterate through layer outputs
|
||||
for (pidx=0; pidx<path.length; pidx++) {
|
||||
out = path[pidx];
|
||||
speedMMM = (out.speed || process.outputFeedrate) * 60; // range
|
||||
|
||||
let { center, emit, point, speed, type, widget } = out;
|
||||
|
||||
speedMMM = (speed || process.outputFeedrate) * 60; // range
|
||||
if (!isBelt) {
|
||||
fanSpeed = out.fan ?? fanSpeed;
|
||||
}
|
||||
|
||||
// hint to controller that we're working on a specific object
|
||||
// so that gcode between start/stop comments can be cancelled
|
||||
if (out.widget !== cwidget) {
|
||||
if (widget !== cwidget) {
|
||||
if (cwidget) {
|
||||
append(`; end object id: ${cwidget.track.grid_id}`);
|
||||
isBambu && append('M625');
|
||||
}
|
||||
if (out.widget) {
|
||||
let off = model_labels.indexOf(out.widget.track.grid_id);
|
||||
if (widget) {
|
||||
let off = model_labels.indexOf(widget.track.grid_id);
|
||||
let b64 = encodeBitOffset(off);
|
||||
append(`; start object id: ${out.widget.track.grid_id}`);
|
||||
append(`; start object id: ${widget.track.grid_id}`);
|
||||
isBambu && append(`M624 ${b64}`);
|
||||
subst.oid = out.widget.track.grid_id;
|
||||
subst.oname = out.widget.meta.file;
|
||||
subst.oid = widget.track.grid_id;
|
||||
subst.oname = widget.meta.file;
|
||||
}
|
||||
cwidget = out.widget;
|
||||
cwidget = widget;
|
||||
}
|
||||
|
||||
// emit comment on output type chage
|
||||
if (last && out.type !== last.type) {
|
||||
subst.feature = out.type;
|
||||
if (last && type !== last.type) {
|
||||
subst.feature = type;
|
||||
if (gcodeFeature && gcodeFeature.length) {
|
||||
appendAllSub(gcodeFeature);
|
||||
} else {
|
||||
append(`; feature ${out.type}`);
|
||||
append(`; feature ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -659,14 +663,14 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
}
|
||||
|
||||
// if no point in output, it's a dwell command
|
||||
if (!out.point) {
|
||||
if (!point) {
|
||||
dwell(out.speed);
|
||||
continue;
|
||||
}
|
||||
|
||||
let x = out.point.x + offset_x,
|
||||
y = out.point.y + offset_y,
|
||||
z = out.point.z;
|
||||
let x = point.x + offset_x,
|
||||
y = point.y + offset_y,
|
||||
z = point.z;
|
||||
|
||||
// adjust for inversions and origin offsets
|
||||
if (process.outputInvertX) x = -x;
|
||||
|
|
@ -676,10 +680,24 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
y += offset.y;
|
||||
}
|
||||
|
||||
dist = lastp ? lastp.distTo2D(out.point) : 0;
|
||||
// G2,G3 arcs
|
||||
if (center) {
|
||||
emitMM = extrudeMM(out.distance, emitPerMM, emit);
|
||||
moveTo({ x, y, e: emitMM }, speedMMM, 'arc', {
|
||||
clockwise: out.clockwise,
|
||||
center
|
||||
});
|
||||
emitted += emitMM;
|
||||
continue;
|
||||
} else if (emit === -1) {
|
||||
// drop display only
|
||||
continue;
|
||||
}
|
||||
|
||||
dist = lastp ? lastp.distTo2D(point) : 0;
|
||||
|
||||
// re-engage post-retraction before new extrusion
|
||||
if (out.emit && retracted) {
|
||||
if (emit && retracted) {
|
||||
unretract();
|
||||
}
|
||||
|
||||
|
|
@ -692,108 +710,16 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
lastFanSpeed = fanSpeed;
|
||||
}
|
||||
|
||||
if (lastp && 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 * 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();
|
||||
}
|
||||
}
|
||||
if (lastp && emit) {
|
||||
// emitMM = emitPerMM * emit * dist;
|
||||
emitMM = extrudeMM(dist, emitPerMM, emit);
|
||||
if (scarfZ) {
|
||||
moveTo({x, y, z:z + path.height/2, e:emitMM}, speedMMM);
|
||||
} else {
|
||||
// emitMM = emitPerMM * out.emit * dist;
|
||||
emitMM = extrudeMM(dist, emitPerMM, out.emit);
|
||||
if (scarfZ) {
|
||||
moveTo({x, y, z:z + path.height/2, e:emitMM}, speedMMM);
|
||||
} else {
|
||||
moveTo({x, y, e:emitMM}, speedMMM);
|
||||
}
|
||||
emitted += emitMM;
|
||||
moveTo({x, y, e:emitMM}, speedMMM);
|
||||
}
|
||||
emitted += emitMM;
|
||||
} else {
|
||||
drainQ();
|
||||
if (scarfZ) {
|
||||
moveTo({x, y, z:z + path.height/2}, speedMMM);
|
||||
} else {
|
||||
|
|
@ -808,7 +734,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
|
||||
// retract filament if point retract flag set
|
||||
if (out.retract) {
|
||||
drainQ();
|
||||
retract(zhop);
|
||||
}
|
||||
|
||||
|
|
@ -829,8 +754,8 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
lastProgress = progress;
|
||||
}
|
||||
|
||||
lastp = out.point;
|
||||
laste = out.emit;
|
||||
lastp = point;
|
||||
laste = emit;
|
||||
}
|
||||
layer++;
|
||||
if (cwidget) {
|
||||
|
|
@ -843,7 +768,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
while (endloop-- > 0) {
|
||||
append(`M808`);
|
||||
}
|
||||
drainQ();
|
||||
|
||||
print.total_time -= path.print_time;
|
||||
subst.remain_time = Math.ceil(print.total_time / 60);
|
||||
|
|
@ -856,126 +780,6 @@ export function fdm_export(print, online, ondone, ondebug) {
|
|||
emitted += emitMM;
|
||||
}
|
||||
|
||||
function drainQ() {
|
||||
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 gc = vec1.cross(vec2) < 0 ? 'G2' : 'G3';
|
||||
let from = arcQ[0];
|
||||
let to = arcQ.peek();
|
||||
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 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)),
|
||||
gc === "G2"
|
||||
);
|
||||
|
||||
if (Math.abs(angle) <= 3 * Math.PI / 4) {
|
||||
cc = util.center2pr(from, to, arcQ.rSum / cl, gc === "G3");
|
||||
}
|
||||
|
||||
if (!cc) {
|
||||
cc = {x:arcQ.xSum/cl, y:arcQ.ySum/cl, z:arcQ[0].z, r:arcQ.rSum/cl};
|
||||
}
|
||||
|
||||
// first arc point
|
||||
emitQrec(from);
|
||||
// console.log(arcQ.slice(), arcQ.center);
|
||||
// console.log({first: from, last: arcQ.peek(), center: cc});
|
||||
// rest of arc to final point
|
||||
let dist = arcQ.slice(1).map(v => v.dist).reduce((a,v) => a+v);
|
||||
let emit = from.e;//arcQ.slice(1).map(v => v.e).reduce((a,v) => a+v);
|
||||
emit = (from.emitPerMM * emit * dist);
|
||||
outputLength += emit;
|
||||
emitted += emit;
|
||||
if (extrudeAbs) {
|
||||
emit = outputLength;
|
||||
}
|
||||
// XYR form
|
||||
// let pre = `${gc} X${to.x.toFixed(decimals)} Y${to.y.toFixed(decimals)} R${cc.r.toFixed(decimals)} E${emit.toFixed(decimals)}`;
|
||||
// XYIJ form
|
||||
let pre = `${gc} 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)}`);
|
||||
pos.x = to.x;
|
||||
pos.y = to.y;
|
||||
pos.z = to.z;
|
||||
} else {
|
||||
for (let rec of arcQ) {
|
||||
emitQrec(rec);
|
||||
}
|
||||
}
|
||||
arcQ.length = 0;
|
||||
arcQ.center = undefined;
|
||||
}
|
||||
|
||||
// comprehensive arc validator
|
||||
function arcValid() {
|
||||
if (arcQ.length < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let globalCenters = []; // see how a point first the curve within the context of the arc's end points.
|
||||
|
||||
for (let i = 0; i < arcQ.length - 2; i++) {
|
||||
let cc = util.center2d(arcQ[0], arcQ[i+1], arcQ[arcQ.length - 1], 1);
|
||||
if (!cc) {
|
||||
return false;
|
||||
}
|
||||
globalCenters.push(cc);
|
||||
}
|
||||
|
||||
let ac = { // average center
|
||||
x:globalCenters.reduce( (t,v) => t + v.x , 0) / (globalCenters.length),
|
||||
y:globalCenters.reduce( (t,v) => t + v.y , 0) / (globalCenters.length),
|
||||
z:globalCenters.reduce( (t,v) => t + v.z , 0) / (globalCenters.length),
|
||||
r:globalCenters.reduce( (t,v) => t + v.r , 0) / (globalCenters.length)
|
||||
};
|
||||
|
||||
// make sure centers are within specified tolerance
|
||||
for (let cc of globalCenters) {
|
||||
let dc = Math.sqrt(Math.pow(cc.x - ac.x, 2) + Math.pow(cc.y - ac.y, 2));
|
||||
if (dc / ac.r > arcDist) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure radii are within specified tolerance
|
||||
for (let point of arcQ) {
|
||||
let rad = Math.sqrt(Math.pow(point.x - ac.x, 2) + Math.pow(point.y - ac.y, 2));
|
||||
if (Math.abs(rad - ac.r) > ac.r * arcDist) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// enforce arcRes(olution)
|
||||
for (let i = 1; i < arcQ.length; i++) {
|
||||
let angle = 2 * Math.asin(arcQ[i].dist/(2*ac.r));
|
||||
if (Math.abs(angle) > Math.PI * 2 / arcRes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check points in the context of neighbors
|
||||
for (let i = 0; i < arcQ.length - 2; i++) {
|
||||
let cc = util.center2d(arcQ[i], arcQ[i+1], arcQ[i+2], 1);
|
||||
if (!cc || Math.abs((cc.r - ac.r) / cc.r) > arcDev) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (inloops) {
|
||||
append(`M808`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,10 @@ export function menu() {
|
|||
outputRetractSpeed: newInput(LANG.ad_rrat_s, {title:LANG.ad_rrat_l, convert:toInt, show:notFwRetract}),
|
||||
outputRetractWipe: newInput(LANG.ad_wpln_s, {title:LANG.ad_wpln_l, convert:toFloat, bound:bound(0.0,10)}),
|
||||
separator: newBlank({ class:"set-sep", driven }),
|
||||
fdmArcEnabled: newBoolean(LANG.cx_arce_s, onBooleanClick, { title:LANG.cx_arce_l }),
|
||||
fdmArcTolerance: newInput(LANG.cx_arct_s, {title:LANG.cx_arct_l, convert:toFloat, bound:bound(0,100), trigger, show:() => ui.fdmArcEnabled.checked}),
|
||||
fdmArcResolution: newInput(LANG.cx_arcr_s, {title:LANG.cx_arcr_l, convert:toFloat, bound:bound(0,180), trigger, show:() => ui.fdmArcEnabled.checked}),
|
||||
separator: newBlank({ class:"set-sep", driven, show:() => ui.fdmArcEnabled.checked}),
|
||||
outputAvoidGaps: newBoolean(LANG.ad_agap_s, onBooleanClick, {title:LANG.ad_agap_l}),
|
||||
sliceSolidify: newBoolean(LANG.ad_sold_s, onBooleanClick, {title:LANG.ad_sold_l}),
|
||||
separator: newBlank({ class:"set-sep", driven, show:isBelt }),
|
||||
|
|
@ -175,7 +179,6 @@ export function menu() {
|
|||
_____: newGroup(LANG.ad_menu, $('fdm-expert'), { modes:FDM, driven, hideable, separator, group:"fdm-xprt" }),
|
||||
sliceAngle: newInput(LANG.sl_angl_s, {title:LANG.sl_angl_l, convert:toFloat, show:isBelt}),
|
||||
antiBacklash: newInput(LANG.ad_abkl_s, {title:LANG.ad_abkl_l, convert:toFloat, bound:bound(0,3)}),
|
||||
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}),
|
||||
outputRetractDwell: newInput(LANG.ad_rdwl_s, {title:LANG.ad_rdwl_l, convert:toInt}),
|
||||
outputMinSpeed: newInput(LANG.ad_mins_s, {title:LANG.ad_mins_l, convert:toFloat, bound:bound(1,200)}),
|
||||
outputMaxFlowrate: newInput(LANG.ad_maxf_s, {title:LANG.ad_maxf_l, convert:toFloat, bound:bound(1,200)}),
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ function offset_default(params) {
|
|||
return { last, gaps };
|
||||
}
|
||||
|
||||
// basic thin wall testing
|
||||
const deltaBig = 160;
|
||||
const deltaSmall = 20;
|
||||
|
||||
|
|
|
|||
|
|
@ -848,6 +848,11 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
|||
maxFlowRate = process.outputMaxFlowrate,
|
||||
wipeDist = process.outputRetractWipe || 0,
|
||||
isBelt = device.bedBelt,
|
||||
{ fdmArcEnabled, fdmArcTolerance, fdmArcResolution } = process,
|
||||
arcOpts = {
|
||||
tolerance: fdmArcTolerance,
|
||||
arcRes: fdmArcResolution
|
||||
},
|
||||
bedOffset = originCenter ? {
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
|
@ -1105,6 +1110,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
|||
let scarf = (slice.index > 0 && traceNo === 0 && z - startPoint?.z > 1e-3) ? scarfLength : 0;
|
||||
startPoint = print.polyPrintPath(poly, startPoint, preout, {
|
||||
accel: finishShell,
|
||||
arcOpts: fdmArcEnabled ? arcOpts : undefined,
|
||||
ccw: opt.shell && process.outputAlternating && slice.index % 2,
|
||||
coast: firstLayer ? 0 : coastDist,
|
||||
extrude: numOrDefault(opt.extrude, shellMult),
|
||||
|
|
|
|||
|
|
@ -958,7 +958,6 @@ export function fdm_slice(settings, widget, onupdate, ondone) {
|
|||
widget.belt.midy = (bounds.miny + bounds.maxy) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function connect_lines(lines, maxd = Infinity) {
|
||||
|
|
@ -1878,84 +1877,4 @@ export function supports(settings, widget) {
|
|||
console.log(`support generated in ${Date.now() - now} ms`);
|
||||
widget.supports = add;
|
||||
return add.length > 0;
|
||||
};
|
||||
|
||||
class Vector3Cache {
|
||||
constructor() {
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
get(x, y, z) {
|
||||
let key = [x.round(4),y.round(4),z.round(4)].join(',');
|
||||
let val = this.cache[key];
|
||||
if (!val) {
|
||||
val = new THREE.Vector3(x, y, z);
|
||||
this.cache[key] = val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
class Coplanars {
|
||||
constructor() {
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
put(a, b, c, norm) {
|
||||
let key = norm.round(7).toString();
|
||||
let arr = this.cache[key];
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
this.cache[key] = arr;
|
||||
}
|
||||
arr.push([a,b,c]);
|
||||
}
|
||||
|
||||
group(union) {
|
||||
let out = {};
|
||||
for (let norm in this.cache) {
|
||||
let arr = this.cache[norm];
|
||||
let groups = [];
|
||||
for (let face of arr) {
|
||||
let match = undefined;
|
||||
// see if face matches vertices in any group
|
||||
outer: for (let group of groups) {
|
||||
for (let el of group) {
|
||||
if (
|
||||
el.indexOf(face[0]) >= 0 ||
|
||||
el.indexOf(face[1]) >= 0 ||
|
||||
el.indexOf(face[2]) >= 0
|
||||
) {
|
||||
match = group;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
match.push(face);
|
||||
} else {
|
||||
groups.push([face]);
|
||||
}
|
||||
}
|
||||
if (union) {
|
||||
// convert groups of faces to contiguous polygon groups
|
||||
groups = groups.map(group => {
|
||||
let parr = group.map(arr => {
|
||||
return newPolygon()
|
||||
.add(arr[0].x, arr[0].y, arr[0].z)
|
||||
.add(arr[1].x, arr[1].y, arr[1].z)
|
||||
.add(arr[2].x, arr[2].y, arr[2].z);
|
||||
});
|
||||
let union = parr.length === 1 ? parr :
|
||||
POLY.union(parr, 0, true, {wasm:false});
|
||||
union.merged = parr.length;
|
||||
union.face = group[0];
|
||||
return union;
|
||||
});
|
||||
}
|
||||
out[norm] = groups;
|
||||
}
|
||||
// console.log(out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -902,8 +902,6 @@ self.lang['en-us'] = {
|
|||
ad_scar_l: ["ease up length for scarf joint. disables arc detection","0 = disabled"],
|
||||
ad_spol_s: "short path",
|
||||
ad_spol_l: ["polygons shorter than this","will have their print speed","scaled down to min speed","in millimeters"],
|
||||
ad_arct_s: "arc tolerance",
|
||||
ad_arct_l: ["convert faceted lines to arcs","center point drift tolerance","when matching arc points","consider values around 0.15","value in millimeters","0 or scarf disables this"],
|
||||
ad_zhop_s: "z hop distance",
|
||||
ad_zhop_l: ["amount to raise z","on retraction moves","in millimeters","0 to disable"],
|
||||
ad_abkl_s: "anti-backlash",
|
||||
|
|
|
|||
Loading…
Reference in a new issue