grid-apps-cmms/src/mode/fdm/prepare.js

869 lines
33 KiB
JavaScript
Raw Normal View History

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,
getRangeParameters = FDM.getRangeParameters;
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);
2020-11-08 13:04:54 -05:00
settings = FDM.fixExtruders(settings);
2021-01-01 12:40:18 -05:00
let render = settings.render !== false,
{ device, process, controller, bounds, mode } = settings,
2020-11-08 10:55:36 -05:00
output = [],
2020-11-05 18:29:04 -05:00
printPoint = newPoint(0,0,0),
nozzle = device.extruders[0].extNozzle,
isBelt = device.bedBelt,
isThin = controller.lineType === "line",
isFlat = controller.lineType === "flat",
isDanger = controller.danger || false,
firstLayerHeight = isBelt ? process.sliceHeight : process.firstSliceHeight || process.sliceHeight,
firstLayerSeek = process.outputSeekrate,
firstLayerRate = process.firstLayerRate,
firstLayerMult = process.firstLayerPrintMult,
firstLayerBrim = process.firstLayerBrim,
firstLayerBrimTrig = process.firstLayerBrimTrig,
layerRetract = process.outputLayerRetract,
layerno = 0,
2020-11-05 18:29:04 -05:00
zoff = 0,
layerout = [],
slices = [],
print = self.worker.print = KIRI.newPrint(settings, widgets),
2021-01-09 19:10:39 -05:00
beltYoff = device.bedDepth / 2,
beltfact = Math.cos(Math.PI/4),
invbfact = 1 / beltfact,
bfactor = invbfact * beltfact;
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) {
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
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
if (offset && brims.length) {
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) {
let slice = kiri.newSlice(zoff + height / 2);
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,
});
layerout.height = height;
output.append(layerout);
layerout = [];
zoff += height;
};
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 6.0, firstLayerRate / 3, 4);
raft(nozzle/1, process.sliceFillAngle + 0 , nozzle * 6.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);
raft(nozzle/2, process.sliceFillAngle + 0 , nozzle * 1.0, process.outputFeedrate, 1.0);
// raise first layer off raft slightly to lessen adhesion
firstLayerHeight += process.outputRaftSpacing || 0;
// 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 = [];
// expand specified # of brims
brims.forEach(function(brim) {
2020-11-19 10:38:13 -05:00
POLY.offset([brim], nozzle, {
outs: polys,
flat: true,
count: process.outputBrimCount,
z: firstLayerHeight / 2
});
2020-11-05 18:29:04 -05:00
});
// output brim points
printPoint = print.poly2polyEmit(polys, printPoint, function(poly, index, count, startPoint) {
return print.polyPrintPath(poly, startPoint, preout, {
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-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 = [];
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();
sslice.height = slice.height;
2021-01-09 21:08:24 -05:00
sslices.push(sslice);
}
if (sslices.length) {
let swidget = KIRI.newWidget(null,widget.group);
2021-01-09 21:08:24 -05:00
swidget.slices = sslices;
swidget.support = true;
swidget.rotinfo = widget.rotinfo;
swidget.belt = widget.belt;
swidget.track = Object.clone(widget.track);
swidget.mesh = { widget: swidget, position: swidget.track.pos };
2021-01-09 21:08:24 -05:00
settings.widget[swidget.id] = { extruder: process.sliceSupportNozzle };
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;
2020-11-05 18:29:04 -05:00
let extruders = [];
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) {
2020-11-05 18:29:04 -05:00
let extruder = (settings.widget[widget.id] || {}).extruder || 0;
if (!extruders[extruder]) {
extruders[extruder] = {};
extcount++;
}
2021-01-09 21:08:24 -05:00
}
2020-11-05 18:29:04 -05:00
let blokpos, walkpos, blok;
if (bounds.min.x < bounds.min.y) {
let dx = ((bounds.max.x - bounds.min.x) - (extcount * 10)) / 2 + 5;
blokpos = { x:bounds.min.x + dx, y: bounds.max.y + 5};
walkpos = { x:10, y:0 };
blok = { x:9, y:4 };
} else {
let dy = ((bounds.max.y - bounds.min.y) - (extcount * 10)) / 2 + 5;
blokpos = { x:bounds.max.x + 5, y: bounds.min.y + dy};
walkpos = { x:0, y:10 };
blok = { x:4, y:9 };
}
// compute purge blocks
extruders = extruders.map((ext,i) => {
if (!ext) return ext;
let noz = device.extruders[i].extNozzle,
pos = {x:blokpos.x, y:blokpos.y, z:0},
rec = {
extruder: i,
2020-11-08 10:55:36 -05:00
poly: newPolygon().centerSpiral(pos, blok.x, blok.y, noz*2, 3)
2020-11-05 18:29:04 -05:00
};
blokpos.x += walkpos.x;
blokpos.y += walkpos.y;
return rec;
});
// generate purge block for given nozzle
function purge(nozzle, track, layer, start, z, using) {
2021-02-24 10:40:00 -05:00
if (extcount < 2 || isBelt) {
2020-11-05 18:29:04 -05:00
return start;
}
let rec = track[nozzle];
if (rec) {
track[nozzle] = null;
if (layer.last()) {
layer.last().retract = true;
}
2020-11-05 18:29:04 -05:00
start = print.polyPrintPath(rec.poly.clone().setZ(z), start, layer, {
tool: using || nozzle,
open: true,
simple: true
});
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) {
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) {
let o = rotinfo.ypos * beltfact;
offset = {
x: rotinfo.xpos,
y: o - belt.midy - belt.yadd/2,
z: o,
};
// offset = { x:0, y:0, z:0 };
// locate the lowest point in slices and widget overall
let minby = Infinity;
for (let slice of widget.slices) {
slice.minby = Infinity;
for (let top of slice.tops) {
let poly = top.poly;
for (let point of poly.points) {
let ypos = -point.y + point.z * bfactor;
slice.minby = Math.min(slice.minby, ypos);
minby = Math.min(minby, ypos);
}
}
}
// todo remove b/c this is now calculated at slice time
// flag slices as being on or off the bed / belt
// for (let slice of widget.slices) {
// slice.onbelt = Math.abs(minby - slice.minby) < 0.01;
// }
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;
let z = slice.z + widget.offset.z;
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
let lastWidget;
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;
let lastExt;
// 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;
}
let offset = slice.widget.offset;
let find = slice.findClosestPointTo(printPoint.sub(offset));
if (find) {
let ext = slice.extruder;
let lex = lastOut ? lastOut.extruder : ext;
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-02-11 14:33:00 -05:00
let params = getRangeParameters(settings, slice.index);
2021-01-09 19:10:39 -05:00
slice.prep = true;
// 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;
}
lastWidget = slice.widget;
layerout.z = z + slice.height / 2;
2021-01-09 19:10:39 -05:00
layerout.height = layerout.height || slice.height;
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) {
printPoint = purge(slice.extruder, track, layerout, printPoint, slice.z);
2020-11-05 18:29:04 -05:00
}
2021-01-11 19:14:07 -05:00
let wtb = slice.widget.track.box;
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,
slice.belt && slice.belt.touch ? newPoint(-wtb.w, wtb.d * 2, 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-03-11 17:24:44 -05:00
seedPoint: printPoint.sub(offset),
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,
support: slice.widget.support,
2021-02-16 22:06:54 -05:00
onBelt: slice.belt && slice.belt.touch,
pretract: (wipeDist) => {
2021-02-09 13:09:56 -05:00
if (lastLayer && lastLayer.length) {
let lastOut = lastLayer.last();
lastOut.retract = true;
2021-02-09 18:29:43 -05:00
if (wipeDist && lastPoly && lastOut.point) {
let endpoint = lastOut.point.followTo(lastPoly.center(true).add(offset), wipeDist);
if (endpoint.inPolygon(lastPoly)) {
print.addOutput(lastLayer, endpoint);
}
}
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;
2020-11-05 18:29:04 -05:00
lastExt = lastOut.ext
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) {
layerout.last().retract = true;
}
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 => {
if (ext && lastOut) {
2020-11-05 18:29:04 -05:00
printPoint = purge(ext.extruder, track, layerout, printPoint, lastOut.z, lastExt);
}
});
// 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
if (layerno === cake.length - 1 && layerout.length) {
2020-11-05 18:29:04 -05:00
layerout.last().retract = true;
}
// notify progress
layerout.layer = layerno++;
update((layerno / cake.length) * 0.5, "prepare");
2020-11-05 18:29:04 -05:00
slices = [];
layerout = [];
lastOut = undefined;
}
2020-11-08 08:19:05 -05:00
print.output = output;
2021-01-01 11:55:49 -05:00
// post-process for base extrusions (touching the bed)
if (isBelt) {
// find belt min y
let minby = Infinity;
for (let layer of output) {
for (let rec of layer) {
let point = rec.point;
minby = Math.min(minby, -point.y + point.z * bfactor);
}
}
// correct y offset to desired layer offset
let miny = process.firstLayerYOffset || 0;
let poff = minby - miny;
for (let layer of output) {
for (let rec of layer) {
rec.point.y += poff;
}
}
2021-01-28 18:04:45 -05:00
// add lead in, when specified
// if (process.firstLayerBeltLead) {
// // add belt lead in
// console.log({process, output});
// }
2021-01-28 18:04:45 -05:00
let thresh = firstLayerHeight * 1.05;
// iterate over layers, find extrusion on belt and
// apply corrections and add brim when specified
for (let layer of output) {
let lastout, first = false;
let minz = Infinity, maxy = -Infinity, minx = Infinity, maxx = -Infinity;
let mins = Infinity;
let miny = Infinity;
for (let rec of layer) {
let point = rec.point;
let belty = rec.belty = -point.y + point.z * bfactor;
miny = Math.min(miny, belty);
if (rec.emit && belty <= thresh && lastout && Math.abs(lastout.belty - belty) < 0.005) {
rec.speed = firstLayerRate;
rec.emit *= firstLayerMult;
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);
first = rec;
// find length of shortest bed-facing segment
mins = Math.min(mins, lastout.point.distTo2D(rec.point));
}
lastout = rec;
}
2021-01-28 18:04:45 -05:00
// skip if brim trigger not met
if (firstLayerBrimTrig > 0 && mins > firstLayerBrimTrig) {
continue;
}
2021-01-29 08:03:13 -05:00
// do not add brims to anchor layers
if (layer.anchor) {
continue;
}
// add brim, if specified
if (first && firstLayerBrim) {
2021-02-19 12:09:39 -05:00
let { emit, tool } = first;
let y = maxy;
let z = minz;
let b = Math.max(firstLayerBrim, 1);
let tmpout = [];
layer.last().retract = true;
print.addOutput(tmpout, newPoint(maxx + b, y, z), 0, firstLayerSeek, tool);
print.addOutput(tmpout, newPoint(maxx + 0, y, z), emit, firstLayerRate, tool).retract = true;
print.addOutput(tmpout, newPoint(minx - b, y, z), 0, firstLayerSeek, tool);
print.addOutput(tmpout, newPoint(minx - 0, y, z), emit, firstLayerRate, tool).retract = false;
layer.splice(0,0,...tmpout);
}
}
}
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);
}, { 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);
};
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 [];
}
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;
const headColor = 0x888888;
const moveColor = opts.move >= 0 ? opts.move : 0xaaaaaa;
const printColor = opts.print >= 0 ? opts.print : 0x777700;
const arrowAll = false;
const arrowSize = arrowAll ? 0.2 : 0.4;
2020-11-08 10:55:36 -05:00
const layers = [];
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
};
const maxspd = levels.map(level => {
2020-11-09 18:21:59 -05:00
return level.map(o => o.speed || 0).reduce((a, v) => Math.max(a,v));
2020-11-09 16:11:02 -05:00
}).reduce((a, v) => Math.max(a, v)) + 1;
// for reporting
self.worker.print.maxSpeed = maxspd - 1;
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;
let lastEnd = null;
let lastOut = null;
let current = null;
2020-11-21 15:58:25 -05:00
let retracted = false;
let retractz = 0;
2020-11-10 23:22:58 -05:00
function color(point) {
2020-11-11 08:07:02 -05:00
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 = [];
const heads = [];
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();
layers.push(output);
2020-11-08 13:04:54 -05:00
const pushPrint = (toolid, poly) => {
toolid = toolid || 0;
const array = prints[toolid] = prints[toolid] || [];
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 10:55:36 -05:00
let height = level.height / 2;
let width = 1;
2020-11-11 17:33:20 -05:00
let emits = 0;
level.forEach((out,oi) => {
2020-11-21 15:58:25 -05:00
if (retracted && out.emit) {
retracted = false;
engages.push(lastOut.point);
}
if (out.retract) {
retracts.push(out.point);
2020-11-21 15:58:25 -05:00
retracted = true;
retractz++;
}
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) {
if (arrowAll || lastOut.emit !== out.emit) {
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) {
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 {
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);
}
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)
}
lastEnd = lastOut;
if (retracts.length) {
output
.setLayer('retract', { line: 0x550000, face: 0xff0000, opacity: 0.5 }, true)
.addAreas(retracts.map(point => {
return newPolygon().centerCircle(point, 0.2, 16).setZ(point.z + 0.01);
}), { 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 => {
return newPolygon().centerCircle(point, 0.2, 16).setZ(point.z + 0.01);
}), { outline: true });
}
if (heads.length) {
output
.setLayer('arrows', { face: headColor, line: 0x112233, opacity: 0.5 }, true)
.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);
const p3 = points.p2.projectOnSlope(s1, arrowSize);
const p4 = points.p2.projectOnSlope(s2, arrowSize);
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 });
// force level when present
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();
poly.length--;
}
output
.setLayer(opts.action || 'print', printOpt)
.addPolys([ poly ],
thin ? { thin, z: opts.z, color: poly.color } :
flat ? {
flat, z: pz, color: poly.color,
2020-11-12 09:55:19 -05:00
outline: true, offset: array.width, open: poly.open } :
{
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
});
});
update(index / levels.length, output);
2020-11-08 10:55:36 -05:00
});
console.log({retractz});
2020-11-08 10:55:36 -05:00
return layers;
}
const colorFunctions = FDM.colorFunctions = {
2020-11-11 10:07:43 -05:00
default: hsv2rgb.bind({ seg: 5, fn: color5 }),
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;
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);
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;
}
}
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;
}
}
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
})();