speed up true shadow computation. progressively compute shadow in all cases. hide ops on view change

This commit is contained in:
Stewart Allen 2022-05-02 16:54:13 -04:00
commit 7650ce0640
4 changed files with 73 additions and 64 deletions

View file

@ -13,6 +13,8 @@ Full docs @ https://docs.grid.space/projects/kiri-moto
* `C` fix CAM output order for tool changes an spindle speed updates * `C` fix CAM output order for tool changes an spindle speed updates
* `C` add CAM pocket operation using surface selection * `C` add CAM pocket operation using surface selection
* `C` fix dogbones on outlines cut by tabs * `C` fix dogbones on outlines cut by tabs
* `C` skip pockets that resolve to null
* `C` 10x speedup for true shadow generation
* `-` refactor cnc to use new core slicer engine * `-` refactor cnc to use new core slicer engine
* `-` improve vertex replacement and widget update matrix tracking * `-` improve vertex replacement and widget update matrix tracking

View file

@ -90,6 +90,7 @@ CAM.init = function(kiri, api) {
isAnimate = false; isAnimate = false;
CAM.animate_clear(api); CAM.animate_clear(api);
func.clearPops(); func.clearPops();
$('camops').style.display = isCamMode && isArrange ? 'flex' : '';
}); });
api.event.on("settings.saved", (settings) => { api.event.on("settings.saved", (settings) => {
@ -271,7 +272,7 @@ CAM.init = function(kiri, api) {
}); });
api.event.on("cam.op.render", func.opRender = () => { api.event.on("cam.op.render", func.opRender = () => {
$('camops').style.display = isCamMode && isArrange ? 'flex' : ''; // $('camops').style.display = isCamMode && isArrange ? 'flex' : '';
let oplist = current.process.ops; let oplist = current.process.ops;
if (!(isCamMode && oplist)) return; if (!(isCamMode && oplist)) return;
let mark = Date.now(); let mark = Date.now();

View file

@ -110,7 +110,7 @@ class OpRough extends CamOp {
let { op, state } = this; let { op, state } = this;
let { settings, widget, slicer, sliceAll, unsafe, color } = state; let { settings, widget, slicer, sliceAll, unsafe, color } = state;
let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state; let { updateToolDiams, thruHoles, tabs, cutTabs, cutPolys } = state;
let { tshadow, shadowTop, ztOff, zBottom, zMax } = state; let { tshadow, shadowTop, ztOff, zBottom, zMax, shadowAt } = state;
let { process, stock } = settings; let { process, stock } = settings;
if (op.down <= 0) { if (op.down <= 0) {
@ -204,7 +204,8 @@ class OpRough extends CamOp {
// exclude flats injected to complete shadow // exclude flats injected to complete shadow
return; return;
} }
data.shadow = trueShadow ? CAM.shadowAt(widget, data.z) : shadow.clone(true); // data.shadow = trueShadow ? CAM.shadowAt(widget, data.z) : shadow.clone(true);
data.shadow = trueShadow ? shadowAt(data.z) : shadow.clone(true);
data.slice.shadow = data.shadow; data.slice.shadow = data.shadow;
// data.slice.tops[0].inner = data.shadow; // data.slice.tops[0].inner = data.shadow;
// data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z); // data.slice.tops[0].inner = POLY.setZ(tshadow.clone(true), data.z);
@ -926,7 +927,7 @@ class OpPocket extends CamOp {
let { op, state } = this; let { op, state } = this;
let { tool, rate, down, plunge, expand } = op; let { tool, rate, down, plunge, expand } = op;
let { settings, widget, sliceAll, zMax, zTop, zThru, tabs } = state; let { settings, widget, sliceAll, zMax, zTop, zThru, tabs } = state;
let { updateToolDiams, cutTabs, cutPolys, healPolys } = state; let { updateToolDiams, cutTabs, cutPolys, healPolys, shadowAt } = state;
let { process, stock } = settings; let { process, stock } = settings;
// generate tracing offsets from chosen features // generate tracing offsets from chosen features
let sliceOut = this.sliceOut = []; let sliceOut = this.sliceOut = [];
@ -945,35 +946,13 @@ class OpPocket extends CamOp {
sliceOut.push(slice); sliceOut.push(slice);
return slice; return slice;
} }
let shadows = {};
function genShadows(zs) {
for (let z of zs) {
if (shadows[z]) continue;
// find closest shadow above and use
let minzkey;
let zover = Object.keys(shadows).map(v => parseFloat(v)).filter(v => v > z);
for (let zkey of zover) {
if (minzkey && zkey < minzkey) {
minzkey = zkey;
} else {
minzkey = zkey;
}
}
let shadow = CAM.shadowAt(widget, z + 0.001, minzkey);
if (minzkey) {
shadow = POLY.union([...shadow, ...shadows[minzkey]], undefined, true);
}
shadows[z] = POLY.setZ(shadow, z);
}
}
function clearZ(polys, z, down) { function clearZ(polys, z, down) {
let zs = down ? base.util.lerp(zTop, z, down) : [ z ]; let zs = down ? base.util.lerp(zTop, z, down) : [ z ];
if (expand) polys = POLY.offset(polys, expand); if (expand) polys = POLY.offset(polys, expand);
let zpro = 0, zinc = pinc / (polys.length * zs.length); let zpro = 0, zinc = pinc / (polys.length * zs.length);
for (let poly of polys) { for (let poly of polys) {
genShadows(zs);
for (let z of zs) { for (let z of zs) {
let shadow = shadows[z]; let shadow = shadowAt(z);
let clip = []; let clip = [];
POLY.subtract([ poly ], shadow, clip); POLY.subtract([ poly ], shadow, clip);
let slice = newSliceOut(z); let slice = newSliceOut(z);
@ -1016,6 +995,9 @@ class OpPocket extends CamOp {
outline = POLY.union(outline, 0.0001, true); outline = POLY.union(outline, 0.0001, true);
outline = POLY.setWinding(outline, cutdir, false); outline = POLY.setWinding(outline, cutdir, false);
outline = healPolys(outline); outline = healPolys(outline);
if (outline.length === 0) {
continue;
}
if (false) newSliceOut(zmin).output() if (false) newSliceOut(zmin).output()
.setLayer("pocket area", {line: 0x1188ff}, false) .setLayer("pocket area", {line: 0x1188ff}, false)
.addPolys(outline) .addPolys(outline)
@ -1315,7 +1297,7 @@ class OpShadow extends CamOp {
slice(progress) { slice(progress) {
let state = this.state; let state = this.state;
let { ops, slicer, widget, unsafe, sliceAll } = state; let { ops, slicer, widget, unsafe, sliceAll, shadowAt } = state;
let real = ops.map(rec => rec.op).filter(op => op); let real = ops.map(rec => rec.op).filter(op => op);
let drill = real.filter(op => op.type === 'drill').length > 0; let drill = real.filter(op => op.type === 'drill').length > 0;
@ -1341,8 +1323,9 @@ class OpShadow extends CamOp {
let lsz; // only shadow up to bottom of last shadow for progressive union let lsz; // only shadow up to bottom of last shadow for progressive union
let terrain = slicer.slice(tzindex, { each: (data, index, total) => { let terrain = slicer.slice(tzindex, { each: (data, index, total) => {
let shadowAt = trueShadow ? CAM.shadowAt(widget, data.z, lsz) : []; // let shadow = trueShadow ? CAM.shadowAt(widget, data.z, lsz) : [];
tshadow = POLY.union(tshadow.slice().appendAll(data.tops).appendAll(shadowAt), 0.01, true); let shadow = trueShadow ? shadowAt(data.z, lsz) : [];
tshadow = POLY.union(tshadow.slice().appendAll(data.tops).appendAll(shadow), 0.01, true);
tslices.push(data.slice); tslices.push(data.slice);
data.slice.shadow = tshadow; data.slice.shadow = tshadow;
if (false) { if (false) {
@ -1373,8 +1356,8 @@ class OpShadow extends CamOp {
state.shadowTop = terrain[terrain.length - 1]; state.shadowTop = terrain[terrain.length - 1];
state.center = tshadow[0].bounds.center(); state.center = tshadow[0].bounds.center();
state.tshadow = tshadow; state.tshadow = tshadow; // true shadow (bottom of part)
state.terrain = terrain; state.terrain = terrain; // stack of shadow slices with tops
state.tslices = tslices; state.tslices = tslices;
state.skipTerrain = skipTerrain; state.skipTerrain = skipTerrain;
@ -1383,27 +1366,34 @@ class OpShadow extends CamOp {
} }
} }
// union triangles > z (opt cap < ztop) into polygon(s)
CAM.shadowAt = function(widget, z, ztop) { CAM.shadowAt = function(widget, z, ztop) {
let geo = widget.cache.geo || new THREE.BufferGeometry() const geo = widget.cache.geo || new THREE.BufferGeometry()
.setAttribute('position', new THREE.BufferAttribute(widget.vertices, 3)); .setAttribute('position', new THREE.BufferAttribute(widget.vertices, 3));
widget.cache.geo = geo; widget.cache.geo = geo;
let rad = (Math.PI / 180); const { position } = geo.attributes;
let deg = (180 / Math.PI); const { itemSize, count, array } = position;
let angle = rad * 1; // cache faces with normals up
let thresh = -Math.sin(angle); if (!widget._shadow_faces) {
let found = []; const faces = [];
let { position } = geo.attributes; for (let i=0, ip=0; i<count; i += itemSize) {
let { itemSize, count, array } = position; const a = new THREE.Vector3(array[ip++], array[ip++], array[ip++]);
if (widget._shadow_array) { const b = new THREE.Vector3(array[ip++], array[ip++], array[ip++]);
array = widget._shadow_array; const c = new THREE.Vector3(array[ip++], array[ip++], array[ip++]);
} else { const n = THREE.computeFaceNormal(a,b,c);
array = widget._shadow_array = [...array].map(v => v.round(3)); if (n.z > 0.001) {
faces.push(a,b,c);
}
}
widget._shadow_faces = faces;
} }
for (let i = 0; i<count; i += 3) { const found = [];
let ip = i * itemSize; const faces = widget._shadow_faces;
let a = new THREE.Vector3(array[ip++], array[ip++], array[ip++]); const { checkOverUnderOn, intersectPoints } = self.kiri.cam_slicer;
let b = new THREE.Vector3(array[ip++], array[ip++], array[ip++]); for (let i=0; i<faces.length; ) {
let c = new THREE.Vector3(array[ip++], array[ip++], array[ip++]); const a = faces[i++];
const b = faces[i++];
const c = faces[i++];
let where = undefined; let where = undefined;
if (ztop && a.z > ztop && b.z > ztop && c.z > ztop) { if (ztop && a.z > ztop && b.z > ztop && c.z > ztop) {
// skip faces over top threshold // skip faces over top threshold
@ -1413,19 +1403,10 @@ CAM.shadowAt = function(widget, z, ztop) {
// skip faces under threshold // skip faces under threshold
continue; continue;
} else if (a.z > z && b.z > z && c.z > z) { } else if (a.z > z && b.z > z && c.z > z) {
// limit to selected faces over threshold
let norm = THREE.computeFaceNormal(a,b,c);
if (norm.z < thresh) {
continue;
}
found.push([a,b,c]); found.push([a,b,c]);
continue;
} else { } else {
// check faces straddling threshold // check faces straddling threshold
where = {under: [], over: [], on: []}; const where = { under: [], over: [], on: [] };
}
if (where) {
let { checkOverUnderOn, intersectPoints } = self.kiri.cam_slicer;
checkOverUnderOn(newPoint(a.x, a.y, a.z), z, where); checkOverUnderOn(newPoint(a.x, a.y, a.z), z, where);
checkOverUnderOn(newPoint(b.x, b.y, b.z), z, where); checkOverUnderOn(newPoint(b.x, b.y, b.z), z, where);
checkOverUnderOn(newPoint(c.x, c.y, c.z), z, where); checkOverUnderOn(newPoint(c.x, c.y, c.z), z, where);
@ -1445,11 +1426,7 @@ CAM.shadowAt = function(widget, z, ztop) {
console.log({msg: "invalid ips", line: line, where: where}); console.log({msg: "invalid ips", line: line, where: where});
} }
} }
continue;
} else {
continue;
} }
found.push([a,b,c]);
} }
let polys = found.map(a => { let polys = found.map(a => {
return newPolygon() return newPolygon()

View file

@ -107,6 +107,34 @@ CAM.slice = function(settings, widget, onupdate, ondone) {
maxToolDiam = Math.max(maxToolDiam, toolDiam); maxToolDiam = Math.max(maxToolDiam, toolDiam);
} }
let shadows = {};
function shadowAt(z) {
let cached = shadows[z];
if (cached) {
return cached;
}
// find closest shadow above and use to speed up delta shadow gen
let minZabove;
let zover = Object.keys(shadows).map(v => parseFloat(v)).filter(v => v > z);
for (let zkey of zover) {
if (minZabove && zkey < minZabove) {
minZabove = zkey;
} else {
minZabove = zkey;
}
}
let shadow = CAM.shadowAt(widget, z, minZabove);
if (minZabove) {
// const merge = shadow.length;
// const plus = shadows[minZabove].length;
// const mark = Date.now();
shadow = POLY.union([...shadow, ...shadows[minZabove]], 0.0011, true);
// console.log({merge, plus, equals: shadow.length, time: Date.now() - mark});
}
return shadows[z] = POLY.setZ(shadow, z);
}
let state = { let state = {
settings, settings,
widget, widget,
@ -115,6 +143,7 @@ CAM.slice = function(settings, widget, onupdate, ondone) {
cutTabs, cutTabs,
cutPolys, cutPolys,
healPolys, healPolys,
shadowAt,
slicer, slicer,
sliceAll, sliceAll,
updateToolDiams, updateToolDiams,
@ -138,7 +167,7 @@ CAM.slice = function(settings, widget, onupdate, ondone) {
} }
let opSum = 0; let opSum = 0;
let opTot = opList.map(op => op.weight()).reduce((a,v) => a + v); let opTot = opList.length ? opList.map(op => op.weight()).reduce((a,v) => a + v) : 0;
for (let op of proc.ops.filter(op => !op.disabled)) { for (let op of proc.ops.filter(op => !op.disabled)) {
let opfn = OPS[op.type]; let opfn = OPS[op.type];