diff --git a/src/kiri/mode/cam/anim-3d-be.js b/src/kiri/mode/cam/anim-3d-be.js index 204e5b2a..13df6084 100644 --- a/src/kiri/mode/cam/anim-3d-be.js +++ b/src/kiri/mode/cam/anim-3d-be.js @@ -5,31 +5,31 @@ import { CSG } from '../../../geo/csg.js'; import { Tool } from './tool.js'; -let nextMeshID = 1; -let stock, center, rez; -let path, pathIndex, tool, tools, last; - -let stockZ; -let stockIndexMsg = false; -let stockSlices; -let stockIndex; -let startTime; - -let toolID = -1; -let toolMesh; -let toolRadius; -let toolUpdateMsg; - -let animateClear = false; -let animating = false; -let moveDist = 0; -let renderDist = 0; -let renderDone = false; -let renderPause = 10; -let renderSteps = 0; -let renderSpeed = 0; -let indexCount = 0; -let updates = 0; +let nextMeshID = 1, + stock, + path, + pathIndex, + tool, + tools, + last, + stockZ, + stockIndexMsg = false, + stockSlices, + stockIndex, + startTime, + toolID = -1, + toolMesh, + toolRadius, + toolUpdateMsg, + showStock = false, + animateClear = false, + animating = false, + renderDone = false, + renderSteps = 0, + renderSpeed = 0, + indexCount = 0, + updates = 0, + maxR; export function init(worker) { const { dispatch } = worker; @@ -45,8 +45,6 @@ export function init(worker) { path = print.output.flat(); tools = settings.tools; stock = settings.stock; - rez = 1 / Math.sqrt(density / (stock.x * stock.y)); - tool = null; last = null; animating = false; @@ -56,11 +54,14 @@ export function init(worker) { startTime = 0; updates = 0; stockZ = isIndexed ? 0 : stock.z; + maxR = Math.hypot(stock.y, stock.z); stockSlices = []; const { x, y, z } = stock; const sliceCount = parseInt(settings.controller.animesh || 2000) / 100; const sliceWidth = stock.x / sliceCount; + + if (showStock) for (let i = 0; i < sliceCount; i++) { let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2; let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0); @@ -74,7 +75,6 @@ export function init(worker) { }; dispatch.animate2 = function (data, send) { - renderPause = data.pause || renderPause; renderSpeed = data.speed || 0; if (animating) { return send.done(); @@ -126,7 +126,6 @@ function renderPath(send) { return; } pathIndex++; - // console.log(next.point.z); if (next.tool && (!tool || tool.getID() !== next.tool.getID())) { // on real tool change, go to safe Z first @@ -143,44 +142,50 @@ function renderPath(send) { } const id = toolID; - const rezstep = rez * 2; + const rezstep = Math.min(tool.maxDiameter(), 1) / 4; + // console.log({ rezstep }); + if (last) { + // console.log(last.point, next.point); const lp = last.point, np = next.point; last = next; + + // render move line on client + send.data({ line: [ lp, np ]}); + // dwell ops have no point if (!np || !lp) { return renderPath(send); } + let dx = np.x - lp.x, dy = np.y - lp.y, dz = np.z - lp.z, - da = Math.abs((np.a || 0) - (lp.a || 0)), - dr = (da / 360) * (2 * Math.PI * Math.max(np.z, lp.z)), - dist = Math.sqrt(dx * dx + dy * dy + dz * dz + dr * dr); - - moveDist += dist; - - // skip moves that are less than grid resolution - if (moveDist < rezstep) { - // console.log('skip', moveDist, rezstep, next); - renderPath(send); - return; - } + da = (np.a ?? 0) - (lp.a ?? 0), // delta rotation degrees + AD = maxR * 2, // arc diameter @ max z + dr = (Math.abs(da) / 360) * AD, // delta rotation arc + dist = Math.max(Math.hypot(dx, dy, dz), dr); // motion distance + // console.log({ dz, da, AD, dr, dist }); + // sub-divide long moves for animation and better boolean ops const md = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), dr); - const st = Math.ceil(md / rezstep); - const mx = dx / st, my = dy / st, mz = dz / st; - // const sd = Math.sqrt(mx*mx + my*my + mz*mz + dr*dr); - const sd = Math.sqrt(mx * mx + my * my + Math.min(1, mz * mz) + dr * dr); + const st = Math.ceil(md / rezstep); // steps + const mx = dx / st, my = dy / st, mz = dz / st, ma = da / st; + const sd = Math.sqrt(mx * mx + my * my + mz * mz + ma * ma); + const sp = next.speed; const moves = []; - for (let i = 0, x = lp.x, y = lp.y, z = lp.z; i < st; i++) { - moves.push({ x, y, z, a: lp.a, md: sd, dx, dy, dz }); + for (let i = 0, x = lp.x, y = lp.y, z = lp.z, a = lp.a; i < st; i++) { + moves.push({ x, y, z, a, md: sd, dx, dy, dz, sp }); x += mx; y += my; z += mz; + a += ma; } - moveDist = 0; - moves.push({ ...next.point, md: sd }); + // console.log({ dist: md, steps: st, speed: next.speed, moves }); + + // push last point + moves.push({ ...next.point, md: sd, sp }); + // console.log(...moves); renderMoves(id, moves, send); } else { last = next; @@ -197,7 +202,7 @@ function renderMoves(id, moves, send, seed = 0) { if (!pos) { throw `no pos @ ${index} of ${moves.length}`; } - const { dx, dy, dz } = pos; + const { dx, dy, dz, sp, md } = pos; toolMove(pos); // console.log('renderMoves', {id, moves, seed}); let subs = 0; @@ -208,15 +213,14 @@ function renderMoves(id, moves, send, seed = 0) { subs++; } } - // console.log({ index, subs }); - renderDist += pos.md; // pause renderer at specified offsets - if (renderSpeed && renderDist >= renderSpeed) { - renderDist = 0; + if (renderSpeed) { + let time = sp ? (md / sp) * 60000 : 10; + // console.log({ time, ...pos }); renderUpdate(send); setTimeout(() => { renderMoves(id, moves, send, index + 1); - }, renderPause); + }, time / renderSpeed); return; } } diff --git a/src/kiri/mode/cam/anim-3d-fe.js b/src/kiri/mode/cam/anim-3d-fe.js index 3f31d97f..2300fdab 100644 --- a/src/kiri/mode/cam/anim-3d-fe.js +++ b/src/kiri/mode/cam/anim-3d-fe.js @@ -10,24 +10,26 @@ const { client } = api; let meshes = {}, button = {}, label = {}, + lineTracker, material, - speedPauses = [0, 0, 0, 0, 0], speedValues = [1, 2, 4, 8, 32], speedNames = ["1x", "2x", "4x", "8x", "!!"], speedMax = speedValues.length - 1, speedIndex = 0, speed, origin, - color = 0; + color = 0, + A2R = Math.PI / 180; export function animate_clear2(api) { let { anim } = api.ui; - client.animate_cleanup2(); + lineTracker?.clear(); Object.keys(meshes).forEach(id => deleteMesh(id)); api.widgets.setAxisIndex(0); api.uc.setVisible(anim.laba, true); api.uc.setVisible(anim.vala, true); anim.vala.value = "0.0"; + client.animate_cleanup2(); } export function animate2(api, delay) { @@ -86,8 +88,9 @@ Object.assign(client, { }, animate_setup2(settings, ondone) { + initPathMesh(); color = settings.controller.dark ? 0x888888 : 0; - material = new THREE.MeshPhongMaterial({ + material = material ?? new THREE.MeshPhongMaterial({ flatShading: true, transparent: false, opacity: 0.5, @@ -102,6 +105,66 @@ Object.assign(client, { } }); +function initPathMesh() { + if (lineTracker) { + return lineTracker.clear(); + } + let count = 0, + max = 10000, + rot = new THREE.Euler(0,0,0), + geo = new THREE.BufferGeometry(), + mat = new THREE.LineBasicMaterial({ color: 0xffff00 }), + pos = new Float32Array(max * 6), + lines = new THREE.LineSegments(geo, mat), + vec = new THREE.Vector3(0,0,0), + ang = new THREE.Vector3(1,0,0); + geo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); + let track = lineTracker = { + xro(p) { + vec.set(p.x, p.y, p.z); + vec.applyAxisAngle(ang, p.a * A2R); + }, + add(p1, p2) { + let ind = count * 6; + track.xro(p1); + pos[ind+0] = vec.x; + pos[ind+1] = vec.y; + pos[ind+2] = vec.z; + track.xro(p2); + pos[ind+3] = vec.x; + pos[ind+4] = vec.y; + pos[ind+5] = vec.z; + count++; + if (count >= max) { + let opos = pos; + max += 10000; + pos = new Float32Array(max * 6), + pos.set(opos); + geo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); + } + geo.setDrawRange(0, count * 2); + geo.attributes.position.needsUpdate = true; + track.visible(true); + }, + clear() { + count = 0; + geo.setDrawRange(0,0); + track.rotate(0); + track.visible(false); + }, + rotate(angle) { + rot.x = angle * A2R; + lines.setRotationFromEuler(rot); + }, + visible(bool) { + if (bool !== lines.visible) { + lines.visible = bool; + } + } + } + space.world.add(lines); +} + function meshAdd(id, ind, pos, ilen, plen) { const geo = new THREE.BufferGeometry(); const pa = plen ? pos.subarray(0, plen * 3) : pos; @@ -132,6 +195,7 @@ function meshUpdate(id, ind, pos, ilen, plen) { function deleteMesh(id) { space.world.remove(meshes[id]); + meshes[id].geometry.dispose(); delete meshes[id]; } @@ -165,8 +229,7 @@ function play(opts) { } client.animate2({ speed, - steps: steps || Infinity, - pause: speedPauses[speedIndex] + steps: steps || Infinity }, handleUpdate); } @@ -177,8 +240,7 @@ function fast(opts) { button.pause.style.display = ''; client.animate2({ speed, - steps: steps || Infinity, - pause: speedPauses[speedIndex] + steps: steps || Infinity }, handleUpdate); } @@ -216,6 +278,7 @@ function handleUpdate(data) { if (data.stock_index !== undefined) { api.widgets.setAxisIndex(data.stock_index); label.a.value = -data.stock_index.toFixed(1); + lineTracker.rotate(-data.stock_index); } if (data.mesh_index) { const { id, index } = data.mesh_index; @@ -229,9 +292,12 @@ function handleUpdate(data) { const { id, ind, pos, ilen, plen } = data.mesh_update; meshUpdate(id, ind, pos, ilen, plen); } - if (data && data.progress) { + if (data.progress) { label.progress.value = (data.progress * 100).toFixed(1); } + if (data.line) { + lineTracker.add(...data.line); + } } function updateSpeed(inc = 0) { diff --git a/src/kiri/mode/cam/client.js b/src/kiri/mode/cam/client.js index 8f6af172..65eba6db 100644 --- a/src/kiri/mode/cam/client.js +++ b/src/kiri/mode/cam/client.js @@ -29,6 +29,7 @@ const { widgets: WIDGETS } = api; class Client { animVer = 0; camStock; + showStock = true; current; currentIndex; flipping; @@ -640,7 +641,8 @@ export function init() { }); api.event.on("cam.stock.toggle", (bool) => { - env.camStock && (env.camStock.visible = bool ?? !env.camStock.visible); + env.showStock = bool ?? !env.showStock; + updateStock(); }); api.event.on("boolean.click", api.platform.update_bounds); diff --git a/src/kiri/mode/cam/export.js b/src/kiri/mode/cam/export.js index 09571532..a162786c 100644 --- a/src/kiri/mode/cam/export.js +++ b/src/kiri/mode/cam/export.js @@ -358,7 +358,6 @@ export function cam_export(print, online) { // } append(nl.join('')); points++; - if (dz < 0) console.log(nl); } // look for SCALE header directive diff --git a/web/kiri/index.css b/web/kiri/index.css index ad3b7705..efc8c86d 100644 --- a/web/kiri/index.css +++ b/web/kiri/index.css @@ -1378,7 +1378,7 @@ details[open] summary::after { padding: 0 0 0 0; } #oplist { - max-height: 70vh; + max-height: calc(70vh - 200px); min-height: 0; overflow-y: auto; padding-top: 1px;