add fdm scarf seams and min layer time
This commit is contained in:
parent
7660570f3a
commit
79a227d741
8 changed files with 129 additions and 32 deletions
16
notes.md
16
notes.md
|
|
@ -31,27 +31,21 @@
|
|||
* `B` multi-extruder rendering of raft fails to offset the rest of the print
|
||||
* `B` multi-extruder purge blocks fail to generate properly for rafts
|
||||
|
||||
* `F` min layer time
|
||||
* `F` separate bridging speed / fan control
|
||||
* `F` elephant foot compensation
|
||||
* `F` inner / outer perimeter compensation
|
||||
* `F` better thin-wall (like arachne)
|
||||
* `F` add scarf seams
|
||||
* `F` add 'random' and 'seam' start point options
|
||||
* `F` new parameter to cap output flow rate (on printer device)
|
||||
* `F` new parameters for bridging speed / bridge fan control
|
||||
* `F` new base paremeter for hole and perimeter compensation
|
||||
* `F` new parameters for inner / outer perimeter compensation
|
||||
* `F` convert ranges to z offsets while continuing to show layer #
|
||||
* `F` support pillar top/bottom should conform to part
|
||||
* `F` more explicit line width control with ranges and min/max adaptive
|
||||
* `F` test outlining solid projected areas (internally)
|
||||
* `F` gradient infill https://www.youtube.com/watch?v=hq53gsYREHU&feature=emb_logo
|
||||
* `F` feather sharp tips by reducing extrusion in area of overlap
|
||||
* `F` segment large flat areas on first layer to mitigate peeling
|
||||
* `F` option to support interior bridges when 0% infill
|
||||
* `F` calculate filament use per extruder per print
|
||||
* `F` expand internal supporting flats / solids before projection (threshold)
|
||||
|
||||
* `P` reduce fan speed, feed rate, and extrusion factor for bridges
|
||||
* `P` auto purge pillars when quick layers are detected for extra cooling
|
||||
* `P` extruder + filament max flow rate cap in planner
|
||||
* `P` solid fill the tops of supports for down facing flats
|
||||
|
||||
# FDM - BELT
|
||||
|
|
@ -91,8 +85,8 @@
|
|||
* `F` ease-in and ease-out especially on tab cut-out start/stop
|
||||
* `F` maintain several part orientations + op chains in a single profile
|
||||
|
||||
* `P` outer outside corners as arc moves
|
||||
* `P` improve parser - do not require spaced tokens and support implied G0 / G1
|
||||
* `P` outer corner arc moves
|
||||
* `P` log Z interpolation for contour XYZ moves
|
||||
* `P` option to start with the smallest poly by area on layer change
|
||||
* `P` redo all path route / planning in prepare to account for terrain before camOut
|
||||
|
|
|
|||
|
|
@ -2324,9 +2324,12 @@ export class Polygon {
|
|||
return obj;
|
||||
}
|
||||
|
||||
// split long straight lines into segments no longer than max
|
||||
// and return a new polygon
|
||||
segment(max = 1, mark, wrap) {
|
||||
// split long straight lines into segments no longer than `max`
|
||||
// and return a new polygon. optionally `mark` points with their
|
||||
// derived segment start point (hinting for thin walls), `wrap`
|
||||
// the poly first point by appending to the end (adaptive walls),
|
||||
// or stopping segmentation at `maxoff`
|
||||
segment(max = 1, mark = false, wrap = false, maxoff = Infinity) {
|
||||
const newp = [];
|
||||
const points = this.points;
|
||||
const length = points.length;
|
||||
|
|
@ -2339,9 +2342,13 @@ export class Polygon {
|
|||
const dl = Math.sqrt(dx * dx + dy * dy);
|
||||
newp.push(p1);
|
||||
if (mark) p1.segment = p1;
|
||||
if (dl < max) {
|
||||
// if segment shorter than max (delta) or
|
||||
// maxoff is exhausted (only segmenting up to some length)
|
||||
// then skip sub-segmenting the current segment
|
||||
if (dl < max || maxoff < 0) {
|
||||
continue;
|
||||
}
|
||||
maxoff -= dl;
|
||||
const div = dl / max;
|
||||
const fit = div | 0;
|
||||
const add = fit - 1;
|
||||
|
|
|
|||
|
|
@ -327,8 +327,10 @@ export const conf = {
|
|||
outputRetractDwell: 20,
|
||||
outputBrimCount: 2,
|
||||
outputBrimOffset: 2,
|
||||
outputScarfLength: 0,
|
||||
outputShortPoly: 100.0,
|
||||
outputMinSpeed: 10.0,
|
||||
outputMinLayerTime: 0,
|
||||
outputCoastDist: 0,
|
||||
outputPurgeTower: 0,
|
||||
outputBeltFirst: false,
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class Print {
|
|||
* @param {Array} output - the array to print to
|
||||
* @param {Object} [options] - optional parameters
|
||||
* @param {boolean} [options.ccw] - set the polygon to be counter-clockwise
|
||||
* @param {boolean} [options.scarf] - scarf seam permitted
|
||||
* @param {number} [options.extrude] - extrude factor for the polygon
|
||||
* @param {number} [options.rate] - print speed in mm/s
|
||||
* @param {number} [options.coast] - distance to coast at the end of the polygon
|
||||
|
|
@ -128,6 +129,7 @@ class Print {
|
|||
closest = options.simple ? poly.first() : poly.findClosestPointTo(startPoint),
|
||||
perimeter = poly.perimeter(),
|
||||
close = !options.open,
|
||||
scarf = !poly.open ? (options.scarf ?? 0) : false,
|
||||
tool = options.tool,
|
||||
last = startPoint,
|
||||
first = true;
|
||||
|
|
@ -137,8 +139,70 @@ class Print {
|
|||
printSpeed = minSpeed + (printSpeed - minSpeed) * (perimeter / process.outputShortPoly);
|
||||
}
|
||||
|
||||
// if not starting at first point in poly, rotate to move start to index = 0
|
||||
let pp = poly.points;
|
||||
if (closest.index > 0) {
|
||||
let cio = pp.indexOf(closest.point);
|
||||
pp = poly.points = [ ...pp.slice(cio), ...pp.slice(0, cio) ];
|
||||
}
|
||||
|
||||
// scarf sanity checks
|
||||
if (scarf) {
|
||||
// cancel scarf for thin wall polys
|
||||
if (pp.filter(p => p.skip || p.moved).length) {
|
||||
scarf = 0;
|
||||
} else {
|
||||
// cancel scarf if any point.z differs
|
||||
let z0 = pp[0].z;
|
||||
let zd = 0;
|
||||
for (let p of pp) zd += Math.abs(p.z - z0);
|
||||
if (zd) scarf = 0;
|
||||
}
|
||||
// console.log({ scarf });
|
||||
}
|
||||
|
||||
// when creating scarf seams, segment poly up to seam length
|
||||
// create array of step up points at start of poly with increasing z
|
||||
// and increasing shellMult and then append the same points on the back
|
||||
// end of the poly with fixed z and decreasing shellMult
|
||||
if (scarf) {
|
||||
let epz = Math.max(...poly.points.map(p => p.z));
|
||||
let spz = startPoint.z;
|
||||
poly = poly.segment(options.nozzleSize ?? 0.4, false, false, scarf * 2);
|
||||
pp = poly.points;
|
||||
let lp, sp = [];
|
||||
for (let p of pp) {
|
||||
let d = lp?.distTo2D(p) ?? 0;
|
||||
sp.push(lp = p);
|
||||
scarf -= d;
|
||||
if (scarf <= 0) break;
|
||||
}
|
||||
let scale = 0.25; // flow compensation on overlap
|
||||
let mid = (1 / sp.length) * 0.75; // flow compensation (- half step)
|
||||
let zd = (epz - spz) / sp.length;
|
||||
let zi = 1;
|
||||
for (let p of sp) {
|
||||
p.z -= zd * (sp.length - zi);
|
||||
p.moved = (((zi++) / sp.length) * scale) - 1 - mid;
|
||||
}
|
||||
let esp = sp.map(p => p.clone()); // ending scarf points
|
||||
for (let p of esp) {
|
||||
p.z = epz;
|
||||
p.moved = (((--zi) / esp.length) * scale) - 1 - mid;
|
||||
}
|
||||
esp.pop(); // stop short to prevent overlap bulge
|
||||
pp.push(...esp);
|
||||
// console.log({ poly, spz, epz, zd, sp, esp });
|
||||
scarf = true;
|
||||
}
|
||||
|
||||
// scarf manages its own close point
|
||||
if (close && !scarf) {
|
||||
pp.push(pp[0]);
|
||||
}
|
||||
|
||||
let lpo;
|
||||
poly.forEachPoint((point, pos, points, count) => {
|
||||
for (let point of pp) {
|
||||
if (point.skip && lpo?.skip) {
|
||||
scope.addOutput(output, point, 0, moveSpeed, tool);
|
||||
} else if (first) {
|
||||
|
|
@ -154,7 +218,8 @@ class Print {
|
|||
first = false;
|
||||
} else {
|
||||
let seglen = last.distTo2D(point);
|
||||
if (coastDist && shellMult && perimeter - seglen <= coastDist) {
|
||||
// cancel coast when using scarf seam
|
||||
if (!scarf && coastDist && shellMult && perimeter - seglen <= coastDist) {
|
||||
let delta = perimeter - coastDist;
|
||||
let offset = seglen - delta;
|
||||
let offPoint = last.offsetPointFrom(point, offset)
|
||||
|
|
@ -168,7 +233,7 @@ class Print {
|
|||
scope.addOutput(output, point, multOut, printSpeed, tool);
|
||||
}
|
||||
last = lpo = point;
|
||||
}, close, closest.index);
|
||||
}
|
||||
|
||||
this.lastPoly = poly;
|
||||
|
||||
|
|
|
|||
|
|
@ -181,8 +181,8 @@ async function path(levels, update, opts = {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (lastOut) { // if last outpoint is defined
|
||||
|
||||
// if last outpoint is defined
|
||||
if (lastOut) {
|
||||
if (arrowAll || lastOut.emit !== out.emit) {
|
||||
heads.push({p1: lastOutPoint, p2: outPoint});
|
||||
}
|
||||
|
|
@ -200,13 +200,10 @@ async function path(levels, update, opts = {}) {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ export function menu() {
|
|||
_____: newGroup(LANG.fc_menu, $('fdm-cool'), { modes:FDM, driven, hideable, separator, group:"fdm-cool" }),
|
||||
outputFanLayer: newInput(LANG.ou_fanl_s, { title:LANG.ou_fanl_l, convert:toInt, bound:bound(0,255) }),
|
||||
outputFanSpeed: newInput(LANG.ou_fans_s, {title:LANG.ou_fans_l, convert:toInt, bound:bound(0,255)}),
|
||||
outputMinLayerTime: newInput(LANG.ou_layt_s, { title:LANG.ou_layt_l, convert:toInt, bound:bound(0,200) }),
|
||||
_____: newGroup(LANG.sp_menu, $('fdm-support'), { modes:FDM, driven, hideable, separator, group:"fdm-supp" }),
|
||||
sliceSupportNozzle: newSelect(LANG.sp_nozl_s, {title:LANG.sp_nozl_l, show:isMultiHead}, "extruders"),
|
||||
sliceSupportDensity: newInput(LANG.sp_dens_s, {title:LANG.sp_dens_l, convert:toFloat, bound:bound(0.0,1.0)}),
|
||||
|
|
@ -177,6 +178,7 @@ export function menu() {
|
|||
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)}),
|
||||
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}),
|
||||
outputScarfLength: newInput(LANG.ad_scar_s, {title:LANG.ad_scar_l, convert:toFloat, bound:bound(0,1000)}),
|
||||
outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}),
|
||||
outputCoastDist: newInput(LANG.ad_scst_s, {title:LANG.ad_scst_l, convert:toFloat, bound:bound(0.0,10)}),
|
||||
zHopDistance: newInput(LANG.ad_zhop_s, {title:LANG.ad_zhop_l, convert:toFloat, bound:bound(0,3.0)}),
|
||||
|
|
|
|||
|
|
@ -800,6 +800,8 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
|||
process = opt.params || settings.process,
|
||||
originCenter = device.originCenter || bedRound,
|
||||
extruder = parseInt(slice.extruder || 0),
|
||||
scarfLength = process.outputScarfLength || 0,
|
||||
minLayerTime = process.outputMinLayerTime || 0,
|
||||
nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle,
|
||||
firstLayer = (opt.first || false) && !opt.support,
|
||||
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
||||
|
|
@ -1036,8 +1038,8 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// solid infill
|
||||
function outputTraces(poly, opt = {}) {
|
||||
// output shell wall
|
||||
function outputTraces(poly, opt = {}, traceNo = 0) {
|
||||
if (!poly) return;
|
||||
if (Array.isArray(poly)) {
|
||||
if (opt.sort) {
|
||||
|
|
@ -1070,26 +1072,28 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
|||
if (next) {
|
||||
last = next;
|
||||
polys.remove(next);
|
||||
outputTraces(next, opt);
|
||||
outputTraces(next, opt, traceNo++);
|
||||
} else {
|
||||
last = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outputOrderClosest(poly, function(next) {
|
||||
outputTraces(next, opt);
|
||||
outputTraces(next, opt, traceNo++);
|
||||
}, null);
|
||||
}
|
||||
} else {
|
||||
let finishShell = poly.depth === 0 && !firstLayer;
|
||||
startPoint = print.polyPrintPath(poly, startPoint, preout, {
|
||||
ccw: opt.shell && process.outputAlternating && slice.index % 2,
|
||||
tool: extruder,
|
||||
rate: finishShell ? finishSpeed : printSpeed,
|
||||
accel: finishShell,
|
||||
wipe: process.outputWipeDistance || 0,
|
||||
ccw: opt.shell && process.outputAlternating && slice.index % 2,
|
||||
coast: firstLayer ? 0 : coastDist,
|
||||
extrude: numOrDefault(opt.extrude, shellMult),
|
||||
nozzleSize,
|
||||
rate: finishShell ? finishSpeed : printSpeed,
|
||||
scarf: (traceNo === 0 && z - startPoint?.z > 1e-3) ? scarfLength : 0,
|
||||
tool: extruder,
|
||||
wipe: process.outputWipeDistance || 0,
|
||||
onfirst: function(firstPoint) {
|
||||
let from = seedPoint || startPoint;
|
||||
if (from.distTo2D(firstPoint) > retractDist) {
|
||||
|
|
@ -1570,6 +1574,28 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
// adjust layer output speeds (by slowing them down)
|
||||
// if layer print time is less than minimum (required for cooling)
|
||||
if (minLayerTime) {
|
||||
let lp, td = 0, tt = 0;
|
||||
for (let p of preout) {
|
||||
if (!p.emit) continue;
|
||||
let mms = p.speed ?? 1;
|
||||
let dst = lp?.distTo2D(p.point) ?? 0;
|
||||
if (dst) {
|
||||
tt += (dst/mms);
|
||||
td += dst;
|
||||
}
|
||||
lp = p.point;
|
||||
}
|
||||
if (tt < minLayerTime) {
|
||||
let fact = tt / minLayerTime;
|
||||
for (let p of preout) {
|
||||
if (p.emit) p.speed *= fact;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// offset print points
|
||||
for (let i=0; i<preout.length; i++) {
|
||||
preout[i].point = preout[i].point.add(offset);
|
||||
|
|
|
|||
|
|
@ -765,8 +765,10 @@ self.lang['en-us'] = {
|
|||
ou_exml_l: ["extrusion multiplier","0.0 - 2.0"],
|
||||
ou_fanl_s: "fan on layer",
|
||||
ou_fanl_l: ["layer # to turn on fan","layers start at 0"],
|
||||
ou_fans_s: "fan speed",
|
||||
ou_fans_s: "fan on speed",
|
||||
ou_fans_l: "0 - 255",
|
||||
ou_layt_s: "min layer time",
|
||||
ou_layt_l: ["minimum allowable layer print time in seconds. printing will be slowed if the layer would outerwise complete in fewer seconds.","0 = disabled"],
|
||||
|
||||
// OUTPUT CAM
|
||||
ou_toll_s: "precision",
|
||||
|
|
@ -862,6 +864,8 @@ self.lang['en-us'] = {
|
|||
ad_msol_l: ["minimum area (mm^2)","required to keep solid","must be > 0.1"],
|
||||
ad_mins_s: "min speed",
|
||||
ad_mins_l: ["minimum speed","for short segments"],
|
||||
ad_scar_s: "scarf length",
|
||||
ad_scar_l: ["ease up length for scarf joint","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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue