2020-11-05 18:29:04 -05:00
|
|
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
let KIRI = self.kiri,
|
|
|
|
|
BASE = self.base,
|
|
|
|
|
UTIL = BASE.util,
|
2020-11-08 08:19:05 -05:00
|
|
|
POLY = BASE.polygons,
|
2020-11-05 18:29:04 -05:00
|
|
|
FDM = KIRI.driver.FDM,
|
2020-11-08 10:55:36 -05:00
|
|
|
newPoint = BASE.newPoint,
|
2021-02-11 14:33:00 -05:00
|
|
|
newPolygon = BASE.newPolygon,
|
2021-05-01 00:43:01 -04:00
|
|
|
getRangeParameters = FDM.getRangeParameters,
|
|
|
|
|
debug = false;
|
2020-11-05 18:29:04 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* DRIVER PRINT CONTRACT
|
|
|
|
|
*
|
2020-11-08 13:04:54 -05:00
|
|
|
* @param {Function} update progress callback
|
|
|
|
|
* @returns {Object[]} returns array of render objects
|
2020-11-05 18:29:04 -05:00
|
|
|
*/
|
2020-11-08 13:04:54 -05:00
|
|
|
FDM.prepare = function(widgets, settings, update) {
|
2021-03-10 11:16:28 -05:00
|
|
|
// filter ignored widgets
|
|
|
|
|
widgets = widgets.filter(w => !w.track.ignore);
|
|
|
|
|
|
2021-01-01 12:40:18 -05:00
|
|
|
let render = settings.render !== false,
|
2021-01-11 22:42:06 -05:00
|
|
|
{ device, process, controller, bounds, mode } = settings,
|
2021-03-14 12:01:23 -04:00
|
|
|
{ bedWidth, bedDepth } = device,
|
2020-11-08 10:55:36 -05:00
|
|
|
output = [],
|
2020-11-05 18:29:04 -05:00
|
|
|
printPoint = newPoint(0,0,0),
|
2021-01-11 22:42:06 -05:00
|
|
|
nozzle = device.extruders[0].extNozzle,
|
|
|
|
|
isBelt = device.bedBelt,
|
|
|
|
|
isThin = controller.lineType === "line",
|
|
|
|
|
isFlat = controller.lineType === "flat",
|
2021-03-12 13:19:57 -05:00
|
|
|
isDanger = controller.danger || false,
|
2021-11-09 22:17:18 -05:00
|
|
|
isPalette = device.filamentSource === 'palette3',
|
2021-01-11 22:42:06 -05:00
|
|
|
firstLayerHeight = isBelt ? process.sliceHeight : process.firstSliceHeight || process.sliceHeight,
|
2021-01-09 23:06:03 -05:00
|
|
|
firstLayerSeek = process.outputSeekrate,
|
|
|
|
|
firstLayerRate = process.firstLayerRate,
|
|
|
|
|
firstLayerMult = process.firstLayerPrintMult,
|
2021-11-15 00:14:21 -05:00
|
|
|
purgeTower = process.outputPurgeTower || 0,
|
2021-02-07 20:52:11 -05:00
|
|
|
layerRetract = process.outputLayerRetract,
|
2021-01-26 20:07:17 -05:00
|
|
|
layerno = 0,
|
2020-11-05 18:29:04 -05:00
|
|
|
zoff = 0,
|
|
|
|
|
layerout = [],
|
2021-08-17 15:08:39 -04:00
|
|
|
// slices = [],
|
2020-11-08 16:44:32 -05:00
|
|
|
print = self.worker.print = KIRI.newPrint(settings, widgets),
|
2021-08-17 15:08:39 -04:00
|
|
|
beltfact = Math.cos(Math.PI/4);
|
2020-11-05 18:29:04 -05:00
|
|
|
|
2020-12-31 23:22:39 -05:00
|
|
|
// compute bounds if missing
|
|
|
|
|
if (!bounds) {
|
|
|
|
|
bounds = new THREE.Box3();
|
|
|
|
|
for (let widget of widgets) {
|
|
|
|
|
let wp = widget.track.pos;
|
|
|
|
|
let wb = widget.bounds.clone();
|
|
|
|
|
wb.min.x += wp.x;
|
|
|
|
|
wb.max.x += wp.x;
|
|
|
|
|
wb.min.y += wp.y;
|
|
|
|
|
wb.max.y += wp.y;
|
|
|
|
|
bounds.union(wb);
|
|
|
|
|
}
|
|
|
|
|
settings.bounds = bounds;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 18:29:04 -05:00
|
|
|
// TODO pick a widget with a slice on the first layer and use that nozzle
|
|
|
|
|
// create brim, skirt, raft if specificed in FDM mode (code shared by laser)
|
2021-01-07 22:46:16 -05:00
|
|
|
if (!isBelt && (process.outputBrimCount || process.outputRaft)) {
|
2020-11-05 18:29:04 -05:00
|
|
|
let brims = [],
|
|
|
|
|
offset = process.outputBrimOffset || (process.outputRaft ? 4 : 0);
|
|
|
|
|
|
|
|
|
|
// compute first brim
|
2021-02-24 04:22:10 -05:00
|
|
|
widgets.filter(w => w.slices.length).forEach(function(widget) {
|
2020-11-05 18:29:04 -05:00
|
|
|
let tops = [];
|
|
|
|
|
// collect top outer polygons
|
|
|
|
|
widget.slices[0].tops.forEach(function(top) {
|
|
|
|
|
tops.push(top.poly.clone());
|
|
|
|
|
});
|
|
|
|
|
// collect support polygons
|
|
|
|
|
if (widget.slices[0].supports)
|
|
|
|
|
widget.slices[0].supports.forEach(function(support) {
|
|
|
|
|
tops.push(support.clone());
|
|
|
|
|
});
|
|
|
|
|
// nest and offset tops
|
|
|
|
|
POLY.nest(tops).forEach(function(poly) {
|
2021-03-12 13:19:57 -05:00
|
|
|
let off = poly.offset(-offset + nozzle / 2);
|
|
|
|
|
if (off) off.forEach(function(brim) {
|
2021-01-24 00:13:09 -05:00
|
|
|
brim.move(widget.track.pos);
|
2020-11-05 18:29:04 -05:00
|
|
|
brims.push(brim);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// merge brims
|
2021-03-12 13:19:57 -05:00
|
|
|
brims = POLY.union(brims, undefined, true);
|
2020-11-05 18:29:04 -05:00
|
|
|
|
|
|
|
|
// if brim is offset, over-expand then shrink to induce brims to merge
|
2021-08-17 15:08:39 -04:00
|
|
|
if (brims.length) {
|
2020-11-05 18:29:04 -05:00
|
|
|
let extra = process.sliceSupportExtra + 2;
|
|
|
|
|
let zheight = brims[0].getZ();
|
|
|
|
|
brims = POLY.expand(brims, extra, zheight, null, 1);
|
|
|
|
|
brims = POLY.expand(brims, -extra, zheight, null, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if raft is specified
|
|
|
|
|
if (process.outputRaft) {
|
|
|
|
|
let offset = newPoint(0,0,0),
|
|
|
|
|
height = nozzle;
|
|
|
|
|
|
|
|
|
|
// cause first point of raft to be used
|
|
|
|
|
printPoint = null;
|
|
|
|
|
|
|
|
|
|
let raft = function(height, angle, spacing, speed, extrude) {
|
2021-11-16 22:07:49 -05:00
|
|
|
let slice = KIRI.newSlice(zoff + height / 2);
|
2020-11-05 18:29:04 -05:00
|
|
|
brims.forEach(function(brim) {
|
|
|
|
|
// use first point of first brim as start point
|
|
|
|
|
if (printPoint === null) printPoint = brim.first();
|
|
|
|
|
let t = slice.addTop(brim);
|
|
|
|
|
t.traces = [ brim ];
|
|
|
|
|
t.inner = POLY.expand(t.traces, -nozzle * 0.5, 0, null, 1);
|
|
|
|
|
// tweak bounds for fill to induce an offset
|
|
|
|
|
t.inner[0].bounds.minx -= nozzle/2;
|
|
|
|
|
t.inner[0].bounds.maxx += nozzle/2;
|
|
|
|
|
t.fill_lines = POLY.fillArea(t.inner, angle, spacing, []);
|
|
|
|
|
})
|
|
|
|
|
offset.z = slice.z;
|
|
|
|
|
printPoint = print.slicePrintPath(slice, printPoint, offset, layerout, {
|
|
|
|
|
speed: speed,
|
|
|
|
|
mult: extrude,
|
|
|
|
|
});
|
2021-03-14 13:53:39 -04:00
|
|
|
layerout.z = zoff + height;
|
2020-11-05 18:29:04 -05:00
|
|
|
layerout.height = height;
|
|
|
|
|
output.append(layerout);
|
|
|
|
|
|
|
|
|
|
layerout = [];
|
|
|
|
|
zoff += height;
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-14 18:50:47 -04:00
|
|
|
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 5.0, firstLayerRate / 3, 4);
|
|
|
|
|
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 5.0, firstLayerRate / 2, 4);
|
2020-11-05 18:29:04 -05:00
|
|
|
raft(nozzle/2, process.sliceFillAngle + 90, nozzle * 3.0, process.outputFeedrate, 2.5);
|
|
|
|
|
raft(nozzle/2, process.sliceFillAngle + 0 , nozzle * 1.0, process.outputFeedrate, 1.5);
|
2021-03-14 18:50:47 -04:00
|
|
|
raft(nozzle/2, process.sliceFillAngle + 90 , nozzle * 0.7, process.outputFeedrate, 0.75);
|
2020-11-05 18:29:04 -05:00
|
|
|
|
|
|
|
|
// raise first layer off raft slightly to lessen adhesion
|
|
|
|
|
firstLayerHeight += process.outputRaftSpacing || 0;
|
2021-03-14 14:32:16 -04:00
|
|
|
zoff += process.outputRaftSpacing || 0;
|
2020-11-05 18:29:04 -05:00
|
|
|
|
|
|
|
|
// retract after last raft layer
|
|
|
|
|
output.last().last().retract = true;
|
|
|
|
|
}
|
|
|
|
|
// raft excludes brims
|
|
|
|
|
else
|
|
|
|
|
// if using brim vs raft
|
|
|
|
|
if (process.outputBrimCount) {
|
|
|
|
|
let polys = [],
|
|
|
|
|
preout = [];
|
|
|
|
|
|
2021-08-17 15:08:39 -04:00
|
|
|
// offset specified # of brims
|
|
|
|
|
POLY.offset(brims, nozzle, {
|
|
|
|
|
outs: polys,
|
|
|
|
|
flat: true,
|
|
|
|
|
count: process.outputBrimCount,
|
|
|
|
|
z: firstLayerHeight / 2
|
2020-11-05 18:29:04 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// output brim points
|
2021-03-14 12:01:23 -04:00
|
|
|
let brimStart = offset < nozzle * 2 ? newPoint(-bedWidth, -bedDepth, 0) : printPoint;
|
|
|
|
|
printPoint = print.poly2polyEmit(polys, brimStart, (poly, index, count, startPoint) => {
|
2020-11-05 18:29:04 -05:00
|
|
|
return print.polyPrintPath(poly, startPoint, preout, {
|
2021-01-09 23:06:03 -05:00
|
|
|
rate: firstLayerRate,
|
2020-11-05 18:29:04 -05:00
|
|
|
onfirst: function(point) {
|
|
|
|
|
if (preout.length && point.distTo2D(startPoint) > 2) {
|
|
|
|
|
// retract between brims
|
|
|
|
|
preout.last().retract = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2020-11-06 14:27:26 -05:00
|
|
|
});
|
2020-11-05 18:29:04 -05:00
|
|
|
|
|
|
|
|
print.addPrintPoints(preout, layerout, null);
|
|
|
|
|
|
|
|
|
|
if (preout.length) {
|
|
|
|
|
// retract between brims and print
|
|
|
|
|
preout.last().retract = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// recompute bounds for purge block offsets
|
|
|
|
|
let bbounds = BASE.newBounds();
|
|
|
|
|
brims.forEach(brim => {
|
|
|
|
|
bbounds.merge(brim.bounds);
|
|
|
|
|
});
|
|
|
|
|
bounds.min.x = Math.min(bounds.min.x, bbounds.minx);
|
|
|
|
|
bounds.min.y = Math.min(bounds.min.y, bbounds.miny);
|
|
|
|
|
bounds.max.x = Math.max(bounds.max.x, bbounds.maxx);
|
|
|
|
|
bounds.max.y = Math.max(bounds.max.y, bbounds.maxy);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-09 21:08:24 -05:00
|
|
|
// synthesize support widgets when needed
|
|
|
|
|
// so that they can use a separate extruder
|
|
|
|
|
for (let widget of widgets.slice()) {
|
|
|
|
|
let sslices = [];
|
2021-02-14 15:50:29 -05:00
|
|
|
if (!widget.slices) {
|
|
|
|
|
console.log('invalid widget', widget);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-01-09 21:08:24 -05:00
|
|
|
for (let slice of widget.slices) {
|
|
|
|
|
if (!slice.supports) {
|
|
|
|
|
continue;
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
2021-01-09 21:08:24 -05:00
|
|
|
let sslice = KIRI.newSlice(slice.z);
|
|
|
|
|
sslice.extruder = process.sliceSupportNozzle;
|
|
|
|
|
sslice.supports = slice.supports.slice();
|
2021-01-26 14:22:54 -05:00
|
|
|
sslice.height = slice.height;
|
2021-01-09 21:08:24 -05:00
|
|
|
sslices.push(sslice);
|
|
|
|
|
}
|
|
|
|
|
if (sslices.length) {
|
2021-01-26 13:56:14 -05:00
|
|
|
let swidget = KIRI.newWidget(null,widget.group);
|
2021-01-09 21:08:24 -05:00
|
|
|
swidget.slices = sslices;
|
|
|
|
|
swidget.support = true;
|
2021-01-21 21:39:51 -05:00
|
|
|
swidget.rotinfo = widget.rotinfo;
|
|
|
|
|
swidget.belt = widget.belt;
|
2021-01-26 13:56:14 -05:00
|
|
|
swidget.track = Object.clone(widget.track);
|
|
|
|
|
swidget.mesh = { widget: swidget, position: swidget.track.pos };
|
2021-10-18 23:36:19 -04:00
|
|
|
widget.anno.extruder = process.sliceSupportNozzle;
|
2021-01-09 21:08:24 -05:00
|
|
|
widgets.push(swidget);
|
|
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
|
|
|
|
|
2021-02-09 18:29:43 -05:00
|
|
|
let lastPoly;
|
2021-02-09 13:09:56 -05:00
|
|
|
let lastLayer;
|
2021-11-15 22:07:02 -05:00
|
|
|
let lastOffset;
|
2021-10-28 19:48:50 -04:00
|
|
|
let extruders = print.extruders = [];
|
2020-11-05 18:29:04 -05:00
|
|
|
let extcount = 0;
|
|
|
|
|
|
|
|
|
|
// find max layers (for updates)
|
|
|
|
|
// generate list of used extruders for purge blocks
|
2021-01-09 21:08:24 -05:00
|
|
|
for (let widget of widgets) {
|
2021-10-18 23:36:19 -04:00
|
|
|
let extruder = widget.anno.extruder || 0;
|
2020-11-05 18:29:04 -05:00
|
|
|
if (!extruders[extruder]) {
|
|
|
|
|
extruders[extruder] = {};
|
|
|
|
|
extcount++;
|
|
|
|
|
}
|
2021-01-09 21:08:24 -05:00
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
|
2021-11-10 11:40:34 -05:00
|
|
|
// determine size/location of purge blocks
|
2021-11-15 00:14:21 -05:00
|
|
|
let blokw = Math.sqrt(purgeTower),
|
|
|
|
|
blokh = blokw;
|
2021-11-12 23:21:11 -05:00
|
|
|
let stepw = blokw + nozzle / 2;
|
2020-11-05 18:29:04 -05:00
|
|
|
let blokpos, walkpos, blok;
|
2021-11-12 23:21:11 -05:00
|
|
|
|
2021-11-15 22:07:02 -05:00
|
|
|
if (bounds.min.x < bounds.min.y && !isBelt) {
|
2021-11-12 23:21:11 -05:00
|
|
|
let mx = (bounds.max.x + bounds.min.x) / 2;
|
|
|
|
|
let px = mx - (extcount * blokw) / 2 + (blokw / 2);
|
|
|
|
|
let dx = ((bounds.max.x - bounds.min.x) - (extcount * blokw)) / 2;
|
|
|
|
|
blokpos = { x:px, y: bounds.max.y + 2 + blokh / 2};
|
|
|
|
|
walkpos = { x:stepw, y:0 };
|
|
|
|
|
blok = { x:blokw, y:blokh };
|
2020-11-05 18:29:04 -05:00
|
|
|
} else {
|
2021-11-12 23:21:11 -05:00
|
|
|
let my = (bounds.max.y + bounds.min.y) / 2;
|
2021-11-15 22:07:02 -05:00
|
|
|
let mp = (extcount * blokw) / 2 + (blokw / 2);
|
|
|
|
|
let py = my - mp;
|
|
|
|
|
blokpos = { x:bounds.max.x + 2 + blokh / 2, y:isBelt ? -mp : py};
|
2021-11-12 23:21:11 -05:00
|
|
|
walkpos = { x:0, y:stepw };
|
|
|
|
|
blok = { x:blokh, y:blokw };
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 11:40:34 -05:00
|
|
|
// replace extruders array with object array containing more info
|
2020-11-05 18:29:04 -05:00
|
|
|
extruders = extruders.map((ext,i) => {
|
|
|
|
|
if (!ext) return ext;
|
2021-11-16 11:27:36 -05:00
|
|
|
let exi = device.extruders[i],
|
|
|
|
|
noz = exi.extNozzle,
|
2020-11-05 18:29:04 -05:00
|
|
|
pos = {x:blokpos.x, y:blokpos.y, z:0},
|
2021-11-12 23:21:11 -05:00
|
|
|
rect = newPolygon().centerRectangle(pos, blok.x, blok.y),
|
|
|
|
|
full = linesToPoly(POLY.fillArea([
|
|
|
|
|
newPolygon().centerRectangle(pos, blok.x - noz, blok.y - noz)
|
|
|
|
|
], 45, noz)),
|
|
|
|
|
sparse = linesToPoly(POLY.fillArea([
|
|
|
|
|
newPolygon().centerRectangle(pos, blok.x - noz, blok.y - noz)
|
|
|
|
|
], 135, noz * 5)),
|
2020-11-05 18:29:04 -05:00
|
|
|
rec = {
|
|
|
|
|
extruder: i,
|
2021-11-16 11:27:36 -05:00
|
|
|
diameter: exi.extNozzle,
|
2021-11-12 23:21:11 -05:00
|
|
|
rect,
|
|
|
|
|
full,
|
|
|
|
|
sparse
|
2020-11-05 18:29:04 -05:00
|
|
|
};
|
|
|
|
|
blokpos.x += walkpos.x;
|
|
|
|
|
blokpos.y += walkpos.y;
|
|
|
|
|
return rec;
|
|
|
|
|
});
|
|
|
|
|
|
2021-11-12 23:21:11 -05:00
|
|
|
function linesToPoly(points) {
|
|
|
|
|
let poly = newPolygon().setOpen();
|
|
|
|
|
let ping = 0;
|
|
|
|
|
for (let i=0; i<points.length; i += 2) {
|
|
|
|
|
let p1 = points[i];
|
|
|
|
|
let p2 = points[i+1];
|
|
|
|
|
if (ping++ % 2 === 0) {
|
|
|
|
|
poly.push(p1);
|
|
|
|
|
poly.push(p2);
|
|
|
|
|
} else {
|
|
|
|
|
poly.push(p2);
|
|
|
|
|
poly.push(p1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return poly;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let lastPurgeTool;
|
|
|
|
|
|
2020-11-05 18:29:04 -05:00
|
|
|
// generate purge block for given nozzle
|
2021-11-15 22:07:02 -05:00
|
|
|
function purge(nozzle, track, layer, start, z, using, offset) {
|
|
|
|
|
if (!purgeTower || extcount < 2) {
|
2020-11-05 18:29:04 -05:00
|
|
|
return start;
|
|
|
|
|
}
|
|
|
|
|
let rec = track[nozzle];
|
2021-11-12 23:21:11 -05:00
|
|
|
let thin = using >= 0 || lastPurgeTool === nozzle;
|
|
|
|
|
let tool = using >= 0 ? using : nozzle;
|
|
|
|
|
let first = layer.slice.index === 0;
|
|
|
|
|
let rate = first ? process.firstLayerRate : process.outputFeedrate;
|
|
|
|
|
lastPurgeTool = tool;
|
2020-11-05 18:29:04 -05:00
|
|
|
if (rec) {
|
|
|
|
|
track[nozzle] = null;
|
2021-02-07 20:52:11 -05:00
|
|
|
if (layer.last()) {
|
|
|
|
|
layer.last().retract = true;
|
|
|
|
|
}
|
2021-11-16 11:27:36 -05:00
|
|
|
let purgeOn = (!isBelt && first) || !thin;
|
2021-11-15 22:07:02 -05:00
|
|
|
let box = rec.rect.clone().setZ(z);
|
|
|
|
|
let fill = (purgeOn ? rec.full : rec.sparse).clone().setZ(z);
|
|
|
|
|
if (isBelt) {
|
2021-11-16 22:07:49 -05:00
|
|
|
let scale = z >= 0 ? 0 : 1 + ((z / blokw));
|
|
|
|
|
if (scale) {
|
|
|
|
|
box.scale({x:1, y:scale, z:1});
|
|
|
|
|
let inset = box.offset(rec.diameter/2);
|
|
|
|
|
if (inset) {
|
|
|
|
|
fill = linesToPoly(POLY.fillArea(inset, 90, rec.diameter * 5));
|
|
|
|
|
} else {
|
|
|
|
|
fill = undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-16 11:27:36 -05:00
|
|
|
// offset by nozzle diameter + half step (shell) then angled
|
2021-11-16 22:07:49 -05:00
|
|
|
let exo = rec.diameter * beltfact * 1.5;
|
2021-11-16 11:27:36 -05:00
|
|
|
box.move({x:0, y:z - exo, z:0});
|
2021-11-16 22:07:49 -05:00
|
|
|
if (fill) fill.move({x:0, y:z - exo, z:0});
|
2021-11-15 22:07:02 -05:00
|
|
|
if (offset) {
|
|
|
|
|
box.move(offset);
|
2021-11-16 22:07:49 -05:00
|
|
|
if (fill) fill.move(offset);
|
2021-11-15 22:07:02 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
start = print.polyPrintPath(box, start, layer, {
|
2021-11-12 23:21:11 -05:00
|
|
|
tool,
|
|
|
|
|
rate,
|
|
|
|
|
simple: true,
|
|
|
|
|
open: false,
|
|
|
|
|
});
|
2021-11-16 22:07:49 -05:00
|
|
|
if (fill) start = print.polyPrintPath(fill, start, layer, {
|
2021-11-12 23:21:11 -05:00
|
|
|
tool,
|
|
|
|
|
rate,
|
|
|
|
|
simple: true,
|
2020-11-05 18:29:04 -05:00
|
|
|
open: true,
|
2021-11-15 14:10:15 -05:00
|
|
|
onfirst: (point) => { point.purgeOn = purgeOn }
|
2020-11-05 18:29:04 -05:00
|
|
|
});
|
2021-11-15 14:10:15 -05:00
|
|
|
start.purgeOff = purgeOn;
|
2020-11-05 18:29:04 -05:00
|
|
|
layer.last().retract = true;
|
|
|
|
|
return start;
|
|
|
|
|
} else {
|
|
|
|
|
console.log({already_purged: nozzle, from: track, layer});
|
|
|
|
|
return start;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-09 19:10:39 -05:00
|
|
|
// establish offsets
|
|
|
|
|
for (let widget of widgets) {
|
2021-01-11 22:42:06 -05:00
|
|
|
let { rotinfo, belt } = widget;
|
2021-01-24 00:13:09 -05:00
|
|
|
let offset = Object.clone(widget.track.pos);
|
2021-01-09 19:10:39 -05:00
|
|
|
if (isBelt) {
|
2021-01-24 20:38:45 -05:00
|
|
|
let o = rotinfo.ypos * beltfact;
|
|
|
|
|
offset = {
|
|
|
|
|
x: rotinfo.xpos,
|
2021-04-27 21:53:21 -04:00
|
|
|
y: o,
|
|
|
|
|
z: o
|
2021-01-24 20:38:45 -05:00
|
|
|
};
|
2021-01-09 19:10:39 -05:00
|
|
|
} else {
|
|
|
|
|
// when rafts used this is non-zero
|
|
|
|
|
offset.z = zoff;
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
2021-01-09 19:10:39 -05:00
|
|
|
widget.offset = offset;
|
|
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
|
2021-01-09 19:10:39 -05:00
|
|
|
// create shuffled slice cake by z offset (slice.z + offset.z)
|
|
|
|
|
let cake = [];
|
|
|
|
|
let zrec = {};
|
|
|
|
|
for (let widget of widgets) {
|
|
|
|
|
// skip synthesized support widget(s)
|
|
|
|
|
if (!widget.mesh) {
|
|
|
|
|
continue;
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
2021-01-09 19:10:39 -05:00
|
|
|
for (let slice of widget.slices) {
|
|
|
|
|
slice.widget = widget;
|
2021-05-28 11:19:33 -04:00
|
|
|
let z = (slice.z + widget.offset.z).round(2);
|
2021-01-09 19:10:39 -05:00
|
|
|
let rec = zrec[z] = zrec[z] || {z, slices:[]};
|
|
|
|
|
if (rec.slices.length === 0) {
|
|
|
|
|
cake.push(rec);
|
|
|
|
|
}
|
|
|
|
|
rec.slices.push(slice);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cake.sort((a, b) => {
|
|
|
|
|
return a.z - b.z;
|
|
|
|
|
});
|
2020-11-05 18:29:04 -05:00
|
|
|
|
2021-02-07 20:52:11 -05:00
|
|
|
let lastWidget;
|
2021-11-16 13:13:18 -05:00
|
|
|
let lastExt;
|
2021-02-07 20:52:11 -05:00
|
|
|
|
2021-01-09 19:10:39 -05:00
|
|
|
// walk cake layers bottom up
|
|
|
|
|
for (let layer of cake) {
|
2020-11-05 18:29:04 -05:00
|
|
|
// track purge blocks generated for each layer
|
|
|
|
|
let track = extruders.slice();
|
|
|
|
|
let lastOut;
|
|
|
|
|
|
|
|
|
|
// iterate over layer slices, find closest widget, print, eliminate
|
|
|
|
|
for (;;) {
|
|
|
|
|
let order = [];
|
|
|
|
|
// select slices of the same extruder type first then distance
|
2021-01-09 19:10:39 -05:00
|
|
|
for (let slice of layer.slices) {
|
|
|
|
|
if (slice.prep) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-11-15 22:07:02 -05:00
|
|
|
let offset = lastOffset = slice.widget.offset;
|
2021-01-09 19:10:39 -05:00
|
|
|
let find = slice.findClosestPointTo(printPoint.sub(offset));
|
|
|
|
|
if (find) {
|
|
|
|
|
let ext = slice.extruder;
|
2021-11-16 13:13:18 -05:00
|
|
|
let lex = lastExt;
|
2021-01-09 19:10:39 -05:00
|
|
|
let dst = Math.abs(find.distance);
|
|
|
|
|
// penalize extruder swaps
|
|
|
|
|
if (ext !== lex) {
|
|
|
|
|
dst *= 10000;
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
2021-01-09 19:10:39 -05:00
|
|
|
order.push({dst, slice, offset, z: layer.z});
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
|
|
|
|
}
|
2021-01-09 19:10:39 -05:00
|
|
|
if (order.length === 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
order.sort((a,b) => {
|
|
|
|
|
return a.dst - b.dst;
|
|
|
|
|
});
|
2021-01-09 19:10:39 -05:00
|
|
|
let { z, slice, offset } = order[0];
|
2021-06-23 22:31:27 -04:00
|
|
|
let params = getRangeParameters(process, slice.index);
|
2021-01-09 19:10:39 -05:00
|
|
|
slice.prep = true;
|
2021-02-07 20:52:11 -05:00
|
|
|
// retract between widgets or layers (when set)
|
|
|
|
|
if (layerout.length && slice.widget !== lastWidget) {
|
2020-11-05 18:29:04 -05:00
|
|
|
layerout.last().retract = true;
|
|
|
|
|
}
|
2021-02-07 20:52:11 -05:00
|
|
|
lastWidget = slice.widget;
|
2021-01-25 12:38:40 -05:00
|
|
|
layerout.z = z + slice.height / 2;
|
2021-01-09 19:10:39 -05:00
|
|
|
layerout.height = layerout.height || slice.height;
|
2021-03-14 15:16:30 -04:00
|
|
|
layerout.slice = slice;
|
2021-01-29 08:03:13 -05:00
|
|
|
// mark layer as anchor if slice is belt and flag set
|
|
|
|
|
layerout.anchor = slice.belt && slice.belt.anchor;
|
2020-11-05 18:29:04 -05:00
|
|
|
// detect extruder change and print purge block
|
2021-01-09 19:10:39 -05:00
|
|
|
if (!lastOut || lastOut.extruder !== slice.extruder) {
|
2021-11-16 22:07:49 -05:00
|
|
|
if (slice.extruder >= 0)
|
2021-11-15 22:07:02 -05:00
|
|
|
printPoint = purge(slice.extruder, track, layerout, printPoint, slice.z, undefined, offset);
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
2021-01-11 19:14:07 -05:00
|
|
|
let wtb = slice.widget.track.box;
|
2021-11-16 13:13:18 -05:00
|
|
|
let beltStart = slice.belt && slice.belt.touch;// && (widgets.length === 1);
|
2020-11-05 18:29:04 -05:00
|
|
|
// output seek to start point between mesh slices if previous data
|
|
|
|
|
printPoint = print.slicePrintPath(
|
2021-01-09 19:10:39 -05:00
|
|
|
slice,
|
2021-11-16 13:13:18 -05:00
|
|
|
beltStart ? newPoint(-5000, 5000, 0) : printPoint.sub(offset),
|
2021-01-09 19:10:39 -05:00
|
|
|
offset,
|
2021-02-09 13:09:56 -05:00
|
|
|
layerout,
|
2020-11-05 18:29:04 -05:00
|
|
|
{
|
2021-11-03 10:07:19 -04:00
|
|
|
routeAround: process.outputAvoidGaps,
|
2021-03-11 17:24:44 -05:00
|
|
|
seedPoint: printPoint.sub(offset),
|
2021-03-12 13:19:57 -05:00
|
|
|
danger: isDanger,
|
2021-02-11 14:33:00 -05:00
|
|
|
params, // range parameters
|
2021-01-09 19:10:39 -05:00
|
|
|
first: slice.index === 0,
|
2021-01-16 16:56:37 -05:00
|
|
|
support: slice.widget.support,
|
2021-11-16 13:13:18 -05:00
|
|
|
onBelt: beltStart,
|
2021-02-09 15:38:05 -05:00
|
|
|
pretract: (wipeDist) => {
|
2021-02-09 13:09:56 -05:00
|
|
|
if (lastLayer && lastLayer.length) {
|
2021-02-09 15:38:05 -05:00
|
|
|
let lastOut = lastLayer.last();
|
|
|
|
|
lastOut.retract = true;
|
2021-02-09 18:29:43 -05:00
|
|
|
if (wipeDist && lastPoly && lastOut.point) {
|
2021-02-09 19:08:28 -05:00
|
|
|
let endpoint = lastOut.point.followTo(lastPoly.center(true).add(offset), wipeDist);
|
|
|
|
|
if (endpoint.inPolygon(lastPoly)) {
|
|
|
|
|
print.addOutput(lastLayer, endpoint);
|
|
|
|
|
}
|
2021-02-09 15:38:05 -05:00
|
|
|
}
|
2021-02-09 13:09:56 -05:00
|
|
|
}
|
|
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
|
|
|
|
);
|
2021-01-09 19:10:39 -05:00
|
|
|
lastOut = slice;
|
2021-11-08 22:46:25 -05:00
|
|
|
lastExt = lastOut.extruder;
|
2021-02-09 18:29:43 -05:00
|
|
|
lastPoly = slice.lastPoly;
|
|
|
|
|
lastLayer = layerout;
|
2021-02-08 02:03:40 -05:00
|
|
|
if (layerRetract && layerout.length) {
|
2021-02-07 20:52:11 -05:00
|
|
|
layerout.last().retract = true;
|
|
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
|
|
|
|
|
2021-11-08 22:36:16 -05:00
|
|
|
// clear slice.prep so it can be re-previewed in a different mode
|
|
|
|
|
for (let widget of widgets) {
|
|
|
|
|
// skip synthesized support widget(s)
|
|
|
|
|
if (!widget.mesh) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (let slice of widget.slices) {
|
|
|
|
|
slice.prep = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 18:29:04 -05:00
|
|
|
// if a declared extruder isn't used in a layer, use selected
|
|
|
|
|
// extruder to fill the relevant purge blocks for later support
|
|
|
|
|
track.forEach(ext => {
|
2021-01-24 20:38:45 -05:00
|
|
|
if (ext && lastOut) {
|
2021-11-15 22:07:02 -05:00
|
|
|
printPoint = purge(ext.extruder, track, layerout, printPoint, lastOut.z, lastExt, lastOffset);
|
2020-11-05 18:29:04 -05:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// if layer produced output, append to output array
|
2021-01-09 19:10:39 -05:00
|
|
|
if (layerout.length) {
|
|
|
|
|
output.append(layerout);
|
|
|
|
|
}
|
2020-11-05 18:29:04 -05:00
|
|
|
|
|
|
|
|
// retract after last layer
|
2021-01-26 20:07:17 -05:00
|
|
|
if (layerno === cake.length - 1 && layerout.length) {
|
2020-11-05 18:29:04 -05:00
|
|
|
layerout.last().retract = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-26 09:10:20 -05:00
|
|
|
// notify progress
|
2021-01-26 20:07:17 -05:00
|
|
|
layerout.layer = layerno++;
|
|
|
|
|
update((layerno / cake.length) * 0.5, "prepare");
|
2020-11-26 09:10:20 -05:00
|
|
|
|
2020-11-05 18:29:04 -05:00
|
|
|
layerout = [];
|
|
|
|
|
lastOut = undefined;
|
|
|
|
|
}
|
2020-11-08 08:19:05 -05:00
|
|
|
|
2020-11-08 16:44:32 -05:00
|
|
|
print.output = output;
|
2021-01-01 11:55:49 -05:00
|
|
|
|
2021-01-11 22:42:06 -05:00
|
|
|
// post-process for base extrusions (touching the bed)
|
|
|
|
|
if (isBelt) {
|
2021-05-01 00:43:01 -04:00
|
|
|
// // correct y offset to desired layer offset
|
2021-04-06 22:38:02 -04:00
|
|
|
let seqn = 0;
|
2021-05-01 12:48:05 -04:00
|
|
|
// tune base threshold
|
|
|
|
|
let thresh = Infinity;
|
|
|
|
|
for (let layer of output) {
|
|
|
|
|
for (let rec of layer) {
|
|
|
|
|
let point = rec.point;
|
|
|
|
|
thresh = Math.min(thresh, point.z - point.y);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-02 22:36:16 -04:00
|
|
|
// store this offset to be removed from Y values in export
|
|
|
|
|
print.belty = thresh;
|
2021-05-01 12:48:05 -04:00
|
|
|
thresh = thresh + firstLayerHeight * 0.25;
|
2021-01-11 22:42:06 -05:00
|
|
|
// iterate over layers, find extrusion on belt and
|
|
|
|
|
// apply corrections and add brim when specified
|
|
|
|
|
for (let layer of output) {
|
2021-06-23 22:31:27 -04:00
|
|
|
let params = getRangeParameters(process, layer.layer || 0);
|
2021-05-03 10:10:13 -04:00
|
|
|
let brimHalf = params.firstLayerBrim < 0;
|
|
|
|
|
let firstLayerBrim = Math.abs(params.firstLayerBrim);
|
2021-04-21 21:35:37 -04:00
|
|
|
let firstLayerBrimIn = params.firstLayerBrimIn;
|
2021-03-28 13:00:30 -04:00
|
|
|
let firstLayerBrimTrig = params.firstLayerBrimTrig;
|
2021-04-06 22:38:02 -04:00
|
|
|
let firstLayerBrimComb = params.firstLayerBrimComb;
|
|
|
|
|
let firstLayerBrimGap = params.firstLayerBrimGap || 0;
|
2021-01-11 22:42:06 -05:00
|
|
|
let lastout, first = false;
|
|
|
|
|
let minz = Infinity, maxy = -Infinity, minx = Infinity, maxx = -Infinity;
|
2021-01-21 15:28:38 -05:00
|
|
|
let mins = Infinity;
|
|
|
|
|
let miny = Infinity;
|
2021-04-21 20:49:16 -04:00
|
|
|
let pads = [];
|
2021-04-06 13:11:35 -04:00
|
|
|
|
2021-01-21 15:28:38 -05:00
|
|
|
for (let rec of layer) {
|
2021-09-08 11:32:10 -04:00
|
|
|
let brate = params.firstLayerRate || firstLayerRate;
|
|
|
|
|
let bmult = params.firstLayerPrintMult || firstLayerMult;
|
2021-01-21 15:28:38 -05:00
|
|
|
let point = rec.point;
|
2021-05-01 00:43:01 -04:00
|
|
|
let belty = rec.belty = -point.y + point.z;
|
2021-01-21 15:28:38 -05:00
|
|
|
miny = Math.min(miny, belty);
|
2021-08-27 21:00:41 -04:00
|
|
|
if (layer.anchor) {
|
|
|
|
|
// apply base rate to entire anchor (including bump)
|
2021-09-08 11:32:10 -04:00
|
|
|
rec.speed = brate;
|
2021-08-27 21:00:41 -04:00
|
|
|
}
|
2021-01-21 15:28:38 -05:00
|
|
|
if (rec.emit && belty <= thresh && lastout && Math.abs(lastout.belty - belty) < 0.005) {
|
2021-04-06 13:11:35 -04:00
|
|
|
// apply base speed to segments touching belt
|
2021-09-08 11:32:10 -04:00
|
|
|
rec.speed = brate;
|
|
|
|
|
rec.emit *= bmult;
|
2021-01-11 22:42:06 -05:00
|
|
|
minx = Math.min(minx, point.x, lastout.point.x);
|
|
|
|
|
maxx = Math.max(maxx, point.x, lastout.point.x);
|
|
|
|
|
maxy = Math.max(maxy, point.y);
|
|
|
|
|
minz = Math.min(minz, point.z);
|
2021-01-21 15:28:38 -05:00
|
|
|
first = rec;
|
|
|
|
|
// find length of shortest bed-facing segment
|
|
|
|
|
mins = Math.min(mins, lastout.point.distTo2D(rec.point));
|
2021-04-21 20:49:16 -04:00
|
|
|
// add to pads list if > 1mm long
|
|
|
|
|
if (point.x - lastout.point.x > 1) {
|
|
|
|
|
pads.push([lastout.point.x, point.x]);
|
|
|
|
|
}
|
2021-01-11 22:42:06 -05:00
|
|
|
}
|
2021-01-21 15:28:38 -05:00
|
|
|
lastout = rec;
|
|
|
|
|
}
|
2021-01-29 08:03:13 -05:00
|
|
|
// do not add brims to anchor layers
|
2021-04-12 18:47:38 -04:00
|
|
|
if (!first || layer.anchor) {
|
|
|
|
|
seqn = 0;
|
2021-01-29 08:03:13 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
2021-04-12 18:47:38 -04:00
|
|
|
let tmpout = [];
|
2021-05-09 10:57:06 -04:00
|
|
|
let trigmet = firstLayerBrimTrig === 0 || (firstLayerBrimTrig && mins <= firstLayerBrimTrig);
|
2021-04-21 21:35:37 -04:00
|
|
|
let brimax = Math.max(firstLayerBrim, firstLayerBrimIn);
|
2021-04-12 18:47:38 -04:00
|
|
|
// add brim when all conditions met
|
2021-04-21 21:35:37 -04:00
|
|
|
if (brimax && seqn <= firstLayerBrimComb && trigmet) {
|
2021-02-19 12:09:39 -05:00
|
|
|
let { emit, tool } = first;
|
2021-01-11 22:42:06 -05:00
|
|
|
let y = maxy;
|
|
|
|
|
let z = minz;
|
2021-04-06 22:38:02 -04:00
|
|
|
let g = firstLayerBrimGap || 0;
|
|
|
|
|
let b = Math.max(firstLayerBrim, 1) + g;
|
2021-04-21 21:35:37 -04:00
|
|
|
let bi = Math.max(firstLayerBrimIn, 1) + g;
|
2021-01-17 11:26:55 -05:00
|
|
|
layer.last().retract = true;
|
2021-04-21 21:35:37 -04:00
|
|
|
// outside brim
|
2021-05-03 10:10:13 -04:00
|
|
|
if (firstLayerBrim && !brimHalf) {
|
2021-04-21 21:35:37 -04:00
|
|
|
print.addOutput(tmpout, newPoint(maxx + b, y, z), 0, firstLayerSeek, tool);
|
|
|
|
|
print.addOutput(tmpout, newPoint(maxx + g, y, z), emit, firstLayerRate, tool).retract = true;
|
|
|
|
|
}
|
|
|
|
|
// inside brim
|
|
|
|
|
if (firstLayerBrimIn && pads.length > 1) {
|
2021-04-21 20:49:16 -04:00
|
|
|
let gaps = [];
|
|
|
|
|
let lpad = pads[0];
|
|
|
|
|
for (let pad of pads.slice(1)) {
|
|
|
|
|
let x0 = lpad[1];
|
|
|
|
|
let x1 = pad[0];
|
|
|
|
|
lpad = pad;
|
2021-04-21 21:35:37 -04:00
|
|
|
if (x1 - x0 > bi * 2) {
|
2021-04-21 20:49:16 -04:00
|
|
|
// over 2x brim so emit two segments
|
|
|
|
|
print.addOutput(tmpout, newPoint(x1 - g, y, z), 0, firstLayerSeek, tool);
|
2021-04-21 21:35:37 -04:00
|
|
|
print.addOutput(tmpout, newPoint(x1 - bi, y, z), emit, firstLayerRate, tool).retract = true;
|
|
|
|
|
print.addOutput(tmpout, newPoint(x0 + bi, y, z), 0, firstLayerSeek, tool);
|
2021-04-21 20:49:16 -04:00
|
|
|
print.addOutput(tmpout, newPoint(x0 + g, y, z), emit, firstLayerRate, tool).retract = true;
|
2021-04-21 21:35:37 -04:00
|
|
|
} else if (x1 - x0 > bi / 3) {
|
2021-04-21 20:49:16 -04:00
|
|
|
// over 1/3rd brim length emit single segment
|
|
|
|
|
print.addOutput(tmpout, newPoint(x1 - g, y, z), 0, firstLayerSeek, tool);
|
|
|
|
|
print.addOutput(tmpout, newPoint(x0 + g, y, z), emit, firstLayerRate, tool).retract = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-21 21:35:37 -04:00
|
|
|
// outside brim
|
|
|
|
|
if (firstLayerBrim) {
|
|
|
|
|
print.addOutput(tmpout, newPoint(minx - b, y, z), 0, firstLayerSeek, tool);
|
|
|
|
|
print.addOutput(tmpout, newPoint(minx - g, y, z), emit, firstLayerRate, tool).retract = false;
|
|
|
|
|
}
|
2021-04-06 22:38:02 -04:00
|
|
|
if (firstLayerBrimComb) {
|
|
|
|
|
seqn++;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-04-12 18:47:38 -04:00
|
|
|
// for any layer touching belt, ensure start point is nearest origin
|
2021-04-15 22:08:44 -04:00
|
|
|
// print.addOutput(tmpout, newPoint(minx, maxy, minz), 0, firstLayerSeek, first.tool);
|
|
|
|
|
// print.lastPoint = newPoint(minx, maxy, minz);
|
2021-04-06 22:38:02 -04:00
|
|
|
seqn = 0;
|
2021-01-11 22:42:06 -05:00
|
|
|
}
|
2021-04-12 18:47:38 -04:00
|
|
|
layer.splice(0,0,...tmpout);
|
2021-01-11 22:42:06 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-09 11:51:46 -05:00
|
|
|
// render if not explicitly disabled
|
|
|
|
|
if (render) {
|
|
|
|
|
print.render = FDM.prepareRender(output, (progress, layer) => {
|
|
|
|
|
update(0.5 + progress * 0.5, "render", layer);
|
2021-11-08 22:36:16 -05:00
|
|
|
}, {
|
|
|
|
|
toolMode: settings.pmode === 2,
|
|
|
|
|
tools: device.extruders,
|
|
|
|
|
thin: isThin,
|
|
|
|
|
flat: isFlat,
|
|
|
|
|
fdm: true
|
|
|
|
|
});
|
2021-01-08 14:42:01 -05:00
|
|
|
}
|
|
|
|
|
|
2021-01-08 23:44:26 -05:00
|
|
|
return print.render;
|
2020-11-05 18:29:04 -05:00
|
|
|
};
|
|
|
|
|
|
2020-11-09 16:11:02 -05:00
|
|
|
class Counter {
|
|
|
|
|
constructor() {
|
|
|
|
|
this.map = {};
|
|
|
|
|
this.total = 0;
|
|
|
|
|
}
|
|
|
|
|
put(key) {
|
|
|
|
|
const map = this.map;
|
|
|
|
|
const kp = key || 'bad';
|
|
|
|
|
map[kp] = (map[kp] || 0) + 1;
|
|
|
|
|
this.total++;
|
|
|
|
|
}
|
|
|
|
|
get() {
|
|
|
|
|
return { map: this.map, total: this.total };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-11 08:07:02 -05:00
|
|
|
FDM.rateToColor = function(rate, max) {
|
2020-11-11 10:07:43 -05:00
|
|
|
return currentColorFunction(rate/max, 1, 0.85);
|
2020-11-10 21:48:04 -05:00
|
|
|
};
|
|
|
|
|
|
2021-01-18 18:45:03 -05:00
|
|
|
FDM.prepareRender = function(levels, update, opts = {}) {
|
2020-11-22 17:01:36 -05:00
|
|
|
levels = levels.filter(level => level.length);
|
2020-11-10 21:54:18 -05:00
|
|
|
if (levels.length === 0) {
|
|
|
|
|
self.worker.print.maxSpeed = 0;
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-08 16:44:32 -05:00
|
|
|
const tools = opts.tools || {};
|
2020-11-12 09:55:19 -05:00
|
|
|
const flat = opts.flat;
|
|
|
|
|
const thin = opts.thin && !flat;
|
2020-11-09 00:21:35 -05:00
|
|
|
const ckspeed = opts.speed !== false;
|
2021-03-04 22:13:44 -05:00
|
|
|
const headColor = 0x888888;
|
2020-11-08 16:44:32 -05:00
|
|
|
const moveColor = opts.move >= 0 ? opts.move : 0xaaaaaa;
|
|
|
|
|
const printColor = opts.print >= 0 ? opts.print : 0x777700;
|
2021-03-12 12:39:36 -05:00
|
|
|
const arrowAll = false;
|
|
|
|
|
const arrowSize = arrowAll ? 0.2 : 0.4;
|
2020-11-08 10:55:36 -05:00
|
|
|
const layers = [];
|
2021-11-08 22:36:16 -05:00
|
|
|
const toolMode = opts.toolMode;
|
2020-11-09 18:21:59 -05:00
|
|
|
|
2020-11-12 09:55:19 -05:00
|
|
|
const moveOpt = {
|
|
|
|
|
face: moveColor,
|
|
|
|
|
line: flat ? 1 : moveColor,
|
|
|
|
|
opacity: flat ? 0.5 : 1
|
|
|
|
|
};
|
|
|
|
|
const printOpt = {
|
|
|
|
|
face: printColor,
|
|
|
|
|
line: flat ? 1 : printColor,
|
|
|
|
|
opacity: flat ? 0.5 : 1
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-20 09:40:15 -04:00
|
|
|
let minspd = Infinity;
|
|
|
|
|
let maxspd = 0;
|
2021-11-08 22:36:16 -05:00
|
|
|
let maxtool = [];
|
2021-05-20 09:40:15 -04:00
|
|
|
|
|
|
|
|
for (let level of levels) {
|
|
|
|
|
for (let o of level) {
|
|
|
|
|
if (o.speed) {
|
|
|
|
|
minspd = Math.min(minspd, o.speed);
|
|
|
|
|
maxspd = Math.max(maxspd, o.speed);
|
|
|
|
|
}
|
2021-11-08 22:36:16 -05:00
|
|
|
if (toolMode && o.tool !== undefined) {
|
|
|
|
|
if (maxtool.indexOf(o.tool) < 0) {
|
|
|
|
|
maxtool.push(o.tool);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-20 09:40:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// const maxspd = levels.map(level => {
|
|
|
|
|
// return level.map(o => o.speed || 0).reduce((a, v) => Math.max(a,v));
|
|
|
|
|
// }).reduce((a, v) => Math.max(a, v)) + 1;
|
2020-11-08 16:44:32 -05:00
|
|
|
|
2020-11-10 21:48:04 -05:00
|
|
|
// for reporting
|
2021-05-20 09:40:15 -04:00
|
|
|
self.worker.print.minSpeed = minspd;
|
|
|
|
|
self.worker.print.maxSpeed = maxspd;
|
2020-11-10 23:22:58 -05:00
|
|
|
self.worker.print.thinColor = thin;
|
2020-11-12 09:55:19 -05:00
|
|
|
self.worker.print.flatColor = flat;
|
2020-11-10 21:48:04 -05:00
|
|
|
|
2021-11-08 22:36:16 -05:00
|
|
|
let lastTool = null;
|
2020-11-15 01:01:10 -05:00
|
|
|
let lastEnd = null;
|
2020-11-08 16:44:32 -05:00
|
|
|
let lastOut = null;
|
|
|
|
|
let current = null;
|
2020-11-21 15:58:25 -05:00
|
|
|
let retracted = false;
|
2021-03-12 12:39:36 -05:00
|
|
|
let retractz = 0;
|
2020-11-08 16:44:32 -05:00
|
|
|
|
2020-11-10 23:22:58 -05:00
|
|
|
function color(point) {
|
2021-11-08 22:36:16 -05:00
|
|
|
if (toolMode) {
|
|
|
|
|
return FDM.rateToColor(maxtool.indexOf(point.tool), maxtool.length);
|
|
|
|
|
} else {
|
|
|
|
|
return FDM.rateToColor(point.speed, maxspd);
|
|
|
|
|
}
|
2020-11-09 19:22:28 -05:00
|
|
|
}
|
|
|
|
|
|
2020-11-08 13:04:54 -05:00
|
|
|
levels.forEach((level, index) => {
|
2020-11-08 10:55:36 -05:00
|
|
|
const prints = {};
|
|
|
|
|
const moves = [];
|
2020-11-16 11:00:52 -05:00
|
|
|
const heads = [];
|
2021-11-08 22:36:16 -05:00
|
|
|
const changes = [];
|
2020-11-16 11:00:52 -05:00
|
|
|
const retracts = [];
|
2020-11-21 15:58:25 -05:00
|
|
|
const engages = [];
|
2020-11-30 22:36:06 -05:00
|
|
|
const output = new KIRI.Layers();
|
2020-11-09 00:00:57 -05:00
|
|
|
layers.push(output);
|
2020-11-08 16:44:32 -05:00
|
|
|
|
2020-11-08 13:04:54 -05:00
|
|
|
const pushPrint = (toolid, poly) => {
|
|
|
|
|
toolid = toolid || 0;
|
|
|
|
|
const array = prints[toolid] = prints[toolid] || [];
|
2020-11-08 16:44:32 -05:00
|
|
|
const tool = tools[toolid] || {};
|
2020-11-08 13:04:54 -05:00
|
|
|
array.width = (tool.extNozzle || 1) / 2;
|
2020-11-08 10:55:36 -05:00
|
|
|
array.push(poly);
|
2020-11-11 17:33:20 -05:00
|
|
|
emits++;
|
2020-11-08 10:55:36 -05:00
|
|
|
};
|
2020-11-08 16:44:32 -05:00
|
|
|
|
2020-11-08 10:55:36 -05:00
|
|
|
let height = level.height / 2;
|
|
|
|
|
let width = 1;
|
2020-11-11 17:33:20 -05:00
|
|
|
let emits = 0;
|
2020-11-08 16:44:32 -05:00
|
|
|
|
2020-11-15 01:01:10 -05:00
|
|
|
level.forEach((out,oi) => {
|
2020-11-21 15:58:25 -05:00
|
|
|
if (retracted && out.emit) {
|
|
|
|
|
retracted = false;
|
|
|
|
|
engages.push(lastOut.point);
|
|
|
|
|
}
|
2021-11-08 22:36:16 -05:00
|
|
|
if (out.tool !== lastTool) {
|
|
|
|
|
lastTool = out.tool;
|
|
|
|
|
changes.push(out.point);
|
|
|
|
|
}
|
2020-11-16 11:00:52 -05:00
|
|
|
if (out.retract) {
|
|
|
|
|
retracts.push(out.point);
|
2020-11-21 15:58:25 -05:00
|
|
|
retracted = true;
|
2021-03-12 12:39:36 -05:00
|
|
|
retractz++;
|
2020-11-16 11:00:52 -05:00
|
|
|
}
|
2020-11-08 16:44:32 -05:00
|
|
|
if (!out.point) {
|
|
|
|
|
// in cam mode, these are drilling or dwell ops
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-11-14 21:33:46 -05:00
|
|
|
|
2020-11-08 10:55:36 -05:00
|
|
|
if (lastOut) {
|
2021-03-12 12:39:36 -05:00
|
|
|
if (arrowAll || lastOut.emit !== out.emit) {
|
2020-11-16 11:00:52 -05:00
|
|
|
heads.push({p1: lastOut.point, p2: out.point});
|
|
|
|
|
}
|
2020-12-19 23:24:03 -05:00
|
|
|
const op = out.point, lp = lastOut.point;
|
|
|
|
|
// const moved = Math.max(
|
|
|
|
|
// Math.abs(op.x - lp.x),
|
|
|
|
|
// Math.abs(op.y - lp.y),
|
|
|
|
|
// Math.abs(op.z - lp.z));
|
|
|
|
|
// if (moved < 0.0001) return;
|
2020-11-08 10:55:36 -05:00
|
|
|
if (out.emit) {
|
2020-11-15 01:01:10 -05:00
|
|
|
if (!lastOut.emit || (ckspeed && out.speed !== lastOut.speed) || lastEnd) {
|
2020-11-08 10:55:36 -05:00
|
|
|
current = newPolygon().setOpen();
|
|
|
|
|
current.push(lastOut.point);
|
2020-11-09 19:22:28 -05:00
|
|
|
current.color = color(out);
|
2020-11-08 10:55:36 -05:00
|
|
|
pushPrint(out.tool, current);
|
|
|
|
|
}
|
|
|
|
|
current.push(out.point);
|
|
|
|
|
} else {
|
2020-11-15 01:01:10 -05:00
|
|
|
if (lastOut.emit || lastEnd) {
|
2020-11-08 10:55:36 -05:00
|
|
|
current = newPolygon().setOpen();
|
|
|
|
|
current.push(lastOut.point);
|
|
|
|
|
moves.push(current);
|
|
|
|
|
}
|
|
|
|
|
current.push(out.point);
|
|
|
|
|
}
|
2020-11-15 01:01:10 -05:00
|
|
|
lastEnd = null;
|
2020-11-08 10:55:36 -05:00
|
|
|
} else {
|
|
|
|
|
current = newPolygon().setOpen();
|
|
|
|
|
current.push(out.point);
|
|
|
|
|
if (out.emit) {
|
2020-11-09 19:22:28 -05:00
|
|
|
current.color = color(out);
|
2020-11-08 10:55:36 -05:00
|
|
|
pushPrint(out.tool, current);
|
|
|
|
|
} else {
|
|
|
|
|
moves.push(current);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lastOut = out;
|
|
|
|
|
});
|
2020-11-11 17:33:20 -05:00
|
|
|
// all moves with an emit at the very end (common in contouring)
|
|
|
|
|
if (lastOut.emit && !emits) {
|
|
|
|
|
pushPrint(lastOut.tool, current)
|
|
|
|
|
}
|
2020-11-15 01:01:10 -05:00
|
|
|
lastEnd = lastOut;
|
2021-11-08 22:36:16 -05:00
|
|
|
if (changes.length) {
|
|
|
|
|
output
|
|
|
|
|
.setLayer('tool', { line: 0x000055, face: 0x0000ff, opacity: 0.5 }, true)
|
|
|
|
|
.addAreas(changes.map(point => {
|
|
|
|
|
return newPolygon().centerCircle(point, 0.2, 4).setZ(point.z + 0.03);
|
|
|
|
|
}), { outline: true });
|
|
|
|
|
}
|
2020-11-16 11:00:52 -05:00
|
|
|
if (retracts.length) {
|
|
|
|
|
output
|
|
|
|
|
.setLayer('retract', { line: 0x550000, face: 0xff0000, opacity: 0.5 }, true)
|
|
|
|
|
.addAreas(retracts.map(point => {
|
2021-11-08 22:36:16 -05:00
|
|
|
return newPolygon().centerCircle(point, 0.2, 5).setZ(point.z + 0.01);
|
2020-11-16 11:00:52 -05:00
|
|
|
}), { outline: true });
|
|
|
|
|
}
|
2020-11-21 15:58:25 -05:00
|
|
|
if (engages.length) {
|
|
|
|
|
output
|
|
|
|
|
.setLayer('engage', { line: 0x005500, face: 0x00ff00, opacity: 0.5 }, true)
|
|
|
|
|
.addAreas(engages.map(point => {
|
2021-11-08 22:36:16 -05:00
|
|
|
return newPolygon().centerCircle(point, 0.2, 7).setZ(point.z + 0.02);
|
2020-11-21 15:58:25 -05:00
|
|
|
}), { outline: true });
|
|
|
|
|
}
|
2020-11-16 11:00:52 -05:00
|
|
|
if (heads.length) {
|
|
|
|
|
output
|
2021-03-04 22:13:44 -05:00
|
|
|
.setLayer('arrows', { face: headColor, line: 0x112233, opacity: 0.5 }, true)
|
2020-11-16 11:00:52 -05:00
|
|
|
.addAreas(heads.map(points => {
|
|
|
|
|
const {p1, p2} = points;
|
|
|
|
|
const slope = p2.slopeTo(p1);
|
|
|
|
|
const s1 = BASE.newSlopeFromAngle(slope.angle + 20);
|
|
|
|
|
const s2 = BASE.newSlopeFromAngle(slope.angle - 20);
|
2021-03-12 12:39:36 -05:00
|
|
|
const p3 = points.p2.projectOnSlope(s1, arrowSize);
|
|
|
|
|
const p4 = points.p2.projectOnSlope(s2, arrowSize);
|
2020-11-16 11:00:52 -05:00
|
|
|
return newPolygon().addPoints([p2,p3,p4]).setZ(p2.z + 0.01);
|
|
|
|
|
}), { thin: true, outline: true });
|
|
|
|
|
}
|
2020-11-08 20:19:50 -05:00
|
|
|
output
|
2020-11-12 09:55:19 -05:00
|
|
|
.setLayer(opts.other || 'move', moveOpt, opts.moves !== true)
|
2020-11-08 20:19:50 -05:00
|
|
|
.addPolys(moves, { thin: true, z: opts.z });
|
2021-01-18 18:45:03 -05:00
|
|
|
// force level when present
|
2021-02-07 20:52:11 -05:00
|
|
|
let pz = level.z ? level.z - height : opts.z;
|
2020-11-08 10:55:36 -05:00
|
|
|
Object.values(prints).forEach(array => {
|
2020-11-12 09:55:19 -05:00
|
|
|
array.forEach(poly => {
|
2020-11-12 12:15:39 -05:00
|
|
|
if (flat && poly.appearsClosed()) {
|
2020-11-12 09:55:19 -05:00
|
|
|
poly.setClosed();
|
|
|
|
|
poly.points.pop();
|
|
|
|
|
}
|
|
|
|
|
output
|
|
|
|
|
.setLayer(opts.action || 'print', printOpt)
|
|
|
|
|
.addPolys([ poly ],
|
|
|
|
|
thin ? { thin, z: opts.z, color: poly.color } :
|
|
|
|
|
flat ? {
|
2021-02-07 20:52:11 -05:00
|
|
|
flat, z: pz, color: poly.color,
|
2020-11-12 09:55:19 -05:00
|
|
|
outline: true, offset: array.width, open: poly.open } :
|
|
|
|
|
{
|
2021-02-07 20:52:11 -05:00
|
|
|
offset: array.width, height, z: pz,
|
2020-11-12 09:55:19 -05:00
|
|
|
color: { face: poly.color, line: poly.color }
|
|
|
|
|
})
|
2020-11-09 16:11:02 -05:00
|
|
|
});
|
2020-11-08 16:44:32 -05:00
|
|
|
});
|
|
|
|
|
|
2020-11-26 09:10:20 -05:00
|
|
|
update(index / levels.length, output);
|
2020-11-08 10:55:36 -05:00
|
|
|
});
|
2021-03-14 15:16:30 -04:00
|
|
|
// console.log({retractz});
|
2020-11-08 10:55:36 -05:00
|
|
|
return layers;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 13:06:21 -05:00
|
|
|
const colorFunctions = FDM.colorFunctions = {
|
2020-11-11 10:07:43 -05:00
|
|
|
default: hsv2rgb.bind({ seg: 5, fn: color5 }),
|
2021-01-15 13:06:21 -05:00
|
|
|
simple: hsv2rgb.bind({ seg: 3, fn: color4 }),
|
2020-11-11 10:07:43 -05:00
|
|
|
dark: hsv2rgb.bind({ seg: 3, fn: color3 })
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let currentColorFunction = colorFunctions.default;
|
|
|
|
|
|
2020-11-09 13:57:18 -05:00
|
|
|
// hsv values all = 0 to 1
|
2020-11-11 10:07:43 -05:00
|
|
|
function hsv2rgb(h, s, v) {
|
|
|
|
|
const div = this.seg;
|
2020-11-11 08:27:16 -05:00
|
|
|
const ss = 1 / div;
|
2020-11-10 23:07:18 -05:00
|
|
|
const seg = Math.floor(h / ss);
|
|
|
|
|
const rem = h - (seg * ss);
|
|
|
|
|
const inc = (rem / ss);
|
|
|
|
|
const dec = (1 - inc);
|
|
|
|
|
const rgb = {r: 0, g: 0, b: 0};
|
2020-11-11 10:07:43 -05:00
|
|
|
this.fn(rgb, inc, seg);
|
2020-11-11 08:27:16 -05:00
|
|
|
rgb.r = ((rgb.r * 255 * v) & 0xff) << 16;
|
|
|
|
|
rgb.g = ((rgb.g * 255 * v) & 0xff) << 8;
|
|
|
|
|
rgb.b = ((rgb.b * 255 * v) & 0xff);
|
|
|
|
|
return rgb.r | rgb.g | rgb.b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function color5(rgb, inc, seg) {
|
|
|
|
|
const dec = 1 - inc;
|
2020-11-09 13:57:18 -05:00
|
|
|
switch (seg) {
|
|
|
|
|
case 0:
|
2020-11-10 23:07:18 -05:00
|
|
|
rgb.r = 1;
|
|
|
|
|
rgb.g = inc;
|
|
|
|
|
rgb.b = 0;
|
2020-11-09 13:57:18 -05:00
|
|
|
break;
|
|
|
|
|
case 1:
|
2020-11-10 23:07:18 -05:00
|
|
|
rgb.r = dec;
|
|
|
|
|
rgb.g = 1;
|
2020-11-11 08:07:02 -05:00
|
|
|
rgb.b = 0;
|
2020-11-09 13:57:18 -05:00
|
|
|
break;
|
|
|
|
|
case 2:
|
2020-11-11 08:07:02 -05:00
|
|
|
rgb.r = 0;
|
2020-11-10 23:07:18 -05:00
|
|
|
rgb.g = dec;
|
2020-11-11 08:07:02 -05:00
|
|
|
rgb.b = inc;
|
2020-11-09 13:57:18 -05:00
|
|
|
break;
|
|
|
|
|
case 3:
|
2020-11-11 08:07:02 -05:00
|
|
|
rgb.r = inc;
|
|
|
|
|
rgb.g = 0;
|
|
|
|
|
rgb.b = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
2020-11-10 23:07:18 -05:00
|
|
|
rgb.r = dec;
|
|
|
|
|
rgb.g = 0;
|
|
|
|
|
rgb.b = dec;
|
2020-11-09 13:57:18 -05:00
|
|
|
break;
|
|
|
|
|
}
|
2020-11-11 08:27:16 -05:00
|
|
|
}
|
|
|
|
|
|
2021-01-15 13:06:21 -05:00
|
|
|
function color4(rgb, inc, seg) {
|
|
|
|
|
const dec = 1 - inc;
|
|
|
|
|
switch (seg) {
|
|
|
|
|
case 0:
|
|
|
|
|
rgb.r = 0;
|
|
|
|
|
rgb.g = inc;
|
|
|
|
|
rgb.b = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
rgb.r = inc;
|
|
|
|
|
rgb.g = 1;
|
|
|
|
|
rgb.b = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
rgb.r = 1;
|
|
|
|
|
rgb.g = dec;
|
|
|
|
|
rgb.b = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
rgb.r = dec;
|
|
|
|
|
rgb.g = 0;
|
|
|
|
|
rgb.b = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-11 08:27:16 -05:00
|
|
|
function color3(rgb, inc, seg) {
|
|
|
|
|
const dec = 1 - inc;
|
|
|
|
|
switch (seg) {
|
|
|
|
|
case 0:
|
|
|
|
|
rgb.r = dec;
|
|
|
|
|
rgb.g = inc;
|
|
|
|
|
rgb.b = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
rgb.r = 0;
|
|
|
|
|
rgb.g = dec;
|
|
|
|
|
rgb.b = inc;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
rgb.r = inc/2;
|
|
|
|
|
rgb.g = 0;
|
|
|
|
|
rgb.b = dec/2 + 0.5;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-11-09 13:57:18 -05:00
|
|
|
}
|
|
|
|
|
|
2020-11-05 18:29:04 -05:00
|
|
|
})();
|