From 7650ce0640f7575da1b9382122807133a887e0c5 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 2 May 2022 16:54:13 -0400 Subject: [PATCH] speed up true shadow computation. progressively compute shadow in all cases. hide ops on view change --- release.md | 2 + src/kiri-mode/cam/client.js | 3 +- src/kiri-mode/cam/ops.js | 101 ++++++++++++++---------------------- src/kiri-mode/cam/slice.js | 31 ++++++++++- 4 files changed, 73 insertions(+), 64 deletions(-) diff --git a/release.md b/release.md index 6bcf7a51..14aa527f 100644 --- a/release.md +++ b/release.md @@ -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` add CAM pocket operation using surface selection * `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 * `-` improve vertex replacement and widget update matrix tracking diff --git a/src/kiri-mode/cam/client.js b/src/kiri-mode/cam/client.js index cae4b444..ab8c101b 100644 --- a/src/kiri-mode/cam/client.js +++ b/src/kiri-mode/cam/client.js @@ -90,6 +90,7 @@ CAM.init = function(kiri, api) { isAnimate = false; CAM.animate_clear(api); func.clearPops(); + $('camops').style.display = isCamMode && isArrange ? 'flex' : ''; }); api.event.on("settings.saved", (settings) => { @@ -271,7 +272,7 @@ CAM.init = function(kiri, api) { }); 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; if (!(isCamMode && oplist)) return; let mark = Date.now(); diff --git a/src/kiri-mode/cam/ops.js b/src/kiri-mode/cam/ops.js index e4f31d6a..02118c90 100644 --- a/src/kiri-mode/cam/ops.js +++ b/src/kiri-mode/cam/ops.js @@ -110,7 +110,7 @@ class OpRough extends CamOp { let { op, state } = this; let { settings, widget, slicer, sliceAll, unsafe, color } = 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; if (op.down <= 0) { @@ -204,7 +204,8 @@ class OpRough extends CamOp { // exclude flats injected to complete shadow 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.tops[0].inner = data.shadow; // 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 { tool, rate, down, plunge, expand } = op; 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; // generate tracing offsets from chosen features let sliceOut = this.sliceOut = []; @@ -945,35 +946,13 @@ class OpPocket extends CamOp { sliceOut.push(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) { let zs = down ? base.util.lerp(zTop, z, down) : [ z ]; if (expand) polys = POLY.offset(polys, expand); let zpro = 0, zinc = pinc / (polys.length * zs.length); for (let poly of polys) { - genShadows(zs); for (let z of zs) { - let shadow = shadows[z]; + let shadow = shadowAt(z); let clip = []; POLY.subtract([ poly ], shadow, clip); let slice = newSliceOut(z); @@ -1016,6 +995,9 @@ class OpPocket extends CamOp { outline = POLY.union(outline, 0.0001, true); outline = POLY.setWinding(outline, cutdir, false); outline = healPolys(outline); + if (outline.length === 0) { + continue; + } if (false) newSliceOut(zmin).output() .setLayer("pocket area", {line: 0x1188ff}, false) .addPolys(outline) @@ -1315,7 +1297,7 @@ class OpShadow extends CamOp { slice(progress) { 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 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 terrain = slicer.slice(tzindex, { each: (data, index, total) => { - let shadowAt = trueShadow ? CAM.shadowAt(widget, data.z, lsz) : []; - tshadow = POLY.union(tshadow.slice().appendAll(data.tops).appendAll(shadowAt), 0.01, true); + // let shadow = trueShadow ? CAM.shadowAt(widget, data.z, lsz) : []; + let shadow = trueShadow ? shadowAt(data.z, lsz) : []; + tshadow = POLY.union(tshadow.slice().appendAll(data.tops).appendAll(shadow), 0.01, true); tslices.push(data.slice); data.slice.shadow = tshadow; if (false) { @@ -1373,8 +1356,8 @@ class OpShadow extends CamOp { state.shadowTop = terrain[terrain.length - 1]; state.center = tshadow[0].bounds.center(); - state.tshadow = tshadow; - state.terrain = terrain; + state.tshadow = tshadow; // true shadow (bottom of part) + state.terrain = terrain; // stack of shadow slices with tops state.tslices = tslices; 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) { - let geo = widget.cache.geo || new THREE.BufferGeometry() + const geo = widget.cache.geo || new THREE.BufferGeometry() .setAttribute('position', new THREE.BufferAttribute(widget.vertices, 3)); widget.cache.geo = geo; - let rad = (Math.PI / 180); - let deg = (180 / Math.PI); - let angle = rad * 1; - let thresh = -Math.sin(angle); - let found = []; - let { position } = geo.attributes; - let { itemSize, count, array } = position; - if (widget._shadow_array) { - array = widget._shadow_array; - } else { - array = widget._shadow_array = [...array].map(v => v.round(3)); + const { position } = geo.attributes; + const { itemSize, count, array } = position; + // cache faces with normals up + if (!widget._shadow_faces) { + const faces = []; + for (let i=0, ip=0; i 0.001) { + faces.push(a,b,c); + } + } + widget._shadow_faces = faces; } - for (let i = 0; i ztop && b.z > ztop && c.z > ztop) { // skip faces over top threshold @@ -1413,19 +1403,10 @@ CAM.shadowAt = function(widget, z, ztop) { // skip faces under threshold continue; } 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]); - continue; } else { // check faces straddling threshold - where = {under: [], over: [], on: []}; - } - if (where) { - let { checkOverUnderOn, intersectPoints } = self.kiri.cam_slicer; + const where = { under: [], over: [], on: [] }; checkOverUnderOn(newPoint(a.x, a.y, a.z), z, where); checkOverUnderOn(newPoint(b.x, b.y, b.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}); } } - continue; - } else { - continue; } - found.push([a,b,c]); } let polys = found.map(a => { return newPolygon() diff --git a/src/kiri-mode/cam/slice.js b/src/kiri-mode/cam/slice.js index b6ce475d..4d679102 100644 --- a/src/kiri-mode/cam/slice.js +++ b/src/kiri-mode/cam/slice.js @@ -107,6 +107,34 @@ CAM.slice = function(settings, widget, onupdate, ondone) { 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 = { settings, widget, @@ -115,6 +143,7 @@ CAM.slice = function(settings, widget, onupdate, ondone) { cutTabs, cutPolys, healPolys, + shadowAt, slicer, sliceAll, updateToolDiams, @@ -138,7 +167,7 @@ CAM.slice = function(settings, widget, onupdate, ondone) { } 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)) { let opfn = OPS[op.type];