reflow / rename / document fdm slicing
This commit is contained in:
parent
97fbcb109e
commit
9a463881d1
4 changed files with 178 additions and 160 deletions
|
|
@ -9,14 +9,17 @@ import { slicer } from '../../../geo/slicer.js';
|
||||||
import { Point } from '../../../geo/point.js';
|
import { Point } from '../../../geo/point.js';
|
||||||
import { Slope } from '../../../geo/slope.js';
|
import { Slope } from '../../../geo/slope.js';
|
||||||
|
|
||||||
slicer.slicePost.FDM = slicePost;
|
// tell the slicer what to do in FDM mode
|
||||||
|
// with slices (post processing). the post
|
||||||
|
// processor may depend on worker / minion context
|
||||||
|
slicer.slicePost.FDM = slicerPostProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* may run in minion or worker context. do not create objects
|
* may run in minion or worker context. do not create objects
|
||||||
* that will not quickly encode in threaded mode. add to existing
|
* that will not quickly encode in threaded mode. add to existing
|
||||||
* data object. return is ignored.
|
* data object. return is ignored.
|
||||||
*/
|
*/
|
||||||
export function slicePost(data, options) {
|
function slicerPostProcessor(data, options) {
|
||||||
const { groups, z } = data;
|
const { groups, z } = data;
|
||||||
const { post_args, useAssembly, zIndexes } = options;
|
const { post_args, useAssembly, zIndexes } = options;
|
||||||
const { process, vaseMode } = post_args;
|
const { process, vaseMode } = post_args;
|
||||||
|
|
@ -81,7 +84,7 @@ export function slicePost(data, options) {
|
||||||
const nutops = [];
|
const nutops = [];
|
||||||
// co-locate shell processing with top generation in slicer
|
// co-locate shell processing with top generation in slicer
|
||||||
for (let top of data.tops) {
|
for (let top of data.tops) {
|
||||||
nutops.push(doTopShells(z, top, count, offset/2, offset, fillOff, {
|
nutops.push(layerProcessTop(z, top, count, offset/2, offset, fillOff, {
|
||||||
thinType,
|
thinType,
|
||||||
vaseMode,
|
vaseMode,
|
||||||
useAssembly
|
useAssembly
|
||||||
|
|
@ -687,7 +690,7 @@ function trace_noodle(noodle, noodleWidth, minR, midR, maxR, opt = {}) {
|
||||||
* may run in minion or worker context. performs shell offsetting
|
* may run in minion or worker context. performs shell offsetting
|
||||||
* including thin wall detection when enabled
|
* including thin wall detection when enabled
|
||||||
*/
|
*/
|
||||||
export function doTopShells(z, top, count, offset1, offsetN, fillOffset, opt = {}) {
|
export function layerProcessTop(z, top, count, offset1, offsetN, fillOffset, opt = {}) {
|
||||||
// pretend we're a top object in minions
|
// pretend we're a top object in minions
|
||||||
if (!top.poly) {
|
if (!top.poly) {
|
||||||
top = { poly: top };
|
top = { poly: top };
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
import { base, util } from '../../../geo/base.js';
|
import { layerProcessTop } from './post.js';
|
||||||
import { doTopShells } from './post.js';
|
|
||||||
import { fill, fill_fixed } from './fill.js';
|
import { fill, fill_fixed } from './fill.js';
|
||||||
import { generateBeltAnchor, embossBeltPooch, finalizeBeltBounds } from './belt.js';
|
import { generateBeltAnchor, embossBeltPooch, finalizeBeltBounds } from './belt.js';
|
||||||
import { getRangeParameters } from './driver.js';
|
import { getRangeParameters } from './driver.js';
|
||||||
|
|
@ -10,8 +9,7 @@ import { newPolygon } from '../../../geo/polygon.js';
|
||||||
import { newSlice } from '../../core/slice.js';
|
import { newSlice } from '../../core/slice.js';
|
||||||
import { polygons as POLY } from '../../../geo/polygons.js';
|
import { polygons as POLY } from '../../../geo/polygons.js';
|
||||||
import { slice, sliceZ } from '../../../geo/slicer.js';
|
import { slice, sliceZ } from '../../../geo/slicer.js';
|
||||||
|
import { util } from '../../../geo/base.js';
|
||||||
const { config } = base;
|
|
||||||
|
|
||||||
const CONSTANTS = {
|
const CONSTANTS = {
|
||||||
// Support fill
|
// Support fill
|
||||||
|
|
@ -174,55 +172,6 @@ function vopt(opt, ctx) {
|
||||||
return opt;
|
return opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* DRIVER SLICE CONTRACT
|
|
||||||
*
|
|
||||||
* global slice operation run after all individual widget slice() complete
|
|
||||||
*/
|
|
||||||
export function slicePost(settings, onupdate) {
|
|
||||||
// future home of brim and anchor generation
|
|
||||||
let widgets = Object.values(self.kiri_worker.cache)
|
|
||||||
.filter(w => !w.meta.disabled)
|
|
||||||
.sort((a,b) => {
|
|
||||||
return a.slices[0].z - b.slices[0].z
|
|
||||||
});
|
|
||||||
// assign grid_id which can be embedded in gcode and
|
|
||||||
// used by the controller to cancel objects during print
|
|
||||||
let { bounds } = settings;
|
|
||||||
for (let widget of widgets) {
|
|
||||||
let { pos, box } = widget.track;
|
|
||||||
// calculate top/left coordinate for widget
|
|
||||||
// relative to bounding box for all widgets
|
|
||||||
let tl = {
|
|
||||||
x: Math.round((pos.x - box.w/2 - bounds.min.x) / 10) + 1,
|
|
||||||
y: Math.round((pos.y - box.h/2 - bounds.min.y) / 10) + 1
|
|
||||||
};
|
|
||||||
widget.track.grid_id = tl.x * 100 + tl.y;
|
|
||||||
}
|
|
||||||
// count extruders used
|
|
||||||
let ext = [];
|
|
||||||
for (let w of widgets) {
|
|
||||||
if (w.anno && w.anno.extruder >= 0) {
|
|
||||||
let e = w.anno.extruder;
|
|
||||||
if (ext.indexOf(e) < 0) {
|
|
||||||
ext.push(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// sort widgets by first slice Z
|
|
||||||
widgets.sort((a,b) => {
|
|
||||||
return a.slices[0].z - b.slices[0].z;
|
|
||||||
});
|
|
||||||
// give first widget a pass since it should have the anchor
|
|
||||||
widgets.shift();
|
|
||||||
// remove anchor slices from other widgets (only with multi-material)
|
|
||||||
if (ext.length > 1) {
|
|
||||||
for (let w of widgets) {
|
|
||||||
w.slices = w.slices.filter(s => s.index >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DRIVER SLICE CONTRACT
|
* DRIVER SLICE CONTRACT
|
||||||
*
|
*
|
||||||
|
|
@ -258,7 +207,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
shellOffset = lineWidth,
|
shellOffset = lineWidth,
|
||||||
fillSpacing = lineWidth,
|
fillSpacing = lineWidth,
|
||||||
fillOffset = lineWidth * fillOffsetMult,
|
fillOffset = lineWidth * fillOffsetMult,
|
||||||
clipOffset = process.sliceSupportOffset,
|
clipOffset = process.sliceSupportOffset + lineWidth / 2,
|
||||||
sliceFillAngle = process.sliceFillAngle,
|
sliceFillAngle = process.sliceFillAngle,
|
||||||
supportDensity = process.sliceSupportDensity,
|
supportDensity = process.sliceSupportDensity,
|
||||||
{ sliceCompInner, sliceCompOuter } = process;
|
{ sliceCompInner, sliceCompOuter } = process;
|
||||||
|
|
@ -344,6 +293,8 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create Slice objects for specified list of Z heights
|
||||||
|
// zGen() produces the list (or empty for slicer auto-detected)
|
||||||
slice(points, {
|
slice(points, {
|
||||||
strict: true,
|
strict: true,
|
||||||
debug: process.xray,
|
debug: process.xray,
|
||||||
|
|
@ -372,7 +323,13 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
onupdate(update) {
|
onupdate(update) {
|
||||||
return onupdate(0.0 + update * 0.5)
|
return onupdate(0.0 + update * 0.5)
|
||||||
}
|
}
|
||||||
}).then((output) => {
|
})
|
||||||
|
.then(decodeSlices)
|
||||||
|
.then(processSlices)
|
||||||
|
.then(ondone);
|
||||||
|
|
||||||
|
// turn slicer raw output into Slice objects
|
||||||
|
function decodeSlices(output) {
|
||||||
// post process slices and re-incorporate missing meta-data
|
// post process slices and re-incorporate missing meta-data
|
||||||
return output.slices.map(data => {
|
return output.slices.map(data => {
|
||||||
let { z, clip, lines, groups, changes } = data;
|
let { z, clip, lines, groups, changes } = data;
|
||||||
|
|
@ -397,9 +354,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
}
|
}
|
||||||
return slice;
|
return slice;
|
||||||
}).filter(s => s);
|
}).filter(s => s);
|
||||||
}).then(slices => {
|
}
|
||||||
return onSliceDone(slices);
|
|
||||||
}).then(ondone);
|
|
||||||
|
|
||||||
// z index generator
|
// z index generator
|
||||||
function zGen(zopt) {
|
function zGen(zopt) {
|
||||||
|
|
@ -483,9 +438,9 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
return zi;
|
return zi;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSliceDone(slices) {
|
async function processSlices(slices) {
|
||||||
/**
|
/**
|
||||||
* Process automatic shadow-based support generation
|
* Process shadow-based support generation
|
||||||
*/
|
*/
|
||||||
async function processAutoSupport() {
|
async function processAutoSupport() {
|
||||||
if (process.sliceSupportType !== 'automatic') {
|
if (process.sliceSupportType !== 'automatic') {
|
||||||
|
|
@ -504,17 +459,22 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
stack.sort((a,b) => b.z - a.z); // accumulate deltas
|
stack.sort((a,b) => b.z - a.z); // accumulate deltas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. accumulate / union shadow coverage top down
|
||||||
|
// 2. trim to area outside slice.clips
|
||||||
|
|
||||||
for (let slice of stack) {
|
for (let slice of stack) {
|
||||||
let shadow = await widget.shadowAt(slice.z, true);
|
let shadow = await widget.shadowAt(slice.z, true);
|
||||||
if (process.sliceSupportExtra) {
|
if (process.sliceSupportExtra) {
|
||||||
shadow = POLY.offset(shadow, process.sliceSupportExtra);
|
shadow = POLY.offset(shadow, process.sliceSupportExtra);
|
||||||
}
|
}
|
||||||
slice.supports = shadow;
|
slice.supports = shadow;
|
||||||
|
slice.output().setLayer("shadow", 0xff0000).addPolys(shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process solid layer fills (top/bottom layers)
|
* Process top and bottom layers or any other
|
||||||
|
* layers detected and marked for solid fill
|
||||||
*/
|
*/
|
||||||
function processSolidLayers() {
|
function processSolidLayers() {
|
||||||
forSlices(0.15, 0.2, slice => {
|
forSlices(0.15, 0.2, slice => {
|
||||||
|
|
@ -527,7 +487,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
let solidWidth = isSolid ? range.sliceFillWidth || 1 : 0;
|
||||||
if (solidWidth) {
|
if (solidWidth) {
|
||||||
let fillSpace = fillSpacing * solidWidth;
|
let fillSpace = fillSpacing * solidWidth;
|
||||||
doSolidLayerFill({ slice, spacing: fillSpace, angle: sliceFillAngle });
|
layerMakeSolid({ slice, spacing: fillSpace, angle: sliceFillAngle });
|
||||||
}
|
}
|
||||||
if (slice.index === slices.length - 1) {
|
if (slice.index === slices.length - 1) {
|
||||||
slice.isFlatsLayer = true;
|
slice.isFlatsLayer = true;
|
||||||
|
|
@ -544,15 +504,16 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
* Process layer diffs and project solid areas
|
* Process layer diffs and project solid areas
|
||||||
*/
|
*/
|
||||||
function processLayerDiffs() {
|
function processLayerDiffs() {
|
||||||
|
// boolean diff layers to detect bridges and flats
|
||||||
profileStart("delta");
|
profileStart("delta");
|
||||||
forSlices(0.2, 0.34, slice => {
|
forSlices(0.2, 0.34, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
let solidMinArea = params.sliceSolidMinArea;
|
let solidMinArea = params.sliceSolidMinArea;
|
||||||
let sliceFillGrow = params.sliceFillGrow;
|
let sliceFillGrow = params.sliceFillGrow;
|
||||||
doDiff(slice, { min: solidMinArea, grow: sliceFillGrow });
|
layerDiff(slice, { min: solidMinArea, grow: sliceFillGrow });
|
||||||
}, "layer deltas");
|
}, "layer deltas");
|
||||||
profileEnd();
|
profileEnd();
|
||||||
|
// project bridges and flats up and down into part
|
||||||
profileStart("delta-project");
|
profileStart("delta-project");
|
||||||
forSlices(0.34, 0.35, slice => {
|
forSlices(0.34, 0.35, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
|
|
@ -560,6 +521,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
bottomLayers = params.sliceBottomLayers || 0;
|
bottomLayers = params.sliceBottomLayers || 0;
|
||||||
if (topLayers) projectFlats(slice, topLayers);
|
if (topLayers) projectFlats(slice, topLayers);
|
||||||
if (bottomLayers) projectBridges(slice, bottomLayers);
|
if (bottomLayers) projectBridges(slice, bottomLayers);
|
||||||
|
if (slice.flats) POLY.setZ(slice.flats, slice.z);
|
||||||
}, "layer deltas");
|
}, "layer deltas");
|
||||||
profileEnd();
|
profileEnd();
|
||||||
}
|
}
|
||||||
|
|
@ -572,11 +534,10 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
let promises = isConcurrent ? [] : undefined;
|
let promises = isConcurrent ? [] : undefined;
|
||||||
forSlices(0.35, promises ? 0.4 : 0.5, slice => {
|
forSlices(0.35, promises ? 0.4 : 0.5, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
let first = slice.index === 0;
|
|
||||||
let solidWidth = params.sliceFillWidth || 1;
|
let solidWidth = params.sliceFillWidth || 1;
|
||||||
let fillSpace = fillSpacing * solidWidth;
|
let fillSpace = fillSpacing * solidWidth;
|
||||||
let solidMinArea = params.sliceSolidMinArea;
|
let solidMinArea = params.sliceSolidMinArea;
|
||||||
doSolidsFill({ slice, spacing: fillSpace, angle: sliceFillAngle, minArea: solidMinArea, promises });
|
layerFillSolids({ slice, spacing: fillSpace, angle: sliceFillAngle, minArea: solidMinArea, promises });
|
||||||
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
sliceFillAngle = (sliceFillAngle + 90.0) % 360;
|
||||||
}, "fill solids");
|
}, "fill solids");
|
||||||
// very last layer (top) is set to finish solid rate
|
// very last layer (top) is set to finish solid rate
|
||||||
|
|
@ -601,7 +562,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newType = params.sliceFillType;
|
let newType = params.sliceFillType;
|
||||||
doSparseLayerFill(slice, {
|
layerSparseFill(slice, {
|
||||||
settings,
|
settings,
|
||||||
process,
|
process,
|
||||||
device,
|
device,
|
||||||
|
|
@ -642,7 +603,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
forSlices(0.8, promises ? 0.88 : 0.9, slice => {
|
forSlices(0.8, promises ? 0.88 : 0.9, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
let density = params.sliceSupportDensity;
|
let density = params.sliceSupportDensity;
|
||||||
doSupportFill({
|
layerSupportFill({
|
||||||
angle: process.sliceSupportFill,
|
angle: process.sliceSupportFill,
|
||||||
density,
|
density,
|
||||||
gap: process.sliceSupportGap,
|
gap: process.sliceSupportGap,
|
||||||
|
|
@ -765,9 +726,6 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
slice.params = getRangeParameters(process, slice.index);
|
slice.params = getRangeParameters(process, slice.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not hint polygon fill longer than a max span length
|
|
||||||
config.hint_len_max = util.sqr(process.sliceBridgeMax);
|
|
||||||
|
|
||||||
// reset for solids, support projections
|
// reset for solids, support projections
|
||||||
// and other annotations
|
// and other annotations
|
||||||
slices.forEach(slice => {
|
slices.forEach(slice => {
|
||||||
|
|
@ -781,8 +739,6 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
|
|
||||||
// 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) {
|
||||||
let { slope } = widget.belt;
|
|
||||||
|
|
||||||
// Generate belt anchor
|
// Generate belt anchor
|
||||||
generateBeltAnchor({
|
generateBeltAnchor({
|
||||||
slices,
|
slices,
|
||||||
|
|
@ -841,7 +797,7 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
||||||
if (render) {
|
if (render) {
|
||||||
forSlices(0.9, 1.0, slice => {
|
forSlices(0.9, 1.0, slice => {
|
||||||
let params = slice.params || process;
|
let params = slice.params || process;
|
||||||
doRender(slice, params, {
|
layerRender(slice, params, {
|
||||||
dark: controller.dark,
|
dark: controller.dark,
|
||||||
devel: controller.devel,
|
devel: controller.devel,
|
||||||
renderContext
|
renderContext
|
||||||
|
|
@ -899,7 +855,7 @@ function bound(v,min,max) {
|
||||||
* @param {boolean} opt.devel - Enable development visualization
|
* @param {boolean} opt.devel - Enable development visualization
|
||||||
* @param {RenderContext} opt.renderContext - Rendering context
|
* @param {RenderContext} opt.renderContext - Rendering context
|
||||||
*/
|
*/
|
||||||
function doRender(slice, params, opt = {}) {
|
function layerRender(slice, params, opt = {}) {
|
||||||
const { dark, devel, renderContext: ctx } = opt;
|
const { dark, devel, renderContext: ctx } = opt;
|
||||||
const Color = dark ? COLOR_DARK : COLOR;
|
const Color = dark ? COLOR_DARK : COLOR;
|
||||||
const output = slice.output();
|
const output = slice.output();
|
||||||
|
|
@ -961,15 +917,15 @@ function doRender(slice, params, opt = {}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isThin && devel) {
|
if (isThin && devel) {
|
||||||
if (slice.solids && slice.solids.length) output
|
if (slice.solids?.length) output
|
||||||
.setLayer("solids", { face: 0xbbbb00, check: 0xbbbb00 })
|
.setLayer("solids", { face: 0xbbbb00, check: 0xbbbb00 })
|
||||||
.addAreas(slice.solids);
|
.addAreas(slice.solids);
|
||||||
|
|
||||||
if (slice.bridges && slice.bridges.length) output
|
if (slice.bridges?.length) output
|
||||||
.setLayer("bridges", { face: 0x00cccc, line: 0x00cccc, check: 0x00cccc })
|
.setLayer("bridges", { face: 0x00cccc, line: 0x00cccc, check: 0x00cccc })
|
||||||
.addAreas(slice.bridges);
|
.addAreas(slice.bridges);
|
||||||
|
|
||||||
if (slice.flats && slice.flats.length) output
|
if (slice.flats?.length) output
|
||||||
.setLayer("flats", { face: 0xaa00aa, line: 0xaa00aa, check: 0xaa00aa })
|
.setLayer("flats", { face: 0xaa00aa, line: 0xaa00aa, check: 0xaa00aa })
|
||||||
.addAreas(slice.flats);
|
.addAreas(slice.flats);
|
||||||
}
|
}
|
||||||
|
|
@ -1004,6 +960,55 @@ function doRender(slice, params, opt = {}) {
|
||||||
// console.log(slice.index, slice.render.stats);
|
// console.log(slice.index, slice.render.stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DRIVER SLICE CONTRACT
|
||||||
|
*
|
||||||
|
* global slice operation run after all individual widget slice() complete
|
||||||
|
*/
|
||||||
|
export function slicePost(settings, onupdate) {
|
||||||
|
// future home of brim and anchor generation
|
||||||
|
let widgets = Object.values(self.kiri_worker.cache)
|
||||||
|
.filter(w => !w.meta.disabled)
|
||||||
|
.sort((a,b) => {
|
||||||
|
return a.slices[0].z - b.slices[0].z
|
||||||
|
});
|
||||||
|
// assign grid_id which can be embedded in gcode and
|
||||||
|
// used by the controller to cancel objects during print
|
||||||
|
let { bounds } = settings;
|
||||||
|
for (let widget of widgets) {
|
||||||
|
let { pos, box } = widget.track;
|
||||||
|
// calculate top/left coordinate for widget
|
||||||
|
// relative to bounding box for all widgets
|
||||||
|
let tl = {
|
||||||
|
x: Math.round((pos.x - box.w/2 - bounds.min.x) / 10) + 1,
|
||||||
|
y: Math.round((pos.y - box.h/2 - bounds.min.y) / 10) + 1
|
||||||
|
};
|
||||||
|
widget.track.grid_id = tl.x * 100 + tl.y;
|
||||||
|
}
|
||||||
|
// count extruders used
|
||||||
|
let ext = [];
|
||||||
|
for (let w of widgets) {
|
||||||
|
if (w.anno && w.anno.extruder >= 0) {
|
||||||
|
let e = w.anno.extruder;
|
||||||
|
if (ext.indexOf(e) < 0) {
|
||||||
|
ext.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sort widgets by first slice Z
|
||||||
|
widgets.sort((a,b) => {
|
||||||
|
return a.slices[0].z - b.slices[0].z;
|
||||||
|
});
|
||||||
|
// give first widget a pass since it should have the anchor
|
||||||
|
widgets.shift();
|
||||||
|
// remove anchor slices from other widgets (only with multi-material)
|
||||||
|
if (ext.length > 1) {
|
||||||
|
for (let w of widgets) {
|
||||||
|
w.slices = w.slices.filter(s => s.index >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute offset shell polygons. For FDM, the first offset is usually half
|
* Compute offset shell polygons. For FDM, the first offset is usually half
|
||||||
* of the nozzle width. Each subsequent offset is a full nozzle width. User
|
* of the nozzle width. Each subsequent offset is a full nozzle width. User
|
||||||
|
|
@ -1018,9 +1023,9 @@ function doRender(slice, params, opt = {}) {
|
||||||
* @param {number} fillOffset
|
* @param {number} fillOffset
|
||||||
* @param {Obejct} options
|
* @param {Obejct} options
|
||||||
*/
|
*/
|
||||||
export function doShells(slice, count, offset1, offsetN, fillOffset, opt = {}) {
|
export function layerProcessTops(slice, count, offset1, offsetN, fillOffset, opt = {}) {
|
||||||
for (let top of slice.tops) {
|
for (let top of slice.tops) {
|
||||||
doTopShells(slice.z, top, count, offset1, offsetN, fillOffset, opt);
|
layerProcessTop(slice.z, top, count, offset1, offsetN, fillOffset, opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1039,9 +1044,7 @@ export function doShells(slice, count, offset1, offsetN, fillOffset, opt = {}) {
|
||||||
* @param {number} args.spacing - Fill line spacing in mm
|
* @param {number} args.spacing - Fill line spacing in mm
|
||||||
* @param {number} args.angle - Fill angle in degrees
|
* @param {number} args.angle - Fill angle in degrees
|
||||||
*/
|
*/
|
||||||
function doSolidLayerFill(args) {
|
function layerMakeSolid({ slice, spacing, angle }) {
|
||||||
const { slice, spacing, angle } = args;
|
|
||||||
|
|
||||||
if (slice.xray) {
|
if (slice.xray) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1064,7 +1067,7 @@ function doSolidLayerFill(args) {
|
||||||
* Take output from pluggable sparse infill algorithm and clip to
|
* Take output from pluggable sparse infill algorithm and clip to
|
||||||
* the bounds of the top polygons and their inner solid areas.
|
* the bounds of the top polygons and their inner solid areas.
|
||||||
*/
|
*/
|
||||||
function doSparseLayerFill(slice, options = {}) {
|
function layerSparseFill(slice, options = {}) {
|
||||||
if (slice.xray || options.type === 'none') {
|
if (slice.xray || options.type === 'none') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1239,7 +1242,7 @@ function doSparseLayerFill(slice, options = {}) {
|
||||||
* Used to calculate bridges, flats and then solid projections.
|
* Used to calculate bridges, flats and then solid projections.
|
||||||
* 'expand' is used for top offsets in SLA mode
|
* 'expand' is used for top offsets in SLA mode
|
||||||
*/
|
*/
|
||||||
export function doDiff(slice, options = {}) {
|
export function layerDiff(slice, options = {}) {
|
||||||
const { sla, fakedown, grow, min } = options;
|
const { sla, fakedown, grow, min } = options;
|
||||||
if ((slice.index <= 0 && !fakedown) || slice.xray) {
|
if ((slice.index <= 0 && !fakedown) || slice.xray) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -1277,47 +1280,7 @@ export function doDiff(slice, options = {}) {
|
||||||
|
|
||||||
bridges.appendAll(newBridges);
|
bridges.appendAll(newBridges);
|
||||||
flats.appendAll(newFlats);
|
flats.appendAll(newFlats);
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param {Polygon[]} polys
|
|
||||||
*/
|
|
||||||
function addSolidFills(slice, polys) {
|
|
||||||
if (slice.solids) {
|
|
||||||
slice.solids.appendAll(polys);
|
|
||||||
} else if (polys && polys.length) {
|
|
||||||
console.log({no_solids_in: slice, for: polys})
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* project bottom flats down
|
|
||||||
*/
|
|
||||||
export function projectFlats(slice, count, expand) {
|
|
||||||
if (!slice.down || !slice.flats) return;
|
|
||||||
// these flats are marked for finishing print speed
|
|
||||||
if (slice.flats?.length) {
|
|
||||||
slice.finishSolids = true;
|
|
||||||
slice.isFlatsLayer = true;
|
|
||||||
const flats = expand ? POLY.expand(slice.flats, expand) : slice.flats;
|
|
||||||
projectSolid({ slice, polys: flats, count, up: false, first: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* project top bridges up
|
|
||||||
*/
|
|
||||||
export function projectBridges(slice, count) {
|
|
||||||
if (!slice.up || !slice.bridges) return;
|
|
||||||
// these flats are marked for finishing print speed
|
|
||||||
if (slice.bridges?.length) {
|
|
||||||
slice.finishSolids = true;
|
|
||||||
slice.isBridgeLayer = true;
|
|
||||||
}
|
|
||||||
projectSolid({ slice, polys: slice.bridges, count, up: true, first: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill projected solid areas and store line data
|
* Fill projected solid areas and store line data
|
||||||
|
|
@ -1330,8 +1293,7 @@ export function projectBridges(slice, count) {
|
||||||
* @param {Promise[]} [args.promises] - Array for async operations
|
* @param {Promise[]} [args.promises] - Array for async operations
|
||||||
* @return {boolean} true if filled, false if not
|
* @return {boolean} true if filled, false if not
|
||||||
*/
|
*/
|
||||||
function doSolidsFill(args) {
|
function layerFillSolids({ slice, spacing, angle, minArea, promises: fillQ }) {
|
||||||
const { slice, spacing, angle, minArea, promises: fillQ } = args;
|
|
||||||
let minarea = minArea || 1,
|
let minarea = minArea || 1,
|
||||||
tops = slice.tops,
|
tops = slice.tops,
|
||||||
solids = slice.solids;
|
solids = slice.solids;
|
||||||
|
|
@ -1406,7 +1368,7 @@ function doSolidsFill(args) {
|
||||||
for (let top of tops) {
|
for (let top of tops) {
|
||||||
top.solids = [];
|
top.solids = [];
|
||||||
}
|
}
|
||||||
doSolidLayerFill({ slice, spacing, angle });
|
layerMakeSolid({ slice, spacing, angle });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1435,12 +1397,12 @@ function doSolidsFill(args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tofill.length > 0) {
|
if (tofill.length > 0) {
|
||||||
doFillArea({ promises: fillQ, polys: tofill, angle, spacing, output: newfill });
|
areaFill({ promises: fillQ, polys: tofill, angle, spacing, output: newfill });
|
||||||
}
|
}
|
||||||
if (angfill.length > 0) {
|
if (angfill.length > 0) {
|
||||||
top.fill_lines_ang = {spacing:spacing,list:[],poly:[]};
|
top.fill_lines_ang = {spacing:spacing,list:[],poly:[]};
|
||||||
for (let af of angfill) {
|
for (let af of angfill) {
|
||||||
doFillArea({
|
areaFill({
|
||||||
promises: fillQ,
|
promises: fillQ,
|
||||||
polys: [af],
|
polys: [af],
|
||||||
angle: af.fillang.angle + CONSTANTS.ANGLED_FILL_ROTATION,
|
angle: af.fillang.angle + CONSTANTS.ANGLED_FILL_ROTATION,
|
||||||
|
|
@ -1464,8 +1426,7 @@ function doSolidsFill(args) {
|
||||||
* @param {number} [args.minLen] - Minimum line length
|
* @param {number} [args.minLen] - Minimum line length
|
||||||
* @param {number} [args.maxLen] - Maximum line length
|
* @param {number} [args.maxLen] - Maximum line length
|
||||||
*/
|
*/
|
||||||
function doFillArea(args) {
|
function areaFill({ promises: fillQ, polys, angle, spacing, output, minLen, maxLen }) {
|
||||||
const { promises: fillQ, polys, angle, spacing, output, minLen, maxLen } = args;
|
|
||||||
if (fillQ) {
|
if (fillQ) {
|
||||||
fillQ.push(self.kiri_worker.minions.fill(polys, angle, spacing, output, minLen, maxLen));
|
fillQ.push(self.kiri_worker.minions.fill(polys, angle, spacing, output, minLen, maxLen));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1512,14 +1473,13 @@ function doFillArea(args) {
|
||||||
* Each polygon has a .fill property with fill lines
|
* Each polygon has a .fill property with fill lines
|
||||||
* @modifies {Slice} slice.supportOutline - Stores support outline polygons
|
* @modifies {Slice} slice.supportOutline - Stores support outline polygons
|
||||||
*
|
*
|
||||||
* @see fillSupportPolys - Called internally to generate fill patterns
|
* @see supportPolyFill - Called internally to generate fill patterns
|
||||||
* @see CONSTANTS.SUPPORT_INSET_RATIO - Inset ratio (1/3 of line width)
|
* @see CONSTANTS.SUPPORT_INSET_RATIO - Inset ratio (1/3 of line width)
|
||||||
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_WIDE - Auto angle for wide polygons (1090 = 90°)
|
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_WIDE - Auto angle for wide polygons (1090 = 90°)
|
||||||
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_TALL - Auto angle for tall polygons (1000 = 0°)
|
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_TALL - Auto angle for tall polygons (1000 = 0°)
|
||||||
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
||||||
*/
|
*/
|
||||||
function doSupportFill(args) {
|
function layerSupportFill({ promises, slice, lineWidth, density, minArea, isBelt, angle, outline, gap }) {
|
||||||
const { promises, slice, lineWidth, density, minArea, isBelt, angle, outline, gap } = args;
|
|
||||||
let supports = slice.supports,
|
let supports = slice.supports,
|
||||||
min = minArea || 0.1,
|
min = minArea || 0.1,
|
||||||
sub;
|
sub;
|
||||||
|
|
@ -1546,7 +1506,7 @@ function doFillArea(args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supports) {
|
if (supports) {
|
||||||
fillSupportPolys({
|
supportPolyFill({
|
||||||
promises, polys: supports, lineWidth, density, z: slice.z, isBelt, angle, outline
|
promises, polys: supports, lineWidth, density, z: slice.z, isBelt, angle, outline
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1617,8 +1577,8 @@ function doFillArea(args) {
|
||||||
* - Lines are either individual segments (outline=true) or
|
* - Lines are either individual segments (outline=true) or
|
||||||
* connected paths (outline=false) for travel reduction
|
* connected paths (outline=false) for travel reduction
|
||||||
*
|
*
|
||||||
* @see doSupportFill - Parent function that calls this after clipping/union
|
* @see layerSupportFill - Parent function that calls this after clipping/union
|
||||||
* @see doFillArea - Lower-level function that generates the actual scan lines
|
* @see areaFill - Lower-level function that generates the actual scan lines
|
||||||
* @see connect_lines - Utility that connects nearby line segments
|
* @see connect_lines - Utility that connects nearby line segments
|
||||||
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_WIDE - Auto angle for wide polygons (1090 = 90°)
|
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_WIDE - Auto angle for wide polygons (1090 = 90°)
|
||||||
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_TALL - Auto angle for tall polygons (1000 = 0°)
|
* @see CONSTANTS.SUPPORT_AUTO_ANGLE_TALL - Auto angle for tall polygons (1000 = 0°)
|
||||||
|
|
@ -1626,7 +1586,7 @@ function doFillArea(args) {
|
||||||
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function fillSupportPolys({ promises, polys, lineWidth, density, z, isBelt, angle, outline }) {
|
function supportPolyFill({ promises, polys, lineWidth, density, z, isBelt, angle, outline }) {
|
||||||
// calculate fill density
|
// calculate fill density
|
||||||
let spacing = lineWidth * (1 / density);
|
let spacing = lineWidth * (1 / density);
|
||||||
for (let poly of polys) {
|
for (let poly of polys) {
|
||||||
|
|
@ -1636,7 +1596,7 @@ function fillSupportPolys({ promises, polys, lineWidth, density, z, isBelt, angl
|
||||||
let inset = POLY.offset([poly], -lineWidth * CONSTANTS.SUPPORT_INSET_RATIO, {flat: true, z, wasm: true});
|
let inset = POLY.offset([poly], -lineWidth * CONSTANTS.SUPPORT_INSET_RATIO, {flat: true, z, wasm: true});
|
||||||
// do the fill
|
// do the fill
|
||||||
if (inset && inset.length > 0) {
|
if (inset && inset.length > 0) {
|
||||||
doFillArea({
|
areaFill({
|
||||||
angle: angle || auto,
|
angle: angle || auto,
|
||||||
output: poly.fill = [],
|
output: poly.fill = [],
|
||||||
polys: inset,
|
polys: inset,
|
||||||
|
|
@ -1660,24 +1620,79 @@ function fillSupportPolys({ promises, polys, lineWidth, density, z, isBelt, angl
|
||||||
* @param {boolean} args.up - Project upward (true) or downward (false)
|
* @param {boolean} args.up - Project upward (true) or downward (false)
|
||||||
* @param {boolean} args.first - First projection (hint fill angles)
|
* @param {boolean} args.first - First projection (hint fill angles)
|
||||||
*/
|
*/
|
||||||
function projectSolid(args) {
|
function projectSolid({ slice, polys, count, up, first }) {
|
||||||
const { slice, polys, count, up, first } = args;
|
|
||||||
|
|
||||||
if (slice && !up && count === 1) {
|
if (slice && !up && count === 1) {
|
||||||
slice.isBridgeLayer = true;
|
slice.isBridgeLayer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!slice || count <= 0) {
|
if (!slice || count <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let clones = polys.clone(true);
|
let clones = polys.clone(true);
|
||||||
if (first) {
|
if (first) {
|
||||||
clones.forEach(function(p) {
|
for (let clone of clones) {
|
||||||
p.hintFillAngle();
|
clone.hintFillAngle();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
addSolidFills(slice, clones);
|
}
|
||||||
|
|
||||||
|
if (slice.solids) {
|
||||||
|
slice.solids.appendAll(clones);
|
||||||
|
} else if (clones && clones.length) {
|
||||||
|
console.log({no_solids_in: slice, for: clones})
|
||||||
|
}
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
if (up) projectSolid({ slice: slice.up, polys, count: count-1, up: true, first: false });
|
projectSolid({
|
||||||
else projectSolid({ slice: slice.down, polys, count: count-1, up: false, first: false });
|
count: count - 1,
|
||||||
|
first: false,
|
||||||
|
polys,
|
||||||
|
slice: up ? slice.up : slice.down,
|
||||||
|
up
|
||||||
|
});
|
||||||
|
// if (up) {
|
||||||
|
// projectSolid({
|
||||||
|
// count: count-1,
|
||||||
|
// first: false,
|
||||||
|
// polys,
|
||||||
|
// slice: slice.up,
|
||||||
|
// up: true
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// projectSolid({
|
||||||
|
// count: count-1,
|
||||||
|
// first: false,
|
||||||
|
// polys,
|
||||||
|
// slice: slice.down,
|
||||||
|
// up: false
|
||||||
|
// });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* project bottom flats down into part
|
||||||
|
*/
|
||||||
|
export function projectFlats(slice, count, expand) {
|
||||||
|
if (!slice.down || !slice.flats) return;
|
||||||
|
// these flats are marked for finishing print speed
|
||||||
|
if (slice.flats?.length) {
|
||||||
|
slice.finishSolids = true;
|
||||||
|
slice.isFlatsLayer = true;
|
||||||
|
const flats = expand ? POLY.expand(slice.flats, expand) : slice.flats;
|
||||||
|
projectSolid({ slice, polys: flats, count, up: false, first: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* project top bridges up into part
|
||||||
|
*/
|
||||||
|
export function projectBridges(slice, count) {
|
||||||
|
if (!slice.up || !slice.bridges) return;
|
||||||
|
// these flats are marked for finishing print speed
|
||||||
|
if (slice.bridges?.length) {
|
||||||
|
slice.finishSolids = true;
|
||||||
|
slice.isBridgeLayer = true;
|
||||||
|
}
|
||||||
|
projectSolid({ slice, polys: slice.bridges, count, up: true, first: true });
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { slicer } from '../../../geo/slicer.js';
|
||||||
import { newPolygon } from '../../../geo/polygon.js';
|
import { newPolygon } from '../../../geo/polygon.js';
|
||||||
import { polygons as POLY } from '../../../geo/polygons.js';
|
import { polygons as POLY } from '../../../geo/polygons.js';
|
||||||
import { newSlice, newTop } from '../../core/slice.js';
|
import { newSlice, newTop } from '../../core/slice.js';
|
||||||
import { doShells, doDiff, projectFlats, projectBridges } from '../fdm/slice.js';
|
import { layerProcessTops, layerDiff, projectFlats, projectBridges } from '../fdm/slice.js';
|
||||||
import { PNG } from '../../../ext/pngjs.esm.js';
|
import { PNG } from '../../../ext/pngjs.esm.js';
|
||||||
import { SLA } from './driver.js';
|
import { SLA } from './driver.js';
|
||||||
|
|
||||||
|
|
@ -149,14 +149,14 @@ export function sla_slice(settings, widget, onupdate, ondone) {
|
||||||
work_remain = work_total;
|
work_remain = work_total;
|
||||||
forSlices(slices, 5, (slice,index) => {
|
forSlices(slices, 5, (slice,index) => {
|
||||||
if (process.slaShell) {
|
if (process.slaShell) {
|
||||||
doShells(slice, 2, 0, process.slaShell);
|
layerProcessTops(slice, 2, 0, process.slaShell);
|
||||||
} else {
|
} else {
|
||||||
doShells(slice, 1, 0);
|
layerProcessTops(slice, 1, 0);
|
||||||
}
|
}
|
||||||
}, "shells");
|
}, "shells");
|
||||||
forSlices(slices, 10, (slice) => {
|
forSlices(slices, 10, (slice) => {
|
||||||
if (slice.synth) return;
|
if (slice.synth) return;
|
||||||
doDiff(slice, 0.000001, {
|
layerDiff(slice, 0.000001, {
|
||||||
sla: true,
|
sla: true,
|
||||||
fakedown: !process.slaOpenBase
|
fakedown: !process.slaOpenBase
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import '../../add/three.js';
|
||||||
|
|
||||||
import { base } from '../../geo/base.js';
|
import { base } from '../../geo/base.js';
|
||||||
import { codec, encode, encodePointArray } from '../core/codec.js';
|
import { codec, encode, encodePointArray } from '../core/codec.js';
|
||||||
import { doTopShells } from '../mode/fdm/post.js';
|
import { layerProcessTop } from '../mode/fdm/post.js';
|
||||||
import { newPoint } from '../../geo/point.js';
|
import { newPoint } from '../../geo/point.js';
|
||||||
import { newWidget } from '../core/widget.js';
|
import { newWidget } from '../core/widget.js';
|
||||||
import { polygons as POLY } from '../../geo/polygons.js';
|
import { polygons as POLY } from '../../geo/polygons.js';
|
||||||
|
|
@ -92,7 +92,7 @@ const funcs = self.minion = {
|
||||||
topShells(data, seq) {
|
topShells(data, seq) {
|
||||||
let top = codec.decode(data.top, {full: true});
|
let top = codec.decode(data.top, {full: true});
|
||||||
let {z, count, offset1, offsetN, fillOffset, opt} = data;
|
let {z, count, offset1, offsetN, fillOffset, opt} = data;
|
||||||
doTopShells(z, top, count, offset1, offsetN, fillOffset, opt);
|
layerProcessTop(z, top, count, offset1, offsetN, fillOffset, opt);
|
||||||
let state = { zeros: [] };
|
let state = { zeros: [] };
|
||||||
reply({ seq, top: codec.encode(top, {full: true}) }, state.zeros);
|
reply({ seq, top: codec.encode(top, {full: true}) }, state.zeros);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue