fix arc detect radian / degree
add polygon thickness() ratio helper add fdm slice delta thickness filter parameter add fdm print to moonraker auto-start option fix fdm finish speed ratio for flats and bridges reflow fdm slice.js code for better readability
This commit is contained in:
parent
d51894fb75
commit
7e92845662
11 changed files with 429 additions and 389 deletions
|
|
@ -115,19 +115,20 @@ export function findArcCenters(poly, opt = {}) {
|
||||||
* @param {Polygon} poly - polygon to analyze (will be mutated with arc annotations)
|
* @param {Polygon} poly - polygon to analyze (will be mutated with arc annotations)
|
||||||
* @param {Object} opts - detection options
|
* @param {Object} opts - detection options
|
||||||
* @param {number} opts.tolerance - arc detection tolerance (default 0.1)
|
* @param {number} opts.tolerance - arc detection tolerance (default 0.1)
|
||||||
* @param {number} opts.arcRes - arc resolution in radians (default 0.1)
|
* @param {number} opts.arcRes - arc resolution in degrees (default 1)
|
||||||
* @param {number} opts.minPoints - minimum points to consider an arc (default 4)
|
* @param {number} opts.minPoints - minimum points to consider an arc (default 4)
|
||||||
* @returns {Polygon} this polygon with arc annotations
|
* @returns {Polygon} this polygon with arc annotations
|
||||||
*/
|
*/
|
||||||
export function detectArcs(poly, opts = {}) {
|
export function detectArcs(poly, opts = {}) {
|
||||||
const {
|
const {
|
||||||
tolerance = 0.1,
|
tolerance = 0.1,
|
||||||
arcRes = 0.1,
|
arcRes = 1,
|
||||||
minPoints = 4
|
minPoints = 4
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
const points = poly.points;
|
const points = poly.points;
|
||||||
const length = points.length;
|
const length = points.length;
|
||||||
|
const arcResRadians = arcRes * (Math.PI / 180);
|
||||||
|
|
||||||
if (length < minPoints) {
|
if (length < minPoints) {
|
||||||
return poly;
|
return poly;
|
||||||
|
|
@ -143,7 +144,7 @@ export function detectArcs(poly, opts = {}) {
|
||||||
// because we need at least minPoints to form an arc
|
// because we need at least minPoints to form an arc
|
||||||
while (i < length - minPoints + 1) {
|
while (i < length - minPoints + 1) {
|
||||||
// Try to detect arc starting at position i
|
// Try to detect arc starting at position i
|
||||||
const arcData = detectArcAt(poly, i, points, length, tolerance, arcRes, minPoints);
|
const arcData = detectArcAt(poly, i, points, length, tolerance, arcResRadians, minPoints);
|
||||||
|
|
||||||
if (arcData) {
|
if (arcData) {
|
||||||
// Annotate the starting point
|
// Annotate the starting point
|
||||||
|
|
@ -172,7 +173,7 @@ export function detectArcs(poly, opts = {}) {
|
||||||
* @param {number} minPoints - minimum points for arc
|
* @param {number} minPoints - minimum points for arc
|
||||||
* @returns {Object|null} arc data or null if no arc detected
|
* @returns {Object|null} arc data or null if no arc detected
|
||||||
*/
|
*/
|
||||||
export function detectArcAt(poly, startIdx, points, length, tolerance, arcRes, minPoints) {
|
function detectArcAt(poly, startIdx, points, length, tolerance, arcRes, minPoints) {
|
||||||
// Greedy approach: collect as many points as possible that might form an arc
|
// Greedy approach: collect as many points as possible that might form an arc
|
||||||
let candidates = [];
|
let candidates = [];
|
||||||
|
|
||||||
|
|
@ -222,7 +223,7 @@ export function detectArcAt(poly, startIdx, points, length, tolerance, arcRes, m
|
||||||
* @param {number} minPoints - minimum points for arc
|
* @param {number} minPoints - minimum points for arc
|
||||||
* @returns {Object|null} arc data or null if invalid
|
* @returns {Object|null} arc data or null if invalid
|
||||||
*/
|
*/
|
||||||
export function validateArc(poly, arcPoints, tolerance, arcRes, minPoints) {
|
function validateArc(poly, arcPoints, tolerance, arcRes, minPoints) {
|
||||||
if (arcPoints.length < minPoints) {
|
if (arcPoints.length < minPoints) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +296,7 @@ export function validateArc(poly, arcPoints, tolerance, arcRes, minPoints) {
|
||||||
* @param {number} tolerance - detection tolerance
|
* @param {number} tolerance - detection tolerance
|
||||||
* @returns {Object|null} center with x, y, r properties or null
|
* @returns {Object|null} center with x, y, r properties or null
|
||||||
*/
|
*/
|
||||||
export function findBestCenter(arcPoints, tolerance) {
|
function findBestCenter(arcPoints, tolerance) {
|
||||||
const len = arcPoints.length;
|
const len = arcPoints.length;
|
||||||
|
|
||||||
// Use 3 well-spaced points for initial center calculation
|
// Use 3 well-spaced points for initial center calculation
|
||||||
|
|
|
||||||
|
|
@ -1221,6 +1221,21 @@ export class Polygon {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find the ratio of area to perimeter which can give provide
|
||||||
|
* guidance if this polygon is likely to have a thin cross-section
|
||||||
|
*
|
||||||
|
* @param {*} deep whether to include inner holes in the calculation
|
||||||
|
* @returns ratio
|
||||||
|
*/
|
||||||
|
thickness(deep) {
|
||||||
|
if (deep) {
|
||||||
|
return 2 * this.areaDeep() / this.perimeterDeep();
|
||||||
|
} else {
|
||||||
|
return 2 * this.area() / this.perimeter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Polygon} poly
|
* @param {Polygon} poly
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
|
|
|
||||||
|
|
@ -337,6 +337,7 @@ export const conf = {
|
||||||
sliceShells: 3,
|
sliceShells: 3,
|
||||||
sliceSolidify: false,
|
sliceSolidify: false,
|
||||||
sliceSolidMinArea: 1,
|
sliceSolidMinArea: 1,
|
||||||
|
sliceSolidMinThick: 0.4,
|
||||||
sliceSupportAngle: 50,
|
sliceSupportAngle: 50,
|
||||||
sliceSupportDensity: 0.1,
|
sliceSupportDensity: 0.1,
|
||||||
sliceSupportExtra: 0,
|
sliceSupportExtra: 0,
|
||||||
|
|
|
||||||
|
|
@ -262,12 +262,8 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
localSet('octo-apik', apik.trim());
|
localSet('octo-apik', apik.trim());
|
||||||
localSet('octo-type', type.trim());
|
localSet('octo-type', type.trim());
|
||||||
|
|
||||||
filename = $('print-filename').value;
|
filename = $('print-filename').value + "." + fileext;
|
||||||
form.append("file", getBlob(), filename+"."+fileext);
|
form.append("file", getBlob(), filename);
|
||||||
// moonraker upload params
|
|
||||||
// form.append("root", "gcodes"); // optional but recommended
|
|
||||||
// form.append("path", ""); // optional subfolder
|
|
||||||
if (start) form.append("print", "true"); // <-- auto-start
|
|
||||||
ajax.onreadystatechange = function() {
|
ajax.onreadystatechange = function() {
|
||||||
console.log({ ajax_readyState: ajax.readyState });
|
console.log({ ajax_readyState: ajax.readyState });
|
||||||
if (ajax.readyState === 4) {
|
if (ajax.readyState === 4) {
|
||||||
|
|
@ -279,6 +275,16 @@ function exportGCodeDialog(gcode, sections, info, names) {
|
||||||
api.show.alert("send error\nstatus: "+status+"\nmessage: "+ajax.responseText);
|
api.show.alert("send error\nstatus: "+status+"\nmessage: "+ajax.responseText);
|
||||||
}
|
}
|
||||||
api.show.progress(0);
|
api.show.progress(0);
|
||||||
|
if (start && type === 'moonraker') {
|
||||||
|
fetch(`${host}/printer/print/start`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ filename })
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => console.log('print start', data))
|
||||||
|
.catch(err => console.error('print start error', err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
api.show.progress(0);
|
api.show.progress(0);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class Print {
|
||||||
scarf = !poly.open ? (options.scarf ?? 0) : false,
|
scarf = !poly.open ? (options.scarf ?? 0) : false,
|
||||||
last = startPoint,
|
last = startPoint,
|
||||||
first = true,
|
first = true,
|
||||||
{ arcOpts, tool, zmax } = options;
|
{ arcOpts, tool, zmax, onfirst, onfirstout } = options;
|
||||||
|
|
||||||
// if short, use calculated print speed based on sliding scale
|
// if short, use calculated print speed based on sliding scale
|
||||||
if (perimeter < process.outputShortPoly) {
|
if (perimeter < process.outputShortPoly) {
|
||||||
|
|
@ -169,7 +169,7 @@ class Print {
|
||||||
if (scarf) {
|
if (scarf) {
|
||||||
let epz = Math.max(...poly.points.map(p => p.z));
|
let epz = Math.max(...poly.points.map(p => p.z));
|
||||||
let spz = startPoint.z;
|
let spz = startPoint.z;
|
||||||
poly = poly.segment(options.nozzleSize ?? 0.4, false, false, scarf * 2);
|
poly = poly.segment(nozzleSize ?? 0.4, false, false, scarf * 2);
|
||||||
pp = poly.points;
|
pp = poly.points;
|
||||||
let lp, sp = [];
|
let lp, sp = [];
|
||||||
for (let p of pp) {
|
for (let p of pp) {
|
||||||
|
|
@ -196,6 +196,7 @@ class Print {
|
||||||
} else if (arcOpts) {
|
} else if (arcOpts) {
|
||||||
// annotate arc points
|
// annotate arc points
|
||||||
pp = newPolygon(pp).detectArcs(arcOpts).points;
|
pp = newPolygon(pp).detectArcs(arcOpts).points;
|
||||||
|
// console.log(arcOpts, pp.map(p => p.arc?.skip).filter(v => v));
|
||||||
}
|
}
|
||||||
|
|
||||||
// scarf manages its own close point
|
// scarf manages its own close point
|
||||||
|
|
@ -210,10 +211,13 @@ class Print {
|
||||||
let { arc } = point;
|
let { arc } = point;
|
||||||
if (arc) {
|
if (arc) {
|
||||||
arcInfo = arc;
|
arcInfo = arc;
|
||||||
scope.addOutput(output, point, shellMult, moveSpeed, tool);
|
if (first && onfirst) onfirst(point, output);
|
||||||
|
let out = scope.addOutput(output, point, shellMult, moveSpeed, tool);
|
||||||
|
if (first && onfirstout) onfirstout(out);
|
||||||
// arc center is relative to first point
|
// arc center is relative to first point
|
||||||
arcInfo.center.move({ x: -point.x, y: -point.y, z: 0 });
|
arcInfo.center.move({ x: -point.x, y: -point.y, z: 0 });
|
||||||
last = point;
|
last = point;
|
||||||
|
if (first) first = false;
|
||||||
continue;
|
continue;
|
||||||
} else if (arcInfo) {
|
} else if (arcInfo) {
|
||||||
// accumulate and pass to last point to calc FDM E value
|
// accumulate and pass to last point to calc FDM E value
|
||||||
|
|
@ -235,14 +239,10 @@ class Print {
|
||||||
scope.addOutput(output, point, 0, moveSpeed, tool);
|
scope.addOutput(output, point, 0, moveSpeed, tool);
|
||||||
} else if (first) {
|
} else if (first) {
|
||||||
// if (point.skip) console.log({ skip: point });
|
// if (point.skip) console.log({ skip: point });
|
||||||
if (options.onfirst) {
|
onfirst && onfirst(point, output);
|
||||||
options.onfirst(point, output);
|
|
||||||
}
|
|
||||||
// move to first output point on poly
|
// move to first output point on poly
|
||||||
let out = scope.addOutput(output, point, 0, moveSpeed, tool);
|
let out = scope.addOutput(output, point, 0, moveSpeed, tool);
|
||||||
if (options.onfirstout) {
|
onfirstout && onfirstout(out);
|
||||||
options.onfirstout(out);
|
|
||||||
}
|
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
let seglen = last.distTo2D(point);
|
let seglen = last.distTo2D(point);
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ export function menu() {
|
||||||
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
|
sliceFillAngle: newInput(LANG.fi_angl_s, {title:LANG.fi_angl_l, convert:toFloat}),
|
||||||
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
|
sliceFillGrow: newInput(LANG.fi_grow_s, {title:LANG.fi_grow_l, convert:toFloat}),
|
||||||
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
|
sliceSolidMinArea: newInput(LANG.ad_msol_s, {title:LANG.ad_msol_l, convert:toFloat}),
|
||||||
|
sliceSolidMinThick: newInput(LANG.ad_mthk_s, {title:LANG.ad_mthk_l, convert:toFloat}),
|
||||||
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
|
_____: newGroup(LANG.fi_menu, $('fdm-fill'), { modes:FDM, driven, hideable, separator, group:"fdm-fill" }),
|
||||||
sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"),
|
sliceFillType: newSelect(LANG.fi_type, {trigger}, "infill"),
|
||||||
sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}),
|
sliceFillSparse: newInput(LANG.fi_pcnt_s, {title:LANG.fi_pcnt_l, convert:toFloat, bound:bound(0.0,1.0), show:hasInfill}),
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export function init() {
|
||||||
"sliceFillSparse": LANG.fi_pcnt_s,
|
"sliceFillSparse": LANG.fi_pcnt_s,
|
||||||
"sliceFillGrow": LANG.fi_grow_s,
|
"sliceFillGrow": LANG.fi_grow_s,
|
||||||
"sliceSolidMinArea": LANG.ad_msol_s,
|
"sliceSolidMinArea": LANG.ad_msol_s,
|
||||||
|
"sliceSolidMinThick": LANG.ad_mthk_s,
|
||||||
"sliceTopLayers": LANG.sl_ltop_s,
|
"sliceTopLayers": LANG.sl_ltop_s,
|
||||||
"sliceBottomLayers": LANG.sl_lbot_s,
|
"sliceBottomLayers": LANG.sl_lbot_s,
|
||||||
"firstLayerRate": LANG.fl_rate_s,
|
"firstLayerRate": LANG.fl_rate_s,
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,6 @@ export async function fdm_prepare(widgets, settings, update) {
|
||||||
},
|
},
|
||||||
routeAround: process.outputAvoidGaps,
|
routeAround: process.outputAvoidGaps,
|
||||||
seedPoint: printPoint.sub(offset),
|
seedPoint: printPoint.sub(offset),
|
||||||
support: slice.supports,
|
|
||||||
zmax,
|
zmax,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -780,7 +779,6 @@ 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,
|
||||||
|
|
@ -790,7 +788,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
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),
|
nozzles = device.extruders.map(e => e.extNozzle),
|
||||||
firstLayer = (opt.first || false) && !opt.support,
|
firstLayer = (opt.first || false),
|
||||||
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
thinWall = nozzleSize * (opt.thinWall || 1.75),
|
||||||
retractDist = opt.retractOver || (nozzleSize * 5),
|
retractDist = opt.retractOver || (nozzleSize * 5),
|
||||||
fillMult = opt.mult || process.outputFillMult,
|
fillMult = opt.mult || process.outputFillMult,
|
||||||
|
|
@ -845,10 +843,12 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
printSpeed = finishSpeed || printSpeed;
|
printSpeed = finishSpeed || printSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// override: adapt bridge layer speed
|
// override: adapt bridge layer speed and flats
|
||||||
if (slice.isBridgeLayer) {
|
// but not on the first layer which has it's own settings
|
||||||
|
if (slice.index > 0 && (slice.isBridgeLayer || slice.isFlatsLayer)) {
|
||||||
fillSpeed /= 2;
|
fillSpeed /= 2;
|
||||||
printSpeed /= 2;
|
printSpeed /= 2;
|
||||||
|
finishSpeed /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function retract() {
|
function retract() {
|
||||||
|
|
@ -1444,7 +1444,7 @@ function slicePrintPath(print, slice, startPoint, offset, output, opt = {}) {
|
||||||
if (slice.tops) {
|
if (slice.tops) {
|
||||||
out.appendAll(slice.tops);
|
out.appendAll(slice.tops);
|
||||||
}
|
}
|
||||||
if (opt.support && slice.supports) {
|
if (slice.supports) {
|
||||||
out.appendAll(slice.supports);
|
out.appendAll(slice.supports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ const CONSTANTS = {
|
||||||
* @property {number} sliceFillGrow - Fill boundary expansion in mm
|
* @property {number} sliceFillGrow - Fill boundary expansion in mm
|
||||||
* @property {number} sliceFillOverlap - Fill overlap with shells (0.0-0.8)
|
* @property {number} sliceFillOverlap - Fill overlap with shells (0.0-0.8)
|
||||||
* @property {number} sliceSolidMinArea - Minimum area in mm² for solid regions
|
* @property {number} sliceSolidMinArea - Minimum area in mm² for solid regions
|
||||||
|
* @property {number} sliceSolidMinThick - Miminum "thickness" ratio for culling thin solids
|
||||||
* @property {number} sliceSupportDensity - Support fill density (0.0-1.0)
|
* @property {number} sliceSupportDensity - Support fill density (0.0-1.0)
|
||||||
* @property {number} sliceSupportFill - Support fill angle (or auto if >= 1000)
|
* @property {number} sliceSupportFill - Support fill angle (or auto if >= 1000)
|
||||||
* @property {number} sliceSupportGap - Gap between support and part in mm
|
* @property {number} sliceSupportGap - Gap between support and part in mm
|
||||||
|
|
@ -266,6 +267,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
let indices = [];
|
let indices = [];
|
||||||
let heights = [];
|
let heights = [];
|
||||||
let healed = false;
|
let healed = false;
|
||||||
|
let slices; // set by decodeSlices()
|
||||||
|
|
||||||
// handle z cutting (floor method) and base flattening
|
// handle z cutting (floor method) and base flattening
|
||||||
let zPress = isBelt ? process.firstLayerFlatten || 0 : 0;
|
let zPress = isBelt ? process.firstLayerFlatten || 0 : 0;
|
||||||
|
|
@ -328,35 +330,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
.then(processSlices)
|
.then(processSlices)
|
||||||
.then(ondone);
|
.then(ondone);
|
||||||
|
|
||||||
// turn slicer raw output into Slice objects
|
// z index generator (bottom up)
|
||||||
function decodeSlices(output) {
|
|
||||||
// post process slices and re-incorporate missing meta-data
|
|
||||||
return output.slices.map(data => {
|
|
||||||
let { z, clip, lines, groups, changes } = data;
|
|
||||||
if (!data.tops) return null;
|
|
||||||
let slice = newSlice(z).addTops(data.tops, { minArea: CONSTANTS.MIN_POLY_AREA });
|
|
||||||
slice.index = indices.indexOf(z);
|
|
||||||
slice.height = heights[slice.index];
|
|
||||||
slice.clips = clip;
|
|
||||||
// do not warn on merging supports
|
|
||||||
if (changes) {
|
|
||||||
healed = true;
|
|
||||||
slice.changes = changes;
|
|
||||||
if (devel) {
|
|
||||||
console.log('slice healed', slice.index, slice.z, changes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (process.xray) {
|
|
||||||
slice.index = process.xray.shift();
|
|
||||||
slice.lines = lines;
|
|
||||||
slice.groups = groups;
|
|
||||||
slice.xray = slice.index;
|
|
||||||
}
|
|
||||||
return slice;
|
|
||||||
}).filter(s => s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// z index generator
|
|
||||||
function zGen(zopt) {
|
function zGen(zopt) {
|
||||||
if (process.xray) {
|
if (process.xray) {
|
||||||
return zopt.zIndexes;
|
return zopt.zIndexes;
|
||||||
|
|
@ -438,7 +412,53 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
return zi;
|
return zi;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processSlices(slices) {
|
// turn slicer raw output into Slice objects
|
||||||
|
// z index order should be bottom up at this point
|
||||||
|
async function decodeSlices(output) {
|
||||||
|
// post process slices and re-incorporate missing meta-data
|
||||||
|
slices = output.slices.map(data => {
|
||||||
|
let { z, clip, lines, groups, changes } = data;
|
||||||
|
if (!data.tops) return null;
|
||||||
|
let slice = newSlice(z).addTops(data.tops, { minArea: CONSTANTS.MIN_POLY_AREA });
|
||||||
|
slice.index = indices.indexOf(z);
|
||||||
|
slice.height = heights[slice.index];
|
||||||
|
slice.clips = clip;
|
||||||
|
// do not warn on merging supports
|
||||||
|
if (changes) {
|
||||||
|
healed = true;
|
||||||
|
slice.changes = changes;
|
||||||
|
if (devel) {
|
||||||
|
console.log('slice healed', slice.index, slice.z, changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.xray) {
|
||||||
|
slice.index = process.xray.shift();
|
||||||
|
slice.lines = lines;
|
||||||
|
slice.groups = groups;
|
||||||
|
slice.xray = slice.index;
|
||||||
|
}
|
||||||
|
return slice;
|
||||||
|
}).filter(s => s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate % complete and call onupdate()
|
||||||
|
function doupdate(index, from, to, msg) {
|
||||||
|
trackupdate(index / slices.length, from, to, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// slicing is the first 50% of the update "time"
|
||||||
|
function trackupdate(pct, from, to, msg) {
|
||||||
|
onupdate(0.5 + (from + (pct * (to - from))) * 0.5, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each slice, performe a function and call doupdate()
|
||||||
|
function forSlices(from, to, fn, msg) {
|
||||||
|
slices.forEach(slice => {
|
||||||
|
fn(slice);
|
||||||
|
doupdate(slice.index, from, to, msg)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process automatic and manual shadow-based support generation
|
* Process automatic and manual shadow-based support generation
|
||||||
*/
|
*/
|
||||||
|
|
@ -527,19 +547,17 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
slice.output().setLayer("shadow", 0xff0000).addPolys(shadow);
|
slice.output().setLayer("shadow", 0xff0000).addPolys(shadow);
|
||||||
trackupdate((++count/length), 0.10, 0.15, "support");
|
trackupdate((++count/length), 0.10, 0.15, "support");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process top and bottom layers or any other
|
* Process top and bottom layers or any other
|
||||||
* layers detected and marked for solid fill
|
* layers detected and marked for solid fill
|
||||||
*/
|
*/
|
||||||
function processSolidLayers() {
|
async function processSolidLayers() {
|
||||||
forSlices(0.15, 0.2, slice => {
|
forSlices(0.15, 0.2, slice => {
|
||||||
let range = slice.params;
|
let range = slice.params;
|
||||||
let isBottom = slice.index < bottomLayers;
|
let isBottom = slice.index < bottomLayers;
|
||||||
let isTop = topLayers && slice.index > slices.length - topLayers - 1;
|
let isTop = topLayers && slice.index > slices.length - topLayers - 1;
|
||||||
let isTopBase = isTop && slice.index === slices.length - topLayers;
|
|
||||||
let isDense = range.sliceFillSparse > CONSTANTS.DENSE_INFILL_THRESHOLD;
|
let isDense = range.sliceFillSparse > CONSTANTS.DENSE_INFILL_THRESHOLD;
|
||||||
let isSolid = (isBottom || ((isTop || isDense) && !vaseMode));
|
let isSolid = (isBottom || ((isTop || isDense) && !vaseMode));
|
||||||
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
||||||
|
|
@ -550,10 +568,6 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
if (slice.index === slices.length - 1) {
|
if (slice.index === slices.length - 1) {
|
||||||
slice.isFlatsLayer = true;
|
slice.isFlatsLayer = true;
|
||||||
}
|
}
|
||||||
if (isTopBase) {
|
|
||||||
// mark the first top solid supporting layer as a bridge
|
|
||||||
slice.isBridgeLayer = true;
|
|
||||||
}
|
|
||||||
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
||||||
}, "solid layers");
|
}, "solid layers");
|
||||||
}
|
}
|
||||||
|
|
@ -561,19 +575,20 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
/**
|
/**
|
||||||
* Process layer diffs and project solid areas
|
* Process layer diffs and project solid areas
|
||||||
*/
|
*/
|
||||||
function processLayerDiffs() {
|
async function processLayerDiffs() {
|
||||||
// boolean diff layers to detect bridges and flats
|
// boolean diff layers to detect bridges and flats
|
||||||
profileStart("delta");
|
profileStart("delta");
|
||||||
forSlices(0.2, 0.34, slice => {
|
forSlices(0.2, 0.33, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
let solidMinArea = params.sliceSolidMinArea;
|
let solidMinArea = params.sliceSolidMinArea;
|
||||||
|
let sliceMinThick = params.sliceSolidMinThick;
|
||||||
let sliceFillGrow = params.sliceFillGrow;
|
let sliceFillGrow = params.sliceFillGrow;
|
||||||
layerDiff(slice, { min: solidMinArea, grow: sliceFillGrow });
|
layerDiff(slice, { area: solidMinArea, grow: sliceFillGrow, thick: sliceMinThick });
|
||||||
}, "layer deltas");
|
}, "layer deltas");
|
||||||
profileEnd();
|
profileEnd();
|
||||||
// project bridges and flats up and down into part
|
// project bridges and flats up and down into part
|
||||||
profileStart("delta-project");
|
profileStart("delta-project");
|
||||||
forSlices(0.34, 0.35, slice => {
|
forSlices(0.33, 0.34, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
topLayers = params.sliceTopLayers || 0;
|
topLayers = params.sliceTopLayers || 0;
|
||||||
bottomLayers = params.sliceBottomLayers || 0;
|
bottomLayers = params.sliceBottomLayers || 0;
|
||||||
|
|
@ -582,6 +597,14 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
if (slice.flats) POLY.setZ(slice.flats, slice.z);
|
if (slice.flats) POLY.setZ(slice.flats, slice.z);
|
||||||
}, "layer deltas");
|
}, "layer deltas");
|
||||||
profileEnd();
|
profileEnd();
|
||||||
|
// union solid areas
|
||||||
|
profileStart("solid-union");
|
||||||
|
forSlices(0.34, 0.35, slice => {
|
||||||
|
if (slice.solids) {
|
||||||
|
slice.solids = POLY.union(slice.solids, 0, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
profileEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -682,7 +705,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
/**
|
/**
|
||||||
* Process brick/interleave mode slicing
|
* Process brick/interleave mode slicing
|
||||||
*/
|
*/
|
||||||
function processBrickMode() {
|
async function processBrickMode() {
|
||||||
let indices = slices.map(s => s.index);
|
let indices = slices.map(s => s.index);
|
||||||
let first = indices[1];
|
let first = indices[1];
|
||||||
let last = indices[indices.length - 2];
|
let last = indices[indices.length - 2];
|
||||||
|
|
@ -728,31 +751,26 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
slices.forEach((s,i) => s.index = i);
|
slices.forEach((s,i) => s.index = i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate % complete and call onupdate()
|
async function renderSlices() {
|
||||||
function doupdate(index, from, to, msg) {
|
forSlices(0.9, 1.0, slice => {
|
||||||
trackupdate(index / slices.length, from, to, msg);
|
let params = slice.params || process;
|
||||||
}
|
layerRender(slice, params, {
|
||||||
|
dark: controller.dark,
|
||||||
function trackupdate(pct, from, to, msg) {
|
devel: controller.devel,
|
||||||
onupdate(0.5 + (from + (pct * (to - from))) * 0.5, msg);
|
renderContext
|
||||||
}
|
|
||||||
|
|
||||||
// for each slice, performe a function and call doupdate()
|
|
||||||
function forSlices(from, to, fn, msg) {
|
|
||||||
slices.forEach(slice => {
|
|
||||||
fn(slice);
|
|
||||||
doupdate(slice.index, from, to, msg)
|
|
||||||
});
|
});
|
||||||
|
}, "render");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function processSlices() {
|
||||||
// alert non-manifold parts
|
// alert non-manifold parts
|
||||||
if (healed) {
|
if (healed) {
|
||||||
onupdate(null, null, "part may not be manifold");
|
onupdate(null, null, "part may not be manifold");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reverse slices to perform these ops top-down
|
||||||
// remove all empty slices above part but leave below
|
// remove all empty slices above part but leave below
|
||||||
// for multi-part (multi-extruder) setups where the void is ok
|
// for multi-part (multi-extruder) setups where the void is ok
|
||||||
// also reverse because slicing occurs bottom-up
|
|
||||||
let found = false;
|
let found = false;
|
||||||
slices = slices.reverse().filter(slice => {
|
slices = slices.reverse().filter(slice => {
|
||||||
if (slice.tops.length) {
|
if (slice.tops.length) {
|
||||||
|
|
@ -762,40 +780,48 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
}
|
}
|
||||||
}).reverse();
|
}).reverse();
|
||||||
|
|
||||||
|
// inject one empty slice at the top so that
|
||||||
|
// top layer flats are detected properly
|
||||||
|
slices.push(newSlice(slices.peek().index + 1));
|
||||||
|
|
||||||
|
// attach slices to widget since the
|
||||||
|
// variable will not be replaced after this
|
||||||
|
widget.slices = slices;
|
||||||
|
|
||||||
|
// exit if no slices detected with tops
|
||||||
|
if (!slices || slices.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// connect slices into linked list for island/bridge projections
|
// connect slices into linked list for island/bridge projections
|
||||||
for (let i=1; i<slices.length; i++) {
|
for (let i=1; i<slices.length; i++) {
|
||||||
slices[i-1].up = slices[i];
|
slices[i-1].up = slices[i];
|
||||||
slices[i].down = slices[i-1];
|
slices[i].down = slices[i-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.slices = slices;
|
|
||||||
|
|
||||||
if (!slices || slices.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach range params to each slice
|
// attach range params to each slice
|
||||||
|
// should slice.index be recalculated after filtering?
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
slice.params = getRangeParameters(process, slice.index);
|
slice.params = getRangeParameters(process, slice.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset for solids, support projections
|
// reset solids, support projections, and other annotations
|
||||||
// and other annotations
|
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
slice.widget = widget;
|
|
||||||
slice.extruder = extruder;
|
slice.extruder = extruder;
|
||||||
slice.solids = [];
|
slice.solids = [];
|
||||||
|
slice.widget = widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
// new auto support demonstrator using cast shadow
|
// support generation using either
|
||||||
|
// enclosed shadow or manual painted supports
|
||||||
await processSupports();
|
await processSupports();
|
||||||
|
|
||||||
// process solid layers (top/bottom)
|
// process solid layers (top/bottom)
|
||||||
processSolidLayers();
|
await processSolidLayers();
|
||||||
|
|
||||||
// add lead in anchor when specified in belt mode (but not for synths)
|
// add lead in anchor when specified in belt mode (but not for synths)
|
||||||
if (isBelt) {
|
if (isBelt) {
|
||||||
// Generate belt anchor
|
// generate belt anchor
|
||||||
generateBeltAnchor({
|
generateBeltAnchor({
|
||||||
slices,
|
slices,
|
||||||
widget,
|
widget,
|
||||||
|
|
@ -806,7 +832,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
extruder
|
extruder
|
||||||
});
|
});
|
||||||
|
|
||||||
// Calculate smin for pooch text embossing
|
// calculate smin for pooch text embossing
|
||||||
let smin = Infinity;
|
let smin = Infinity;
|
||||||
for (let slice of slices) {
|
for (let slice of slices) {
|
||||||
if (slice.belt && slice.belt.miny < smin) {
|
if (slice.belt && slice.belt.miny < smin) {
|
||||||
|
|
@ -814,7 +840,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental emboss text on flat underside
|
// experimental emboss text on flat underside
|
||||||
embossBeltPooch({
|
embossBeltPooch({
|
||||||
slices,
|
slices,
|
||||||
widget,
|
widget,
|
||||||
|
|
@ -828,14 +854,11 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
// layer boolean diffs need to be computed to find flat areas to fill
|
// layer boolean diffs need to be computed to find flat areas to fill
|
||||||
// and overhangs that need to be supported. these are stored in flats
|
// and overhangs that need to be supported. these are stored in flats
|
||||||
// and bridges, projected up/down, and merged into an array of solids
|
// and bridges, projected up/down, and merged into an array of solids
|
||||||
if (!vaseMode) {
|
|
||||||
processLayerDiffs();
|
|
||||||
await processSolidFills();
|
|
||||||
}
|
|
||||||
|
|
||||||
// for "real" objects, fill the remaining voids with sparse fill
|
// for "real" objects, fill the remaining voids with sparse fill
|
||||||
// sparse layers only present when non-vase mode and sparse % > 0
|
// sparse layers only present when non-vase mode and sparse % > 0
|
||||||
if (!vaseMode) {
|
if (!vaseMode) {
|
||||||
|
await processLayerDiffs();
|
||||||
|
await processSolidFills();
|
||||||
await processSparseInfill();
|
await processSparseInfill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -846,19 +869,12 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
|
|
||||||
// brick/interleave mode processing
|
// brick/interleave mode processing
|
||||||
if (isBrick) {
|
if (isBrick) {
|
||||||
processBrickMode();
|
await processBrickMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
// render if not explicitly disabled
|
// render if not explicitly disabled
|
||||||
if (render) {
|
if (render) {
|
||||||
forSlices(0.9, 1.0, slice => {
|
await renderSlices();
|
||||||
let params = slice.params || process;
|
|
||||||
layerRender(slice, params, {
|
|
||||||
dark: controller.dark,
|
|
||||||
devel: controller.devel,
|
|
||||||
renderContext
|
|
||||||
});
|
|
||||||
}, "render");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBelt) {
|
if (isBelt) {
|
||||||
|
|
@ -913,14 +929,15 @@ function bound(v,min,max) {
|
||||||
*/
|
*/
|
||||||
function layerRender(slice, params, opt = {}) {
|
function layerRender(slice, params, opt = {}) {
|
||||||
const { dark, devel, renderContext: ctx } = opt;
|
const { dark, devel, renderContext: ctx } = opt;
|
||||||
|
const { offset, isThin, isFlat } = ctx;
|
||||||
|
|
||||||
const Color = dark ? COLOR_DARK : COLOR;
|
const Color = dark ? COLOR_DARK : COLOR;
|
||||||
const output = slice.output();
|
const output = slice.output();
|
||||||
const height = slice.height / 2;
|
const height = slice.height / 2;
|
||||||
const solidWidth = params.sliceFillWidth || 1;
|
const solidWidth = params.sliceFillWidth || 1;
|
||||||
const { offset, isThin, isFlat } = ctx;
|
|
||||||
|
|
||||||
if (slice.tops) // missing for supports
|
if (slice.tops?.length)
|
||||||
slice.tops.forEach(top => {
|
for (let top of slice.tops) {
|
||||||
if (isThin) output
|
if (isThin) output
|
||||||
.setLayer('part', Color.part)
|
.setLayer('part', Color.part)
|
||||||
.addPolys([top.poly]);
|
.addPolys([top.poly]);
|
||||||
|
|
@ -970,7 +987,7 @@ function layerRender(slice, params, opt = {}) {
|
||||||
// .setLayer('last', { face: 0, line: 0x008888, check: 0x008888 })
|
// .setLayer('last', { face: 0, line: 0x008888, check: 0x008888 })
|
||||||
// .addPolys(top.last);
|
// .addPolys(top.last);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
if (isThin && devel) {
|
if (isThin && devel) {
|
||||||
if (slice.solids?.length) output
|
if (slice.solids?.length) output
|
||||||
|
|
@ -1089,12 +1106,6 @@ export function layerProcessTops(slice, count, offset1, offsetN, fillOffset, opt
|
||||||
* Create an entirely solid layer by filling all top polygons
|
* Create an entirely solid layer by filling all top polygons
|
||||||
* with an alternating pattern.
|
* with an alternating pattern.
|
||||||
*
|
*
|
||||||
* @param {number} linewidth
|
|
||||||
* @param {number} angle
|
|
||||||
* @param {number} density
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Create solid fill layer by filling all top polygons with alternating pattern
|
|
||||||
* @param {Object} args - Options object
|
* @param {Object} args - Options object
|
||||||
* @param {Slice} args.slice - Slice to fill
|
* @param {Slice} args.slice - Slice to fill
|
||||||
* @param {number} args.spacing - Fill line spacing in mm
|
* @param {number} args.spacing - Fill line spacing in mm
|
||||||
|
|
@ -1299,33 +1310,38 @@ function layerSparseFill(slice, options = {}) {
|
||||||
* 'expand' is used for top offsets in SLA mode
|
* 'expand' is used for top offsets in SLA mode
|
||||||
*/
|
*/
|
||||||
export function layerDiff(slice, options = {}) {
|
export function layerDiff(slice, options = {}) {
|
||||||
const { sla, fakedown, grow, min } = options;
|
const { sla, grow, area, thick } = options;
|
||||||
if ((slice.index <= 0 && !fakedown) || slice.xray) {
|
|
||||||
|
if (slice.index < 0 || slice.xray) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const top = slice,
|
|
||||||
down = slice.down || (fakedown ? newSlice(-1) : null),
|
let boundary = !(slice.up && slice.down),
|
||||||
|
top = slice,
|
||||||
|
down = slice.down || newSlice(-1),
|
||||||
topInner = sla ? top.topPolys() : top.topInners(),
|
topInner = sla ? top.topPolys() : top.topInners(),
|
||||||
downInner = sla ? down.topPolys() : down.topInners(),
|
downInner = sla ? down.topPolys() : down.topInners(),
|
||||||
bridges = top.bridges = [],
|
bridges = top.bridges = [],
|
||||||
flats = down.flats = [];
|
flats = down.flats = [];
|
||||||
|
|
||||||
// skip diffing layers that are identical
|
// skip diffing layers that are identical
|
||||||
if (slice.fingerprintSame(down)) {
|
if (!boundary && slice.fingerprintSame(down)) {
|
||||||
top.bridges = bridges;
|
top.bridges = bridges;
|
||||||
down.flats = flats;
|
down.flats = flats;
|
||||||
|
// console.log(slice.z, 'layer fingerprint match');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newBridges = [];
|
let newBridges = [];
|
||||||
let newFlats = [];
|
let newFlats = [];
|
||||||
|
|
||||||
POLY.subtract(topInner, downInner, newBridges, newFlats, slice.z, min, {
|
POLY.subtract(topInner, downInner, newBridges, newFlats, slice.z, area, {
|
||||||
wasm: true
|
wasm: true
|
||||||
});
|
});
|
||||||
|
|
||||||
newBridges = newBridges.filter(p => p.areaDeep() >= min);
|
// console.log(slice.z, { newBridges, newFlats });
|
||||||
newFlats = newFlats.filter(p => p.areaDeep() >= min);
|
newBridges = newBridges.filter(p => p.areaDeep() >= area && p.thickness(true) >= thick);
|
||||||
|
newFlats = newFlats.filter(p => p.areaDeep() >= area && p.thickness(true) >= thick);
|
||||||
|
|
||||||
if (grow > 0 && newBridges.length) {
|
if (grow > 0 && newBridges.length) {
|
||||||
newBridges = POLY.offset(newBridges, grow);
|
newBridges = POLY.offset(newBridges, grow);
|
||||||
|
|
@ -1652,20 +1668,21 @@ export function projectFlats(slice, count, expand) {
|
||||||
if (slice.flats?.length) {
|
if (slice.flats?.length) {
|
||||||
slice.finishSolids = true;
|
slice.finishSolids = true;
|
||||||
slice.isFlatsLayer = true;
|
slice.isFlatsLayer = true;
|
||||||
const flats = expand ? POLY.expand(slice.flats, expand) : slice.flats;
|
const polys = expand ? POLY.expand(slice.flats, expand) : slice.flats;
|
||||||
projectSolid({ slice, polys: flats, count, up: false, first: true });
|
projectSolid({ slice, polys, count, up: false, first: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* project top bridges up into part
|
* project top bridges up into part
|
||||||
*/
|
*/
|
||||||
export function projectBridges(slice, count) {
|
export function projectBridges(slice, count, expand) {
|
||||||
if (!slice.up || !slice.bridges) return;
|
if (!slice.up || !slice.bridges) return;
|
||||||
// these flats are marked for finishing print speed
|
// these bridges are marked for finishing print speed
|
||||||
if (slice.bridges?.length) {
|
if (slice.bridges?.length) {
|
||||||
slice.finishSolids = true;
|
slice.finishSolids = true;
|
||||||
slice.isBridgeLayer = true;
|
slice.isBridgeLayer = true;
|
||||||
|
const polys = expand ? POLY.expand(slice.bridges, expand) : slice.bridges;
|
||||||
|
projectSolid({ slice, polys, count, up: true, first: true });
|
||||||
}
|
}
|
||||||
projectSolid({ slice, polys: slice.bridges, count, up: true, first: true });
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -777,7 +777,3 @@
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
<div id="opapik">
|
|
||||||
<label lk="ex_akey">key</label>
|
|
||||||
<input id="octo-apik" size="20" />
|
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,7 @@ self.lang['en-us'] = {
|
||||||
fi_over_l: ["overlap between shells and solid or sparse infill. can improve bonding to shells","as fraction of line width","0.0 - 1.0"],
|
fi_over_l: ["overlap between shells and solid or sparse infill. can improve bonding to shells","as fraction of line width","0.0 - 1.0"],
|
||||||
fi_angl_s: "start angle",
|
fi_angl_s: "start angle",
|
||||||
fi_angl_l: ["starting angle in degrees","90 degrees added to ","each following layer","applies only to solid layers"],
|
fi_angl_l: ["starting angle in degrees","90 degrees added to ","each following layer","applies only to solid layers"],
|
||||||
fi_grow_s: "area expand",
|
fi_grow_s: "expand",
|
||||||
fi_grow_l: ["expand projected solids","can help with solid areas","over sparse infill areas","units in millimeters"],
|
fi_grow_l: ["expand projected solids","can help with solid areas","over sparse infill areas","units in millimeters"],
|
||||||
|
|
||||||
// FDM FILL
|
// FDM FILL
|
||||||
|
|
@ -891,8 +891,10 @@ self.lang['en-us'] = {
|
||||||
ad_rdwl_l: ["time between re-engaging","filament and movement","in milliseconds"],
|
ad_rdwl_l: ["time between re-engaging","filament and movement","in milliseconds"],
|
||||||
ad_scst_s: "shell coast",
|
ad_scst_s: "shell coast",
|
||||||
ad_scst_l: ["non-printing end","of perimeter shells","in millimeters"],
|
ad_scst_l: ["non-printing end","of perimeter shells","in millimeters"],
|
||||||
ad_msol_s: "area filter",
|
ad_msol_s: "min area",
|
||||||
ad_msol_l: ["minimum area (mm^2)","bridges and flats less than this are culled"],
|
ad_msol_l: ["minimum area (mm^2)","bridges and flats less than this are culled"],
|
||||||
|
ad_mthk_s: "min thick",
|
||||||
|
ad_mthk_l: ["minimum area thickness","which is a ratio of area to perimeter","for layer differences which are used","to find bridges and flat areas","values from 0.0 - 0.5 work best"],
|
||||||
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_s: "max flowrate",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue