move src/kiri/* to src/kiri/core and src/kiri-*/ to src/kiri/* and updated all imports
This commit is contained in:
parent
d47dc4e4e2
commit
43104fa5ac
214 changed files with 339 additions and 341 deletions
298
src/kiri/mode/cam/op-outline.js
Normal file
298
src/kiri/mode/cam/op-outline.js
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||
|
||||
import { CamOp } from './op.js';
|
||||
import { Tool } from './tool.js';
|
||||
import { newPolygon } from '../../../geo/polygon.js';
|
||||
import { newSlice } from '../../core/slice.js';
|
||||
import { polygons as POLY } from '../../../geo/polygons.js';
|
||||
import { util as base_util } from '../../../geo/base.js';
|
||||
import { poly2polyEmit } from '../../../geo/paths.js';
|
||||
import { newPoint } from '../../../geo/point.js';
|
||||
|
||||
class OpOutline extends CamOp {
|
||||
constructor(state, op) {
|
||||
super(state, op);
|
||||
}
|
||||
|
||||
async slice(progress) {
|
||||
let { op, state } = this;
|
||||
let { settings, widget, slicer, addSlices, tshadow, thruHoles, unsafe, color } = state;
|
||||
let { updateToolDiams, tabs, cutTabs, cutPolys, workarea, zMax } = state;
|
||||
let { process, stock } = settings;
|
||||
|
||||
if (op.down <= 0) {
|
||||
throw `invalid step down "${op.down}"`;
|
||||
}
|
||||
let toolDiam = this.toolDiam = new Tool(settings, op.tool).fluteDiameter();
|
||||
updateToolDiams(toolDiam);
|
||||
|
||||
let shadow = [];
|
||||
let slices = [];
|
||||
let intopt = {
|
||||
off: 0.01,
|
||||
fit: true,
|
||||
down: true,
|
||||
min: Math.max(0, workarea.bottom_z),
|
||||
max: workarea.top_z
|
||||
};
|
||||
let indices = slicer.interval(op.down, intopt);
|
||||
let trueShadow = process.camTrueShadow === true;
|
||||
// shift out first (top-most) slice
|
||||
indices.shift();
|
||||
// add flats to shadow
|
||||
const flats = Object.keys(slicer.zFlat)
|
||||
.map(v => (parseFloat(v) - 0.01).round(5))
|
||||
.filter(v => v > 0 && indices.indexOf(v) < 0);
|
||||
indices = indices.appendAll(flats).sort((a,b) => b-a);
|
||||
|
||||
let cnt = 0;
|
||||
let tot = 0;
|
||||
if (op.outside && !op.inside) {
|
||||
// console.log({outline_bypass: indices, down: op.down});
|
||||
indices.forEach((ind,i) => {
|
||||
if (flats.indexOf(ind) >= 0) {
|
||||
// exclude flats
|
||||
return;
|
||||
}
|
||||
let slice = newSlice(ind);
|
||||
slice.shadow = shadow.clone(true);
|
||||
slices.push(slice);
|
||||
});
|
||||
} else
|
||||
await slicer.slice(indices, { each: data => {
|
||||
shadow = unsafe ? data.tops : POLY.union(shadow.slice().appendAll(data.tops), 0.01, true);
|
||||
if (flats.indexOf(data.z) >= 0) {
|
||||
// exclude flats injected to complete shadow
|
||||
return;
|
||||
}
|
||||
data.shadow = trueShadow ? shadowAt(widget, data.z) : shadow.clone(true);
|
||||
data.slice.shadow = data.shadow;
|
||||
// data.slice.tops[0].inner = data.shadow;
|
||||
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
|
||||
slices.push(data.slice);
|
||||
// data.slice.xray();
|
||||
// onupdate(0.2 + (index/total) * 0.1, "outlines");
|
||||
progress(0.5 + 0.5 * (++cnt / tot));
|
||||
}, progress: (index, total) => {
|
||||
tot = total;
|
||||
progress((index / total) * 0.5);
|
||||
} });
|
||||
shadow = POLY.union(shadow.appendAll(state.shadow.base), 0.01, true);
|
||||
|
||||
// start slices at top of stock when `clear top` enabled
|
||||
if (op.top) {
|
||||
let first = slices[0];
|
||||
let zlist = slices.map(s => s.z);
|
||||
for (let z of indices.filter(v => v >= zMax)) {
|
||||
if (zlist.contains(z)) {
|
||||
continue;
|
||||
}
|
||||
let add = first.clone(true);
|
||||
add.tops.forEach(top => top.poly.setZ(add.z));
|
||||
add.shadow = first.shadow.clone(true);
|
||||
add.z = z;
|
||||
slices.splice(0,0,add);
|
||||
}
|
||||
}
|
||||
|
||||
// extend cut thru (only when z bottom is 0)
|
||||
if (workarea.bottom_z < 0) {
|
||||
let last = slices[slices.length-1];
|
||||
for (let zneg of base_util.lerp(0, -workarea.bottom_cut, op.down)) {
|
||||
if (!last) continue;
|
||||
let add = last.clone(true);
|
||||
add.tops.forEach(top => top.poly.setZ(add.z));
|
||||
add.shadow = last.shadow.clone(true);
|
||||
add.z -= zneg;
|
||||
slices.push(add);
|
||||
}
|
||||
}
|
||||
|
||||
slices.forEach(slice => {
|
||||
let tops = slice.shadow;
|
||||
|
||||
// outside only (use tshadow for entire cut)
|
||||
if (op.outside) {
|
||||
tops = tshadow;
|
||||
}
|
||||
|
||||
if (op.omitthru) {
|
||||
// eliminate thru holes from shadow
|
||||
for (let hole of thruHoles) {
|
||||
for (let top of tops) {
|
||||
if (!top.inner) continue;
|
||||
top.inner = top.inner.filter(innr => {
|
||||
return !innr.isEquivalent(hole, false, 0.1);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op.omitvoid) {
|
||||
for (let top of tops) {
|
||||
delete top.inner;
|
||||
}
|
||||
}
|
||||
|
||||
let offset = POLY.expand(tops, toolDiam / 2, slice.z);
|
||||
if (!(offset && offset.length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// when pocket only, drop first outer poly
|
||||
// if it matches the shell and promote inner polys
|
||||
if (op.inside) {
|
||||
let shell = POLY.expand(tops.clone(), toolDiam / 2);
|
||||
offset = POLY.filter(offset, [], function(poly) {
|
||||
if (poly.area() < 1) {
|
||||
return null;
|
||||
}
|
||||
for (let sp=0; sp<shell.length; sp++) {
|
||||
// eliminate shell only polys
|
||||
if (poly.isEquivalent(shell[sp])) {
|
||||
if (poly.inner) return poly.inner;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return poly;
|
||||
});
|
||||
} else {
|
||||
if (op.wide) {
|
||||
let stepover = toolDiam * op.step;
|
||||
let wideCuts = [] //accumulator for wide cuts
|
||||
for (let c = (op.steps || 1); c > 0; c--){
|
||||
offset.slice().forEach(op => {
|
||||
// clone removes inners but the real solution is
|
||||
// to limit expanded shells to through holes
|
||||
let wideCut = POLY.expand([op.clone(true)], stepover*c, slice.z, [], 1);
|
||||
wideCut.forEach(cut =>{ //set order of cuts when wide
|
||||
cut.order = c
|
||||
if(cut.inner) cut.inner.forEach(inn =>{ inn.order = c })
|
||||
});
|
||||
wideCuts.push(...wideCut)
|
||||
});
|
||||
}
|
||||
offset.appendAll(wideCuts);
|
||||
}
|
||||
}
|
||||
|
||||
if (op.dogbones && !op.wide) {
|
||||
addDogbones(offset, toolDiam / 5);
|
||||
}
|
||||
|
||||
if (tabs) {
|
||||
tabs.forEach(tab => {
|
||||
tab.off = POLY.expand([tab.poly], toolDiam / 2).flat();
|
||||
});
|
||||
offset = cutTabs(tabs, offset, slice.z);
|
||||
}
|
||||
|
||||
if (process.camStockClipTo && stock.x && stock.y && stock.center) {
|
||||
let rect = newPolygon().centerRectangle({x:0,y:0}, stock.x, stock.y);
|
||||
offset = cutPolys([rect], offset, slice.z, true);
|
||||
}
|
||||
|
||||
// offset.xout(`slice ${slice.z}`);
|
||||
slice.camLines = offset;
|
||||
});
|
||||
|
||||
// when top expand fails above, it creates an empty slice
|
||||
slices = slices.filter(s => s.camLines);
|
||||
|
||||
// project empty up and render
|
||||
for (let slice of slices) {
|
||||
if (false) slice.output()
|
||||
.setLayer("slice", {line: 0xaaaa00}, false)
|
||||
.addPolys(slice.topPolys())
|
||||
slice.output()
|
||||
.setLayer("outline", {face: color, line: color})
|
||||
.addPolys(slice.camLines);
|
||||
}
|
||||
|
||||
addSlices(slices);
|
||||
this.sliceOut = slices;
|
||||
}
|
||||
|
||||
prepare(ops, progress) {
|
||||
let { op, state, sliceOut } = this;
|
||||
let { setTool, setSpindle, setPrintPoint } = ops;
|
||||
let { polyEmit, depthOutlinePath } = ops;
|
||||
let { camOut, newLayer, printPoint } = ops;
|
||||
let { settings, widget } = state;
|
||||
let { process, controller } = settings;
|
||||
|
||||
let easeDown = process.camEaseDown;
|
||||
let toolDiam = this.toolDiam;
|
||||
let cutdir = op.ov_conv;//process.camConventional;
|
||||
let depthFirst = process.camDepthFirst;
|
||||
let depthData = [];
|
||||
|
||||
setTool(op.tool, op.rate, op.plunge);
|
||||
setSpindle(op.spindle);
|
||||
|
||||
// printpoint becomes NaN in engine mode. not sure why but this fixes it
|
||||
if(Object.values(printPoint).some(v=>Number.isNaN(v))){
|
||||
printPoint = newPoint(0,0,0);
|
||||
}
|
||||
|
||||
for (let slice of sliceOut) {
|
||||
let polys = [], t = [], c = [];
|
||||
let lines =POLY.flatten(slice.camLines)
|
||||
// console.log(lines);
|
||||
lines.forEach((poly)=> {
|
||||
poly.order = poly.order ?? 0;
|
||||
let child = poly.parent;
|
||||
if (depthFirst) { poly = poly.clone(); poly.parent = child ? 1 : 0 }
|
||||
if (child) c.push(poly); else t.push(poly);
|
||||
poly.layer = depthData.layer;
|
||||
polys.push(poly);
|
||||
});
|
||||
|
||||
// set cut direction on outer polys
|
||||
POLY.setWinding(t, cutdir);
|
||||
// set cut direction on inner polys
|
||||
POLY.setWinding(c, !cutdir);
|
||||
|
||||
if (depthFirst) {
|
||||
depthData.push(polys);
|
||||
} else {
|
||||
let orderSplit = {}
|
||||
polys.forEach(poly => {
|
||||
if(poly.order in orderSplit) orderSplit[poly.order].push(poly);
|
||||
else orderSplit[poly.order] = [poly];
|
||||
})
|
||||
Object.entries(orderSplit) //split the polys by order
|
||||
.sort((a,b) => -(a[0] - b[0] )) //sort by order (highest first)
|
||||
.forEach(([order, orderPolys]) => { // emit based on closest for each order
|
||||
let polyLast;
|
||||
// console.log({order, orderPolys});
|
||||
printPoint = poly2polyEmit(orderPolys, printPoint, function(poly, index, count) {
|
||||
|
||||
polyLast = polyEmit(poly, index, count, polyLast);
|
||||
}, { swapdir: false });
|
||||
})
|
||||
newLayer();
|
||||
}
|
||||
}
|
||||
|
||||
if (depthFirst) {
|
||||
let flatLevels = depthData.map(level => {
|
||||
return POLY.flatten(level.clone(true), [], true).filter(p => !(p.depth = 0));
|
||||
}).filter(l => l.length > 0);
|
||||
if (flatLevels.length && flatLevels[0].length) {
|
||||
// start with the smallest polygon on the top
|
||||
printPoint = flatLevels[0]
|
||||
.sort((a,b) => { return a.area() - b.area() })[0]
|
||||
.average();
|
||||
// experimental start of ease down
|
||||
let ease = op.down && easeDown ? 0.001 : 0;
|
||||
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, false, ease);
|
||||
printPoint = depthOutlinePath(printPoint, 0, flatLevels, toolDiam, polyEmit, true, ease);
|
||||
}
|
||||
}
|
||||
|
||||
setPrintPoint(printPoint);
|
||||
}
|
||||
}
|
||||
|
||||
export { OpOutline };
|
||||
Loading…
Reference in a new issue