add fdm volumetric flow output caps

This commit is contained in:
Stewart Allen 2025-10-16 21:39:24 -04:00
commit a26b1d6bef
9 changed files with 48 additions and 20 deletions

View file

@ -60,7 +60,7 @@
"three": "^0.178.0", "three": "^0.178.0",
"three-mesh-bvh": "^0.7.6", "three-mesh-bvh": "^0.7.6",
"uglify-js": "3.14.5", "uglify-js": "3.14.5",
"validator": ">=13.7.0", "validator": ">=13.15.15",
"ws": "^8.18.2" "ws": "^8.18.2"
}, },
"devDependencies": { "devDependencies": {

View file

@ -293,6 +293,7 @@ export const conf = {
outputInvertY: false, outputInvertY: false,
outputLayerRetract: false, outputLayerRetract: false,
outputLoops: 0, outputLoops: 0,
outputMaxFlowrate: 15,
outputMinLayerTime: 10, outputMinLayerTime: 10,
outputMinSpeed: 5.0, outputMinSpeed: 5.0,
outputPurgeTower: 0, outputPurgeTower: 0,

View file

@ -38,7 +38,6 @@ export class Topo {
x: axis === "x", x: axis === "x",
y: axis === "y" y: axis === "y"
}, },
zMin = min.z + 0.0001,
tolerance = op.tolerance, tolerance = op.tolerance,
resolution = tolerance ? tolerance : 1 / Math.sqrt(density / (span.x * span.y)), resolution = tolerance ? tolerance : 1 / Math.sqrt(density / (span.x * span.y)),
step = this.step = (tool.traceOffset() * 2) * op.step, step = this.step = (tool.traceOffset() * 2) * op.step,
@ -123,7 +122,9 @@ export class Topo {
let slices = this.slices = []; let slices = this.slices = [];
let slice = { min: range.min, max: range.min + step, index }; let slice = { min: range.min, max: range.min + step, index };
for (let z = range.min; z < range.max; z += resolution) { // console.log({ shards, step, range, resolution });
for (let z = range.min; z <= range.max; z += resolution) {
// console.log({ z });
if (z > slice.max) { if (z > slice.max) {
slices.push(slice); slices.push(slice);
slice = { min: z, max: z + step, index }; slice = { min: z, max: z + step, index };
@ -131,7 +132,7 @@ export class Topo {
index++; index++;
} }
slices.push(slice); slices.push(slice);
// console.log({ shards, range, step, slices }); // console.log({ shards, range, step, slices: slices.slice() });
const { minions } = self.kiri_worker; const { minions } = self.kiri_worker;
if (minions?.running > 1) { if (minions?.running > 1) {

View file

@ -9,6 +9,7 @@ import { fdm_export } from './export.js';
// fil = filament diameter // fil = filament diameter
// slice = slice height // slice = slice height
function extrudePerMM(noz, fil, slice) { function extrudePerMM(noz, fil, slice) {
// (ratio of nozzle to filament) * (ratio of slice to nozzle)
return ((Math.PI * util.sqr(noz / 2)) / (Math.PI * util.sqr(fil / 2))) * (slice / noz); return ((Math.PI * util.sqr(noz / 2)) / (Math.PI * util.sqr(fil / 2))) * (slice / noz);
}; };

View file

@ -26,7 +26,6 @@ export function fdm_export(print, online, ondone, ondebug) {
{ extrudeAbs } = device, { extrudeAbs } = device,
{ exportThumb } = controller, { exportThumb } = controller,
{ extrudeMM, extrudePerMM } = FDM, { extrudeMM, extrudePerMM } = FDM,
extused = Object.keys(print.extruders).map(v => parseInt(v)),
timeFactor = (device.gcodeTime || 1) * 1.5, timeFactor = (device.gcodeTime || 1) * 1.5,
decimals = config.gcode_decimals || 4, decimals = config.gcode_decimals || 4,
zMoveMax = device.deviceZMax || 0, zMoveMax = device.deviceZMax || 0,
@ -49,7 +48,6 @@ export function fdm_export(print, online, ondone, ondebug) {
last = null, last = null,
zpos = 0, zpos = 0,
bmax = 0, bmax = 0,
blastz = 0,
belt_add_y = (process.firstLayerYOffset || 0) - (belty || 0), belt_add_y = (process.firstLayerYOffset || 0) - (belty || 0),
oloops = process.outputLoops || 0, oloops = process.outputLoops || 0,
zhop = process.zHopDistance || 0, // range zhop = process.zHopDistance || 0, // range
@ -58,10 +56,8 @@ export function fdm_export(print, online, ondone, ondebug) {
retDist = process.outputRetractDist || 0, // range retDist = process.outputRetractDist || 0, // range
retSpeed = process.outputRetractSpeed * 60 || 1, // range retSpeed = process.outputRetractSpeed * 60 || 1, // range
retDwell = process.outputRetractDwell || 0, // range retDwell = process.outputRetractDwell || 0, // range
timeDwell = retDwell / 1000,
scarfZ = process.outputScarfLength ? true : false, scarfZ = process.outputScarfLength ? true : false,
arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0), arcDist = isBelt || scarfZ ? 0 : (process.arcTolerance || 0),
arcMin = 1,
arcRes = 20, arcRes = 20,
arcDev = 0.5, arcDev = 0.5,
arcMax = 40, arcMax = 40,
@ -85,7 +81,6 @@ export function fdm_export(print, online, ondone, ondebug) {
fanSpeedSave = undefined, fanSpeedSave = undefined,
fanSpeedBase = 0, fanSpeedBase = 0,
fanSpeed = 0, fanSpeed = 0,
lastType = undefined,
lastNozzleTemp = nozzleTemp, lastNozzleTemp = nozzleTemp,
lastBedTemp = bedTemp, lastBedTemp = bedTemp,
lastFanSpeed = fanSpeed, lastFanSpeed = fanSpeed,
@ -169,7 +164,6 @@ export function fdm_export(print, online, ondone, ondebug) {
retDist = params.outputRetractDist || 0; // range retDist = params.outputRetractDist || 0; // range
retSpeed = params.outputRetractSpeed * 60 || 1; // range retSpeed = params.outputRetractSpeed * 60 || 1; // range
retDwell = params.outputRetractDwell || 0; // range retDwell = params.outputRetractDwell || 0; // range
timeDwell = retDwell / 1000;
nozzleTemp = layer === 0 ? nozzleTemp = layer === 0 ?
params.firstLayerNozzleTemp || params.outputTemp : params.firstLayerNozzleTemp || params.outputTemp :
params.outputTemp || params.firstLayerNozzleTemp; params.outputTemp || params.firstLayerNozzleTemp;
@ -470,7 +464,7 @@ export function fdm_export(print, online, ondone, ondebug) {
if (isBelt) { if (isBelt) {
let zheight = path ? path.height || 0 : 0; let zheight = path ? path.height || 0 : 0;
epos.x = originCenter ? -pos.x : device.bedWidth - pos.x; epos.x = originCenter ? -pos.x : device.bedWidth - pos.x;
epos.z = blastz = pos.z * icos; epos.z = pos.z * icos;
epos.y = -pos.y + epos.z * bcos + belt_add_y; epos.y = -pos.y + epos.z * bcos + belt_add_y;
lout = epos; lout = epos;
} }
@ -635,7 +629,7 @@ export function fdm_export(print, online, ondone, ondebug) {
// emit comment on output type chage // emit comment on output type chage
if (last && out.type !== last.type) { if (last && out.type !== last.type) {
lastType = subst.feature = out.type; subst.feature = out.type;
if (gcodeFeature && gcodeFeature.length) { if (gcodeFeature && gcodeFeature.length) {
appendAllSub(gcodeFeature); appendAllSub(gcodeFeature);
} else { } else {
@ -752,7 +746,6 @@ export function fdm_export(print, online, ondone, ondebug) {
} }
// if new point is off the arc // 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()) { 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]; // let debug = [deem, depm, desp, dc * arcQ.center.length / arcQ.rSum > arcDist, dist > cc.r, cc.r > arcMax, radFault];
if (arcQ.length === 4) { if (arcQ.length === 4) {

View file

@ -177,6 +177,7 @@ export function menu() {
arcTolerance: newInput(LANG.ad_arct_s, {title:LANG.ad_arct_l, convert:toFloat, bound:bound(0,1.0), show:() => { return isNotBelt() }}), 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}), 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)}), 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)}),
outputPurgeTower: newInput(LANG.ad_purg_s, {title:LANG.ad_purg_l, convert:toInt, bound:bound(0,1000)}), 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)}), 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)}), outputShortPoly: newInput(LANG.ad_spol_s, {title:LANG.ad_spol_l, convert:toFloat, bound:bound(0,10000)}),

View file

@ -1,5 +1,6 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */ /** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
import { FDM } from './driver-be.js';
import { base, util } from '../../../geo/base.js'; import { base, util } from '../../../geo/base.js';
import { poly2polyEmit, tip2tipEmit } from '../../../geo/paths.js'; import { poly2polyEmit, tip2tipEmit } from '../../../geo/paths.js';
import { newBounds } from '../../../geo/bounds.js'; import { newBounds } from '../../../geo/bounds.js';
@ -794,10 +795,22 @@ export async function fdm_prepare(widgets, settings, update) {
} }
}; };
/**
* Output and route Slice (shell,fill) paths for a single Slice
*
* @param {*} print Print output Object
* @param {*} slice current slice being Output
* @param {*} startPoint starting point for this layer
* @param {*} offset start point offset
* @param {*} output output prebuffer
* @param {*} opt options
*/
function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) { function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
const { settings } = print; const { settings } = print;
const { device } = settings; const { device } = settings;
const { bedWidth, bedDepth, bedRound } = device; const { bedWidth, bedDepth, bedRound } = device;
const { extrudePerMM } = FDM;
let preout = [], let preout = [],
process = opt.params || settings.process, process = opt.params || settings.process,
@ -806,6 +819,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
scarfLength = process.outputScarfLength || 0, scarfLength = process.outputScarfLength || 0,
minLayerTime = process.outputMinLayerTime || 0, minLayerTime = process.outputMinLayerTime || 0,
nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle, nozzleSize = process.sliceLineWidth || device.extruders[extruder].extNozzle,
nozzles = device.extruders.map(e => e.extNozzle),
firstLayer = (opt.first || false) && !opt.support, firstLayer = (opt.first || false) && !opt.support,
thinWall = nozzleSize * (opt.thinWall || 1.75), thinWall = nozzleSize * (opt.thinWall || 1.75),
retractDist = opt.retractOver || (nozzleSize * 5), retractDist = opt.retractOver || (nozzleSize * 5),
@ -828,6 +842,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
zhop = process.zHopDistance || 0, zhop = process.zHopDistance || 0,
zmax = opt.zmax, zmax = opt.zmax,
antiBacklash = process.antiBacklash, antiBacklash = process.antiBacklash,
maxFlowRate = process.outputMaxFlowrate,
wipeDist = process.outputRetractWipe || 0, wipeDist = process.outputRetractWipe || 0,
isBelt = device.bedBelt, isBelt = device.bedBelt,
bedOffset = originCenter ? { bedOffset = originCenter ? {
@ -1595,16 +1610,34 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
if (!p.emit) continue; if (!p.emit) continue;
let mms = p.speed ?? 1; let mms = p.speed ?? 1;
let dst = lp?.distTo2D(p.point) ?? 0; let dst = lp?.distTo2D(p.point) ?? 0;
lp = p.point;
if (dst) { if (dst) {
tt += (dst/mms); tt += (dst/mms);
td += dst; td += dst;
p.dst = dst;
} }
lp = p.point;
} }
if (minLayerTime && tt < minLayerTime) { if (minLayerTime && tt < minLayerTime) {
let fact = tt / minLayerTime; let fact = tt / minLayerTime;
for (let p of preout) { for (let p of preout) {
if (p.emit) p.speed = Math.max(p.speed * fact, minSpeed); if (p.emit) {
p.speed = Math.max(p.speed * fact, minSpeed);
}
}
}
// apply speed damping when output exceeds volumetric flowrate cap
if (maxFlowRate) {
for (let p of preout) {
if (p.emit && p.dst) {
let vfr = nozzles[p.tool] * slice.height * p.speed;
if (vfr > maxFlowRate) {
let old = p.speed;
let damp = maxFlowRate / vfr;
p.speed = p.speed * damp;
// console.log({ vfr, damp, old, new: p.speed });
}
}
} }
} }

View file

@ -127,16 +127,12 @@ async function redirectOr404(path) {
} }
} }
async function fromCacheOrNetwork(req, test) { async function fromCacheOrNetwork(req) {
const cache = await caches.open(CACHE_VERSION); const cache = await caches.open(CACHE_VERSION);
const hit = await cache.match(req, { ignoreSearch: true }); const hit = await cache.match(req, { ignoreSearch: true });
if (hit) { if (hit) {
// log(`hit: ${req.url}`);
return hit; return hit;
} }
if (test) {
return test;
}
const net = await fetch_safe(req); const net = await fetch_safe(req);
cache.put(req, net.clone()); cache.put(req, net.clone());
log(`put: ${net.url}`); log(`put: ${net.url}`);

View file

@ -868,6 +868,8 @@ self.lang['en-us'] = {
ad_msol_l: ["minimum area (mm^2)","required to keep solid","must be > 0.1"], ad_msol_l: ["minimum area (mm^2)","required to keep solid","must be > 0.1"],
ad_mins_s: "min speed", ad_mins_s: "min speed",
ad_mins_l: ["minimum speed","for short segments"], ad_mins_l: ["minimum speed","for short segments"],
ad_maxf_s: "max flowrate",
ad_maxf_l: ["maximum volumetric flowrate supported by this print head and filament","in millimeters^3 / second"],
ad_scar_s: "scarf length", ad_scar_s: "scarf length",
ad_scar_l: ["ease up length for scarf joint. disables arc detection","0 = disabled"], ad_scar_l: ["ease up length for scarf joint. disables arc detection","0 = disabled"],
ad_spol_s: "short path", ad_spol_s: "short path",