2020-11-05 15:14:20 -05:00
|
|
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
|
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
let KIRI = self.kiri,
|
|
|
|
|
BASE = self.base,
|
|
|
|
|
POLY = BASE.polygons,
|
|
|
|
|
UTIL = BASE.util,
|
|
|
|
|
CAM = KIRI.driver.CAM,
|
|
|
|
|
PRO = CAM.process,
|
|
|
|
|
newPoint = BASE.newPoint;
|
|
|
|
|
|
2020-11-05 15:25:27 -05:00
|
|
|
/**
|
|
|
|
|
* DRIVER PRINT CONTRACT
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} print state object
|
|
|
|
|
* @param {Function} update incremental callback
|
|
|
|
|
* @param {Number} [index] into widget array
|
|
|
|
|
* @param {Object} [firstPoint] starting point
|
|
|
|
|
*/
|
2020-11-08 13:04:54 -05:00
|
|
|
CAM.prepare = function(widgets, settings, update) {
|
|
|
|
|
const count = widgets.length;
|
|
|
|
|
const weight = 1/count;
|
2020-11-08 16:44:32 -05:00
|
|
|
const print = self.worker.print = KIRI.newPrint(settings, widgets);
|
2020-11-08 13:04:54 -05:00
|
|
|
print.output = [];
|
|
|
|
|
|
|
|
|
|
let point;
|
|
|
|
|
widgets.forEach((widget, index) => {
|
|
|
|
|
point = prepEach(widget, settings, print, point, progress => {
|
2020-11-26 08:48:19 -05:00
|
|
|
update((index * weight + progress * weight) * 0.5, "prepare");
|
2020-11-08 13:04:54 -05:00
|
|
|
});
|
|
|
|
|
});
|
2020-11-05 15:14:20 -05:00
|
|
|
|
2020-11-26 09:10:20 -05:00
|
|
|
print.render = KIRI.driver.FDM.prepareRender(print.output, (progress, layer) => {
|
|
|
|
|
update(0.5 + progress * 0.5, "render", layer);
|
2020-11-11 17:33:20 -05:00
|
|
|
}, {
|
|
|
|
|
thin: true,
|
|
|
|
|
print: 0,
|
2020-11-14 21:33:46 -05:00
|
|
|
move: 0x557799,
|
2020-11-11 17:33:20 -05:00
|
|
|
speed: false,
|
|
|
|
|
moves: true,
|
|
|
|
|
other: "moving",
|
|
|
|
|
action: "milling"
|
|
|
|
|
});
|
2020-11-05 15:14:20 -05:00
|
|
|
|
2020-11-08 16:44:32 -05:00
|
|
|
return print.render;
|
2020-11-08 13:04:54 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function prepEach(widget, settings, print, firstPoint, update) {
|
2020-12-16 21:25:45 -05:00
|
|
|
|
2020-12-17 13:12:32 -05:00
|
|
|
if (widget.camops.length === 0) return;
|
|
|
|
|
|
2020-11-08 13:04:54 -05:00
|
|
|
let device = settings.device,
|
2020-11-05 15:14:20 -05:00
|
|
|
process = settings.process,
|
|
|
|
|
stock = settings.stock,
|
|
|
|
|
outer = settings.bounds,
|
|
|
|
|
outerz = outer.max.z,
|
|
|
|
|
slices = widget.slices,
|
2020-11-14 11:12:12 -05:00
|
|
|
hasStock = stock.x && stock.y && stock.z && process.camStockOn,
|
2020-11-05 15:14:20 -05:00
|
|
|
startCenter = process.outputOriginCenter,
|
|
|
|
|
alignTop = settings.controller.alignTop,
|
|
|
|
|
zclear = (process.camZClearance || 1),
|
|
|
|
|
zmax_outer = hasStock ? stock.z + zclear : outerz + zclear,
|
2020-12-22 15:05:50 -05:00
|
|
|
wztop = widget.track.top,
|
|
|
|
|
ztOff = hasStock ? stock.z - wztop : 0,
|
2020-11-14 11:12:12 -05:00
|
|
|
bounds = widget.getBoundingBox(),
|
|
|
|
|
boundsz = bounds.max.z + ztOff,
|
2020-11-05 15:14:20 -05:00
|
|
|
zadd = hasStock ? stock.z - boundsz : alignTop ? outerz - boundsz : 0,
|
|
|
|
|
zmax = outerz + zclear,
|
2021-02-01 01:41:59 -05:00
|
|
|
wmpos = widget.track.pos,
|
2020-11-05 15:14:20 -05:00
|
|
|
wmx = wmpos.x,
|
|
|
|
|
wmy = wmpos.y,
|
|
|
|
|
originx = startCenter ? 0 : hasStock ? -stock.x / 2 : bounds.min.x,
|
|
|
|
|
originy = startCenter ? 0 : hasStock ? -stock.y / 2 : bounds.min.y,
|
|
|
|
|
origin = newPoint(originx + wmx, originy + wmy, zmax),
|
|
|
|
|
output = print.output,
|
|
|
|
|
easeDown = process.camEaseDown,
|
|
|
|
|
depthFirst = process.camDepthFirst,
|
2020-12-16 21:25:45 -05:00
|
|
|
tolerance = 0,
|
|
|
|
|
drillDown = 0,
|
|
|
|
|
drillLift = 0,
|
|
|
|
|
drillDwell = 0,
|
2020-11-08 13:04:54 -05:00
|
|
|
newOutput = print.output || [],
|
2020-11-05 15:14:20 -05:00
|
|
|
layerOut = [],
|
|
|
|
|
printPoint,
|
|
|
|
|
isNewMode,
|
|
|
|
|
tool,
|
2020-12-08 23:27:34 -05:00
|
|
|
toolType,
|
2020-11-05 15:14:20 -05:00
|
|
|
toolDiam,
|
|
|
|
|
toolDiamMove,
|
|
|
|
|
plungeRate,
|
2020-12-17 13:12:32 -05:00
|
|
|
feedRate,
|
2020-11-05 15:14:20 -05:00
|
|
|
lastTool,
|
|
|
|
|
lastMode,
|
|
|
|
|
lastPoint,
|
|
|
|
|
nextIsMove = true,
|
2020-12-10 14:55:46 -05:00
|
|
|
synthPlunge = false,
|
2020-11-05 15:14:20 -05:00
|
|
|
spindle = 0,
|
|
|
|
|
spindleMax = device.spindleMax,
|
|
|
|
|
addOutput = print.addOutput,
|
|
|
|
|
tip2tipEmit = print.tip2tipEmit,
|
|
|
|
|
poly2polyEmit = print.poly2polyEmit,
|
|
|
|
|
maxToolDiam = widget.maxToolDiam,
|
2020-12-06 15:58:28 -05:00
|
|
|
terrain = widget.terrain ? widget.terrain.map(data => {
|
2020-11-05 15:14:20 -05:00
|
|
|
return {
|
|
|
|
|
z: data.z,
|
|
|
|
|
tops: data.tops,
|
|
|
|
|
};
|
2020-12-06 15:58:28 -05:00
|
|
|
}) : zmax;
|
2020-11-05 15:14:20 -05:00
|
|
|
|
|
|
|
|
function newLayer() {
|
|
|
|
|
if (layerOut.length < 2) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
newOutput.push(layerOut);
|
|
|
|
|
layerOut = [];
|
2020-12-25 16:15:05 -05:00
|
|
|
layerOut.mode = lastMode;
|
2020-11-05 15:14:20 -05:00
|
|
|
layerOut.spindle = spindle;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-16 21:25:45 -05:00
|
|
|
// non-zero means contouring
|
|
|
|
|
function setTolerance(dist) {
|
|
|
|
|
tolerance = dist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setPrintPoint(point) {
|
|
|
|
|
printPoint = point;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setSpindle(speed) {
|
|
|
|
|
spindle = Math.min(speed, spindleMax);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 15:14:20 -05:00
|
|
|
function setTool(toolID, feed, plunge) {
|
|
|
|
|
if (toolID !== lastTool) {
|
|
|
|
|
tool = new CAM.Tool(settings, toolID);
|
2020-12-08 23:27:34 -05:00
|
|
|
toolType = tool.getType();
|
2020-11-05 15:14:20 -05:00
|
|
|
toolDiam = tool.fluteDiameter();
|
2020-12-09 10:04:20 -05:00
|
|
|
toolDiamMove = toolType === 'endmill' ? toolDiam : tolerance * 2;
|
2020-11-05 15:14:20 -05:00
|
|
|
lastTool = toolID;
|
|
|
|
|
}
|
2020-12-25 16:15:05 -05:00
|
|
|
feedRate = feed || feedRate;
|
|
|
|
|
plungeRate = plunge || plungeRate;
|
2020-11-05 15:14:20 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-16 21:25:45 -05:00
|
|
|
function setDrill(down, lift, dwell) {
|
|
|
|
|
drillDown = down;
|
|
|
|
|
drillLift = lift;
|
|
|
|
|
drillDwell = dwell;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 15:14:20 -05:00
|
|
|
function emitDrills(polys) {
|
|
|
|
|
polys = polys.slice();
|
|
|
|
|
for (;;) {
|
|
|
|
|
let closestDist = Infinity,
|
|
|
|
|
closestI,
|
|
|
|
|
closest = null,
|
|
|
|
|
dist;
|
|
|
|
|
|
|
|
|
|
for (let i=0; i<polys.length; i++) {
|
|
|
|
|
if (!polys[i]) continue;
|
|
|
|
|
if ((dist = polys[i].first().distTo2D(printPoint)) < closestDist) {
|
|
|
|
|
closestDist = dist;
|
|
|
|
|
closest = polys[i];
|
|
|
|
|
closestI = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!closest) return;
|
|
|
|
|
polys[closestI] = null;
|
|
|
|
|
printPoint = closest.first();
|
|
|
|
|
emitDrill(closest, drillDown, drillLift, drillDwell);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function emitDrill(poly, down, lift, dwell) {
|
|
|
|
|
let remain = poly.first().z - poly.last().z,
|
|
|
|
|
points = [],
|
|
|
|
|
point = poly.first();
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (remain > down * 2) {
|
|
|
|
|
points.push(point.clone());
|
|
|
|
|
point.z -= down;
|
|
|
|
|
remain -= down;
|
|
|
|
|
} else if (remain < down) {
|
|
|
|
|
points.push(point.clone());
|
|
|
|
|
point.z -= remain;
|
|
|
|
|
points.push(point.clone());
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
points.push(point.clone());
|
|
|
|
|
point.z -= remain / 2;
|
|
|
|
|
points.push(point.clone());
|
|
|
|
|
point.z -= remain / 2;
|
|
|
|
|
points.push(point.clone());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-16 21:25:45 -05:00
|
|
|
camOut(point.clone().setZ(zmax));
|
2020-11-05 15:14:20 -05:00
|
|
|
points.forEach(function(point, index) {
|
|
|
|
|
camOut(point, 1);
|
|
|
|
|
if (index > 0 && index < points.length - 1) {
|
|
|
|
|
if (dwell) camDwell(dwell);
|
|
|
|
|
if (lift) camOut(point.clone().setZ(point.z + lift), 0);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
camOut(point.clone().setZ(zmax));
|
|
|
|
|
newLayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Point} point
|
|
|
|
|
* @param {number} emit (0=move, !0=filament emit/laser on/cut mode)
|
|
|
|
|
* @param {number} [speed] speed
|
|
|
|
|
* @param {number} [tool] tool
|
|
|
|
|
*/
|
|
|
|
|
function layerPush(point, emit, speed, tool) {
|
|
|
|
|
layerOut.mode = lastMode;
|
|
|
|
|
addOutput(layerOut, point, emit, speed, tool);
|
2020-11-14 21:33:46 -05:00
|
|
|
return point;
|
2020-11-05 15:14:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function camDwell(time) {
|
|
|
|
|
layerPush(
|
|
|
|
|
null,
|
|
|
|
|
0,
|
|
|
|
|
time,
|
|
|
|
|
tool.getNumber()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-06 15:26:41 -05:00
|
|
|
function camOut(point, cut, moveLen = toolDiamMove) {
|
2020-11-05 15:14:20 -05:00
|
|
|
point = point.clone();
|
|
|
|
|
point.x += wmx;
|
|
|
|
|
point.y += wmy;
|
|
|
|
|
point.z += zadd;
|
|
|
|
|
|
|
|
|
|
if (nextIsMove) {
|
|
|
|
|
cut = 0;
|
|
|
|
|
nextIsMove = false;
|
|
|
|
|
}
|
2020-11-14 21:33:46 -05:00
|
|
|
|
2020-11-05 15:14:20 -05:00
|
|
|
let rate = feedRate;
|
|
|
|
|
|
2020-11-14 21:33:46 -05:00
|
|
|
// before first point, move cutting head to point above it
|
|
|
|
|
// then set that new point as the lastPoint
|
2020-11-05 15:14:20 -05:00
|
|
|
if (!lastPoint) {
|
2020-11-30 10:12:47 -05:00
|
|
|
let above = point.clone().setZ(zmax + zadd + ztOff);
|
2020-11-14 21:33:46 -05:00
|
|
|
lastPoint = layerPush(above, 0, 0, tool.getNumber());
|
2020-11-05 15:14:20 -05:00
|
|
|
}
|
|
|
|
|
|
2020-11-14 21:33:46 -05:00
|
|
|
// measure deltas to last point in XY and Z
|
2020-11-05 15:14:20 -05:00
|
|
|
let deltaXY = lastPoint.distTo2D(point),
|
|
|
|
|
deltaZ = point.z - lastPoint.z,
|
|
|
|
|
absDeltaZ = Math.abs(deltaZ),
|
|
|
|
|
isMove = !cut;
|
2020-11-14 21:33:46 -05:00
|
|
|
|
2020-11-05 15:14:20 -05:00
|
|
|
// drop points too close together
|
|
|
|
|
if (deltaXY < 0.001 && point.z === lastPoint.z) {
|
2020-12-02 10:00:41 -05:00
|
|
|
// console.trace(["drop dup",lastPoint,point]);
|
2020-11-05 15:14:20 -05:00
|
|
|
return;
|
|
|
|
|
}
|
2020-11-14 21:33:46 -05:00
|
|
|
|
2020-11-21 21:04:25 -05:00
|
|
|
// convert short planar moves to cuts in some cases
|
2021-03-06 15:26:41 -05:00
|
|
|
if (isMove && deltaXY <= moveLen) {
|
2020-12-16 21:25:45 -05:00
|
|
|
let iscontour = tolerance > 0;
|
2020-11-21 21:04:25 -05:00
|
|
|
let isflat = absDeltaZ < 0.001;
|
|
|
|
|
// restrict this to contouring
|
|
|
|
|
if (isflat || (iscontour && absDeltaZ <= tolerance)) {
|
2020-11-05 15:14:20 -05:00
|
|
|
cut = 1;
|
|
|
|
|
isMove = false;
|
|
|
|
|
} else if (deltaZ <= -tolerance) {
|
|
|
|
|
// move over before descending
|
|
|
|
|
layerPush(point.clone().setZ(lastPoint.z), 0, 0, tool.getNumber());
|
|
|
|
|
// new pos for plunge calc
|
|
|
|
|
deltaXY = 0;
|
|
|
|
|
}
|
2020-12-02 14:33:27 -05:00
|
|
|
} else if (isMove) {
|
2020-11-15 01:01:10 -05:00
|
|
|
// for longer moves, check the terrain to see if we need to go up and over
|
2021-03-06 15:26:41 -05:00
|
|
|
const bigXY = (deltaXY > moveLen);
|
2020-11-15 01:01:10 -05:00
|
|
|
const bigZ = (deltaZ > toolDiam/2 && deltaXY > tolerance);
|
|
|
|
|
const midZ = (absDeltaZ >= tolerance);
|
|
|
|
|
if ((bigXY || bigZ) && (isMove || midZ)) {
|
|
|
|
|
let maxz = getZClearPath(
|
|
|
|
|
terrain,
|
2020-11-17 00:53:17 -05:00
|
|
|
lastPoint.x - wmx,
|
|
|
|
|
lastPoint.y - wmy,
|
|
|
|
|
point.x - wmx,
|
|
|
|
|
point.y - wmy,
|
2020-11-15 01:01:10 -05:00
|
|
|
Math.max(point.z, lastPoint.z),
|
|
|
|
|
zadd,
|
|
|
|
|
maxToolDiam/2,
|
|
|
|
|
zclear
|
|
|
|
|
) + ztOff,
|
|
|
|
|
mustGoUp = Math.max(maxz - point.z, maxz - lastPoint.z) >= tolerance,
|
|
|
|
|
clearz = maxz;
|
2021-03-09 17:58:24 -05:00
|
|
|
// up if any point between higher than start/outline, go up first
|
2020-11-15 01:01:10 -05:00
|
|
|
if (mustGoUp) {
|
|
|
|
|
layerPush(lastPoint.clone().setZ(clearz), 0, 0, tool.getNumber());
|
|
|
|
|
}
|
|
|
|
|
// move to point above target point
|
|
|
|
|
if (mustGoUp || point.z < maxz) {
|
|
|
|
|
layerPush(point.clone().setZ(clearz), 0, 0, tool.getNumber());
|
|
|
|
|
// new pos for plunge calc
|
|
|
|
|
deltaXY = 0;
|
|
|
|
|
}
|
2020-11-05 15:14:20 -05:00
|
|
|
}
|
|
|
|
|
}
|
2020-11-14 21:33:46 -05:00
|
|
|
|
2020-12-27 20:01:56 -05:00
|
|
|
// set new plunge rate
|
|
|
|
|
if (deltaZ < -tolerance) {
|
2020-11-05 15:14:20 -05:00
|
|
|
let threshold = Math.min(deltaXY / 2, absDeltaZ),
|
|
|
|
|
modifier = threshold / absDeltaZ;
|
2020-12-27 20:01:56 -05:00
|
|
|
if (synthPlunge && threshold && modifier && deltaXY > tolerance) {
|
2020-11-05 15:14:20 -05:00
|
|
|
// use modifier to speed up long XY move plunge rates
|
|
|
|
|
rate = Math.round(plungeRate + ((feedRate - plungeRate) * modifier));
|
|
|
|
|
} else {
|
|
|
|
|
rate = plungeRate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// todo synthesize move speed from feed / plunge accordingly
|
2020-12-25 16:15:05 -05:00
|
|
|
layerOut.mode = lastMode;
|
2020-11-14 21:33:46 -05:00
|
|
|
layerOut.spindle = spindle;
|
|
|
|
|
lastPoint = layerPush(
|
2020-11-05 15:14:20 -05:00
|
|
|
point,
|
|
|
|
|
cut ? 1 : 0,
|
|
|
|
|
rate,
|
|
|
|
|
tool.getNumber()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// coming from a previous widget, use previous last point
|
|
|
|
|
lastPoint = firstPoint;
|
|
|
|
|
|
|
|
|
|
// make top start offset configurable
|
|
|
|
|
printPoint = firstPoint || origin;
|
|
|
|
|
|
|
|
|
|
// accumulated data for depth-first optimiztions
|
|
|
|
|
let depthData = {
|
|
|
|
|
rough: [],
|
|
|
|
|
outline: [],
|
|
|
|
|
roughDiam: 0,
|
|
|
|
|
outlineDiam: 0,
|
|
|
|
|
contourx: [],
|
|
|
|
|
contoury: [],
|
2020-12-01 20:11:19 -05:00
|
|
|
trace: [],
|
|
|
|
|
drill: [],
|
2020-11-05 15:14:20 -05:00
|
|
|
layer: 0,
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-16 21:25:45 -05:00
|
|
|
let ops = {
|
|
|
|
|
setTool,
|
|
|
|
|
setDrill,
|
|
|
|
|
setSpindle,
|
2020-12-16 23:08:52 -05:00
|
|
|
setTolerance,
|
2020-12-16 21:25:45 -05:00
|
|
|
setPrintPoint,
|
|
|
|
|
printPoint,
|
|
|
|
|
newLayer,
|
|
|
|
|
camOut,
|
|
|
|
|
polyEmit,
|
|
|
|
|
poly2polyEmit,
|
2020-12-16 23:08:52 -05:00
|
|
|
tip2tipEmit,
|
2020-12-16 21:25:45 -05:00
|
|
|
depthRoughPath,
|
|
|
|
|
depthOutlinePath,
|
|
|
|
|
emitDrills,
|
2020-12-16 23:08:52 -05:00
|
|
|
emitTrace,
|
|
|
|
|
bounds,
|
|
|
|
|
zmax,
|
|
|
|
|
lastPoint: () => { return lastPoint }
|
2020-12-16 21:25:45 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let opSum = 0;
|
|
|
|
|
let opTot = widget.camops.map(op => op.weight()).reduce((a,v) => a + v);
|
|
|
|
|
|
|
|
|
|
for (let op of widget.camops) {
|
|
|
|
|
setTolerance(0);
|
|
|
|
|
nextIsMove = true;
|
2020-12-25 16:15:05 -05:00
|
|
|
lastMode = op.op.type;
|
2020-12-16 21:25:45 -05:00
|
|
|
let weight = op.weight();
|
|
|
|
|
op.prepare(ops, (progress, message) => {
|
|
|
|
|
onupdate((opSum + (progress * weight)) / opTot, message || op.type());
|
|
|
|
|
});
|
|
|
|
|
opSum += weight;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 20:11:19 -05:00
|
|
|
function emitTrace(slice) {
|
2020-12-24 12:59:57 -05:00
|
|
|
let { tool, rate, plunge } = slice.camTrace;
|
|
|
|
|
setTool(tool, rate, plunge);
|
2020-12-01 20:11:19 -05:00
|
|
|
let traceTool = new CAM.Tool(settings, tool);
|
|
|
|
|
let traceToolDiam = traceTool.fluteDiameter();
|
2020-12-25 13:15:49 -05:00
|
|
|
printPoint = poly2polyEmit(slice.camLines, printPoint, polyEmit);
|
2020-12-01 20:11:19 -05:00
|
|
|
newLayer();
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 21:11:07 -05:00
|
|
|
function polyEmit(poly, index, count, fromPoint) {
|
|
|
|
|
let last = null;
|
|
|
|
|
if (easeDown && poly.isClosed()) {
|
|
|
|
|
last = poly.forEachPointEaseDown(function(point, offset) {
|
|
|
|
|
camOut(point.clone(), offset > 0);
|
|
|
|
|
}, fromPoint);
|
|
|
|
|
} else {
|
|
|
|
|
poly.forEachPoint(function(point, pidx, points, offset) {
|
2020-11-06 14:27:26 -05:00
|
|
|
last = point;
|
2020-11-05 21:11:07 -05:00
|
|
|
camOut(point.clone(), offset !== 0);
|
|
|
|
|
}, poly.isClosed(), index);
|
|
|
|
|
}
|
|
|
|
|
newLayer();
|
|
|
|
|
return last;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 15:19:26 -05:00
|
|
|
function depthRoughPath(start, depth, levels, tops, emitter, fit, ease) {
|
2020-11-05 21:11:07 -05:00
|
|
|
let level = levels[depth];
|
|
|
|
|
if (!level) {
|
|
|
|
|
return start;
|
|
|
|
|
}
|
|
|
|
|
let ltops = tops[depth];
|
|
|
|
|
let fitted = fit ? ltops.filter(poly => poly.isInside(fit)) : ltops;
|
2020-11-06 07:26:58 -05:00
|
|
|
fitted.filter(top => !top.level_emit).forEach(top => {
|
|
|
|
|
top.level_emit = true;
|
2020-11-05 21:11:07 -05:00
|
|
|
let inside = level.filter(poly => poly.isInside(top));
|
2021-02-24 15:19:26 -05:00
|
|
|
if (ease) {
|
|
|
|
|
start.z += ease;
|
|
|
|
|
}
|
2021-01-17 22:23:51 -05:00
|
|
|
start = poly2polyEmit(inside, start, emitter, { mark: "emark", perm: true });
|
2021-02-24 15:19:26 -05:00
|
|
|
if (ease) {
|
|
|
|
|
start.z += ease;
|
|
|
|
|
}
|
|
|
|
|
start = depthRoughPath(start, depth + 1, levels, tops, emitter, top, ease);
|
2020-11-05 21:11:07 -05:00
|
|
|
});
|
|
|
|
|
return start;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 15:19:26 -05:00
|
|
|
function depthOutlinePath(start, depth, levels, radius, emitter, clr, ease) {
|
2020-11-06 14:27:26 -05:00
|
|
|
let bottm = depth < levels.length - 1 ? levels[levels.length - 1] : null;
|
|
|
|
|
let above = levels[depth-1];
|
|
|
|
|
let level = levels[depth];
|
|
|
|
|
if (!level) {
|
|
|
|
|
return start;
|
|
|
|
|
}
|
|
|
|
|
if (above) {
|
|
|
|
|
level = level.filter(lp => {
|
2020-11-10 19:56:34 -05:00
|
|
|
const conf = above.filter(ap => !ap.level_emit && lp.isNear(ap, radius, true));
|
|
|
|
|
return conf.length === 0;
|
2020-11-06 14:27:26 -05:00
|
|
|
});
|
|
|
|
|
}
|
2020-11-15 19:13:53 -05:00
|
|
|
// const thru = []; // match thru polys
|
2020-11-06 14:27:26 -05:00
|
|
|
level = level.filter(lp => {
|
2020-11-10 19:56:34 -05:00
|
|
|
if (lp.level_emit) {
|
|
|
|
|
return false;
|
2020-11-06 14:27:26 -05:00
|
|
|
}
|
2020-11-15 19:13:53 -05:00
|
|
|
// if (bottm && !clr) {
|
|
|
|
|
// const tm = bottm.filter(bp => lp.isEquivalent(bp));
|
|
|
|
|
// thru.appendAll(tm);
|
|
|
|
|
// return tm.length === 0;
|
|
|
|
|
// }
|
2020-11-06 14:27:26 -05:00
|
|
|
return true;
|
2020-11-05 21:11:07 -05:00
|
|
|
});
|
2020-11-06 14:27:26 -05:00
|
|
|
// omit polys that match bottom level polys unless level above is cleared
|
|
|
|
|
start = poly2polyEmit(level, start, (poly, index, count, fromPoint) => {
|
|
|
|
|
poly.level_emit = true;
|
2021-02-24 15:19:26 -05:00
|
|
|
if (ease) {
|
|
|
|
|
fromPoint.z += ease;
|
|
|
|
|
}
|
2020-11-06 14:27:26 -05:00
|
|
|
fromPoint = polyEmit(poly, index, count, fromPoint);
|
2021-02-24 15:19:26 -05:00
|
|
|
if (ease) {
|
|
|
|
|
fromPoint.z += ease;
|
|
|
|
|
}
|
|
|
|
|
fromPoint = depthOutlinePath(fromPoint, depth + 1, levels, radius, emitter, clr, ease);
|
2020-11-06 14:27:26 -05:00
|
|
|
return fromPoint;
|
|
|
|
|
}, {weight: true});
|
|
|
|
|
return start;
|
2020-11-05 21:11:07 -05:00
|
|
|
}
|
|
|
|
|
|
2020-11-05 15:14:20 -05:00
|
|
|
// last layer/move is to zmax
|
|
|
|
|
// injected into the last layer generated
|
|
|
|
|
if (lastPoint)
|
|
|
|
|
addOutput(newOutput[newOutput.length-1], printPoint = lastPoint.clone().setZ(zmax_outer), 0, 0, tool.getNumber());
|
|
|
|
|
|
|
|
|
|
// replace output single flattened layer with all points
|
|
|
|
|
print.output = newOutput;
|
2020-11-08 13:04:54 -05:00
|
|
|
return printPoint;
|
2020-11-05 15:14:20 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return tool Z clearance height for a line segment movement path
|
|
|
|
|
*/
|
|
|
|
|
function getZClearPath(terrain, x1, y1, x2, y2, z, zadd, off, over) {
|
2021-03-09 17:58:24 -05:00
|
|
|
// when terrain skipped, top + pass used
|
2020-12-06 15:58:28 -05:00
|
|
|
if (terrain > 0) {
|
2021-03-09 17:58:24 -05:00
|
|
|
return terrain;
|
2020-12-06 15:58:28 -05:00
|
|
|
}
|
2020-11-05 15:14:20 -05:00
|
|
|
let maxz = z;
|
|
|
|
|
let check = [];
|
|
|
|
|
for (let i=0; i<terrain.length; i++) {
|
|
|
|
|
let data = terrain[i];
|
|
|
|
|
check.push(data);
|
|
|
|
|
if (data.z + zadd < z) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
check.reverse();
|
|
|
|
|
for (let i=0; i<check.length; i++) {
|
|
|
|
|
let data = check[i];
|
|
|
|
|
let p1 = newPoint(x1, y1);
|
|
|
|
|
let p2 = newPoint(x2, y2);
|
|
|
|
|
let int = data.tops.map(p => p.intersections(p1, p2, true)).flat();
|
|
|
|
|
if (int.length) {
|
|
|
|
|
maxz = Math.max(maxz, data.z + zadd + over);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let s1 = p1.slopeTo(p2).toUnit().normal();
|
|
|
|
|
let s2 = p2.slopeTo(p1).toUnit().normal();
|
|
|
|
|
let pa = p1.projectOnSlope(s1, off);
|
|
|
|
|
let pb = p2.projectOnSlope(s1, off);
|
|
|
|
|
int = data.tops.map(p => p.intersections(pa, pb, true)).flat();
|
|
|
|
|
if (int.length) {
|
|
|
|
|
maxz = Math.max(maxz, data.z + zadd + over);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
pa = p1.projectOnSlope(s2, off);
|
|
|
|
|
pb = p2.projectOnSlope(s2, off);
|
|
|
|
|
int = data.tops.map(p => p.intersections(pa, pb, true)).flat();
|
|
|
|
|
if (int.length) {
|
|
|
|
|
maxz = Math.max(maxz, data.z + zadd + over);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return maxz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})();
|