re-attach support to fdm prepare pipeline
This commit is contained in:
parent
9a463881d1
commit
6bbc67d727
2 changed files with 84 additions and 142 deletions
|
|
@ -87,7 +87,7 @@ export async function fdm_prepare(widgets, settings, update) {
|
|||
});
|
||||
// collect support polygons
|
||||
if (slice.supports)
|
||||
slice.supports.forEach(function(support) {
|
||||
slice.supports.forEach((support) => {
|
||||
tops.push(support.clone());
|
||||
});
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ export async function fdm_prepare(widgets, settings, update) {
|
|||
tops = POLY.union(tops,1,true);
|
||||
}
|
||||
// nest and offset tops
|
||||
POLY.nest(tops).forEach(function(poly) {
|
||||
POLY.nest(tops).forEach((poly) => {
|
||||
let off = poly.offset(-offset + nozzle / 2);
|
||||
if (off) off.forEach(function(brim) {
|
||||
brim.move(widget.track.pos);
|
||||
|
|
@ -541,7 +541,7 @@ export async function fdm_prepare(widgets, settings, update) {
|
|||
},
|
||||
routeAround: process.outputAvoidGaps,
|
||||
seedPoint: printPoint.sub(offset),
|
||||
support: slice.widget.support,
|
||||
support: slice.supports,
|
||||
zmax,
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -451,22 +451,49 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
|||
let indices = stack.map(s => s.z);
|
||||
let zAngNorm = Math.sin(process.sliceSupportAngle * Math.PI / 180);
|
||||
|
||||
await widget.computeShadowStack(indices, progress => {}, zAngNorm);
|
||||
await widget.computeShadowStack(indices, progress => {
|
||||
trackupdate(progress, 0.05, 0.15, "support gen");
|
||||
}, zAngNorm);
|
||||
|
||||
if (process.sliceSupportTree) {
|
||||
stack.sort((a,b) => a.z - b.z); // deltas only
|
||||
} else {
|
||||
stack.sort((a,b) => b.z - a.z); // accumulate deltas
|
||||
}
|
||||
stack.sort((a,b) => a.z - b.z); // deltas only
|
||||
|
||||
// 1. accumulate / union shadow coverage top down
|
||||
// 2. trim to area outside slice.clips
|
||||
|
||||
for (let slice of stack) {
|
||||
let shadow = await widget.shadowAt(slice.z, true);
|
||||
slice.shadow = await widget.shadowAt(slice.z, true);
|
||||
}
|
||||
|
||||
let minArea = lineWidth;
|
||||
let shadowSum;
|
||||
for (let slice of stack.reverse()) {
|
||||
let { shadow } = slice;
|
||||
if (process.sliceSupportExtra) {
|
||||
shadow = POLY.offset(shadow, process.sliceSupportExtra);
|
||||
}
|
||||
// shadows accumulate down
|
||||
if (shadowSum) {
|
||||
shadow = POLY.union([...shadow, ...shadowSum], minArea, true);
|
||||
}
|
||||
// trim to slice.clips
|
||||
if (true) {
|
||||
let rem = [];
|
||||
let clips = [
|
||||
slice.up?.clips,
|
||||
slice.clips,
|
||||
slice.down?.clips
|
||||
].filter(v => v).flat();
|
||||
clips = POLY.union(clips, minArea, true);
|
||||
POLY.subtract(shadow, clips, rem, null, slice.z, minArea, { wasm: false });
|
||||
shadow = rem;
|
||||
}
|
||||
// pump shadow to clean to clean it up
|
||||
if (true) {
|
||||
let bump = lineWidth * 2;
|
||||
shadow = POLY.offset(shadow, bump, { z: slice.z });
|
||||
shadow = POLY.offset(shadow, -bump, { z: slice.z });
|
||||
}
|
||||
shadowSum = shadow;
|
||||
slice.supports = shadow;
|
||||
slice.output().setLayer("shadow", 0xff0000).addPolys(shadow);
|
||||
}
|
||||
|
|
@ -606,15 +633,13 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
|||
layerSupportFill({
|
||||
angle: process.sliceSupportFill,
|
||||
density,
|
||||
gap: process.sliceSupportGap,
|
||||
isBelt,
|
||||
lineWidth,
|
||||
minArea: 0,
|
||||
outline: process.sliceSupportOutline !== false,
|
||||
promises,
|
||||
slice,
|
||||
});
|
||||
}, "support");
|
||||
}, "support fill");
|
||||
if (promises) {
|
||||
await tracker(promises, (i, t) => {
|
||||
trackupdate(i / t, 0.88, 0.9);
|
||||
|
|
@ -689,9 +714,6 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
|||
});
|
||||
}
|
||||
|
||||
// new auto support demonstrator using cast shadow
|
||||
await processAutoSupport();
|
||||
|
||||
// alert non-manifold parts
|
||||
if (healed) {
|
||||
onupdate(null, null, "part may not be manifold");
|
||||
|
|
@ -728,11 +750,14 @@ export function sliceOne(settings, widget, onupdate, ondone) {
|
|||
|
||||
// reset for solids, support projections
|
||||
// and other annotations
|
||||
slices.forEach(slice => {
|
||||
for (let slice of slices) {
|
||||
slice.widget = widget;
|
||||
slice.extruder = extruder;
|
||||
slice.solids = [];
|
||||
});
|
||||
}
|
||||
|
||||
// new auto support demonstrator using cast shadow
|
||||
await processAutoSupport();
|
||||
|
||||
// process solid layers (top/bottom)
|
||||
processSolidLayers();
|
||||
|
|
@ -1434,87 +1459,47 @@ function areaFill({ promises: fillQ, polys, angle, spacing, output, minLen, maxL
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate support structure fill patterns for a slice
|
||||
*
|
||||
* This function processes support polygons by:
|
||||
* 1. Unioning all support polygons to eliminate overlaps
|
||||
* 2. Clipping supports to avoid collision with the part (using clip offsets)
|
||||
* 3. Applying optional gap spacing from upper/lower layers
|
||||
* 4. Generating hatching fill patterns based on density
|
||||
* 5. Optionally connecting fill lines to reduce travel moves
|
||||
*
|
||||
* Process flow:
|
||||
* - Union: Merge overlapping support regions into unified polygons
|
||||
* - Clip: Subtract part geometry (slice.clips) to prevent support/part collision
|
||||
* - Gap: If gap specified, also subtract clips from adjacent layers for clearance
|
||||
* - Fill: Generate hatching pattern with auto or specified angle
|
||||
* - Connect: Optionally link nearby fill line endpoints to reduce travels
|
||||
*
|
||||
* @param {SupportFillOptions} args - Configuration object
|
||||
* @param {Promise[]} [args.promises] - Array for async fill operations (concurrent mode)
|
||||
* @param {Slice} args.slice - Current slice to process
|
||||
* @param {number} args.lineWidth - Extrusion width in mm (typically nozzle diameter)
|
||||
* @param {number} args.density - Fill density from 0.0 (0%) to 1.0 (100%)
|
||||
* Typical support density is 0.10-0.25 (10-25%)
|
||||
* @param {number} args.minArea - Minimum polygon area in mm² to keep (default: 0.1)
|
||||
* Smaller polygons are filtered out as noise
|
||||
* @param {boolean} args.isBelt - True for belt printer mode (affects auto-angle calculation)
|
||||
* @param {number} [args.angle] - Fill angle in degrees. Special values:
|
||||
* - undefined/null: Auto-calculate based on polygon shape
|
||||
* - >= 1000: Auto-calculate (1000=0° for tall, 1090=90° for wide)
|
||||
* - 0-360: Use specified angle
|
||||
* @param {boolean} args.outline - Include support perimeter outline in output
|
||||
* If false, only interior fill lines are generated
|
||||
* @param {number} [args.gap] - Gap distance in mm between support and part surfaces
|
||||
* When specified, subtracts adjacent layer clips for clearance
|
||||
*
|
||||
* @modifies {Slice} slice.supports - Updated with filled support polygons (array of Polygon)
|
||||
* Each polygon has a .fill property with fill lines
|
||||
* @modifies {Slice} slice.supportOutline - Stores support outline polygons
|
||||
*
|
||||
* @see supportPolyFill - Called internally to generate fill patterns
|
||||
* @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_TALL - Auto angle for tall polygons (1000 = 0°)
|
||||
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
||||
*/
|
||||
function layerSupportFill({ promises, slice, lineWidth, density, minArea, isBelt, angle, outline, gap }) {
|
||||
let supports = slice.supports,
|
||||
min = minArea || 0.1,
|
||||
sub;
|
||||
|
||||
if (!supports) return;
|
||||
|
||||
// union supports
|
||||
supports = POLY.setZ(POLY.union(supports, undefined, true, { wasm: false }), slice.z);
|
||||
|
||||
// clip supports to slice clip offset (or shell if none)
|
||||
POLY.subtract(supports, slice.clips, sub = [], null, slice.z, min, { wasm: false });
|
||||
supports = sub;
|
||||
|
||||
// trim to upper offsets, if they exist
|
||||
if (gap && slice?.up?.clips) {
|
||||
POLY.subtract(supports, slice.up.clips, sub = [], null, slice.z, min, { wasm: false });
|
||||
supports = sub;
|
||||
}
|
||||
|
||||
// trim to lower offsets, if they exist
|
||||
if (gap && slice?.down?.clips) {
|
||||
POLY.subtract(supports, slice.down.clips, sub = [], null, slice.z, min, { wasm: false });
|
||||
supports = sub;
|
||||
}
|
||||
|
||||
if (supports) {
|
||||
/**
|
||||
* Generate support structure fill patterns for a slice
|
||||
*
|
||||
* @param {SupportFillOptions} args - Configuration object
|
||||
* @param {Promise[]} [args.promises] - Array for async fill operations (concurrent mode)
|
||||
* @param {Slice} args.slice - Current slice to process
|
||||
* @param {number} args.lineWidth - Extrusion width in mm (typically nozzle diameter)
|
||||
* @param {number} args.density - Fill density from 0.0 (0%) to 1.0 (100%)
|
||||
* Typical support density is 0.10-0.25 (10-25%)
|
||||
* @param {boolean} args.isBelt - True for belt printer mode (affects auto-angle calculation)
|
||||
* @param {number} [args.angle] - Fill angle in degrees. Special values:
|
||||
* - undefined/null: Auto-calculate based on polygon shape
|
||||
* - >= 1000: Auto-calculate (1000=0° for tall, 1090=90° for wide)
|
||||
* - 0-360: Use specified angle
|
||||
* @param {boolean} args.outline - Include support perimeter outline in output
|
||||
* If false, only interior fill lines are generated
|
||||
*
|
||||
* @modifies {Slice} slice.supports - Updated with filled support polygons (array of Polygon)
|
||||
* Each polygon has a .fill property with fill lines
|
||||
*
|
||||
* @see supportPolyFill - Called internally to generate fill patterns
|
||||
* @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_TALL - Auto angle for tall polygons (1000 = 0°)
|
||||
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
||||
*/
|
||||
function layerSupportFill({ promises, slice, lineWidth, density, isBelt, angle, outline, gap }) {
|
||||
let polys = slice.supports;
|
||||
if (polys) {
|
||||
supportPolyFill({
|
||||
promises, polys: supports, lineWidth, density, z: slice.z, isBelt, angle, outline
|
||||
angle,
|
||||
density,
|
||||
isBelt,
|
||||
lineWidth,
|
||||
outline,
|
||||
polys,
|
||||
promises,
|
||||
z: slice.z
|
||||
});
|
||||
}
|
||||
|
||||
// re-assign new supports back to slice
|
||||
slice.supportOutline = supports;
|
||||
slice.supports = supports;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate fill patterns for individual support polygons
|
||||
|
|
@ -1525,33 +1510,6 @@ function areaFill({ promises: fillQ, polys, angle, spacing, output, minLen, maxL
|
|||
* - Intelligent inset to prevent perimeter over-extrusion
|
||||
* - Optional line connection to reduce travel moves when no outline is needed
|
||||
*
|
||||
* ## Algorithm Details
|
||||
*
|
||||
* 1. **Spacing Calculation**: Converts density percentage to actual spacing distance
|
||||
* - Formula: spacing = lineWidth * (1 / density)
|
||||
* - Example: 0.4mm line × (1 / 0.15) = 2.67mm spacing for 15% density
|
||||
*
|
||||
* 2. **Auto-Angle Selection**: Chooses fill direction based on polygon shape
|
||||
* - Wide polygons (width/height > 1): Use 1090 (90° horizontal lines)
|
||||
* - Tall polygons (width/height ≤ 1): Use 1000 (0° vertical lines)
|
||||
* - Belt mode: Always use 1090 (90°) regardless of aspect ratio
|
||||
* - Rationale: Aligns fill lines with longest dimension for structural strength
|
||||
*
|
||||
* 3. **Inset Operation**: Shrinks polygon before filling
|
||||
* - Inset distance: lineWidth × (1/3) = 0.33× line width
|
||||
* - Purpose: Prevents over-extrusion where fill lines meet perimeter
|
||||
* - Creates ~0.13mm gap for typical 0.4mm nozzle
|
||||
*
|
||||
* 4. **Fill Generation**: Creates parallel hatching lines at calculated angle
|
||||
* - Uses doFillArea() to generate scan lines through inset polygon
|
||||
* - Stores result in poly.fill array
|
||||
*
|
||||
* 5. **Line Connection** (optional): Reduces travel moves
|
||||
* - Only when outline=false (no support perimeter needed)
|
||||
* - Connects nearby line endpoints within 2× spacing distance
|
||||
* - Converts many short lines into fewer longer continuous paths
|
||||
* - Reduces print time and stringing
|
||||
*
|
||||
* @param {Object} args - Configuration object
|
||||
* @param {Promise[]} [args.promises] - Array for async fill operations (concurrent mode)
|
||||
* Pass same array through all calls for parallelism
|
||||
|
|
@ -1585,13 +1543,14 @@ function areaFill({ promises: fillQ, polys, angle, spacing, output, minLen, maxL
|
|||
* @see CONSTANTS.SUPPORT_INSET_RATIO - Inset ratio for fill area (1/3)
|
||||
* @see CONSTANTS.SUPPORT_CONNECT_DISTANCE_MULT - Distance multiplier for line connection (2x)
|
||||
*/
|
||||
|
||||
function supportPolyFill({ promises, polys, lineWidth, density, z, isBelt, angle, outline }) {
|
||||
// calculate fill density
|
||||
let spacing = lineWidth * (1 / density);
|
||||
for (let poly of polys) {
|
||||
// calculate angle based on width/height ratio
|
||||
let auto = isBelt || (poly.bounds.width() / poly.bounds.height() > 1) ? CONSTANTS.SUPPORT_AUTO_ANGLE_WIDE : CONSTANTS.SUPPORT_AUTO_ANGLE_TALL;
|
||||
let auto = isBelt || (poly.bounds.width() / poly.bounds.height() > 1) ?
|
||||
CONSTANTS.SUPPORT_AUTO_ANGLE_WIDE :
|
||||
CONSTANTS.SUPPORT_AUTO_ANGLE_TALL;
|
||||
// inset support poly for fill lines
|
||||
let inset = POLY.offset([poly], -lineWidth * CONSTANTS.SUPPORT_INSET_RATIO, {flat: true, z, wasm: true});
|
||||
// do the fill
|
||||
|
|
@ -1650,23 +1609,6 @@ function projectSolid({ slice, polys, count, up, first }) {
|
|||
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
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue