refactor cam indexed animations to show cut path only

This commit is contained in:
Stewart Allen 2025-10-13 20:45:23 -04:00
commit 4d9250e662
5 changed files with 139 additions and 68 deletions

View file

@ -5,31 +5,31 @@
import { CSG } from '../../../geo/csg.js'; import { CSG } from '../../../geo/csg.js';
import { Tool } from './tool.js'; import { Tool } from './tool.js';
let nextMeshID = 1; let nextMeshID = 1,
let stock, center, rez; stock,
let path, pathIndex, tool, tools, last; path,
pathIndex,
let stockZ; tool,
let stockIndexMsg = false; tools,
let stockSlices; last,
let stockIndex; stockZ,
let startTime; stockIndexMsg = false,
stockSlices,
let toolID = -1; stockIndex,
let toolMesh; startTime,
let toolRadius; toolID = -1,
let toolUpdateMsg; toolMesh,
toolRadius,
let animateClear = false; toolUpdateMsg,
let animating = false; showStock = false,
let moveDist = 0; animateClear = false,
let renderDist = 0; animating = false,
let renderDone = false; renderDone = false,
let renderPause = 10; renderSteps = 0,
let renderSteps = 0; renderSpeed = 0,
let renderSpeed = 0; indexCount = 0,
let indexCount = 0; updates = 0,
let updates = 0; maxR;
export function init(worker) { export function init(worker) {
const { dispatch } = worker; const { dispatch } = worker;
@ -45,8 +45,6 @@ export function init(worker) {
path = print.output.flat(); path = print.output.flat();
tools = settings.tools; tools = settings.tools;
stock = settings.stock; stock = settings.stock;
rez = 1 / Math.sqrt(density / (stock.x * stock.y));
tool = null; tool = null;
last = null; last = null;
animating = false; animating = false;
@ -56,11 +54,14 @@ export function init(worker) {
startTime = 0; startTime = 0;
updates = 0; updates = 0;
stockZ = isIndexed ? 0 : stock.z; stockZ = isIndexed ? 0 : stock.z;
maxR = Math.hypot(stock.y, stock.z);
stockSlices = []; stockSlices = [];
const { x, y, z } = stock; const { x, y, z } = stock;
const sliceCount = parseInt(settings.controller.animesh || 2000) / 100; const sliceCount = parseInt(settings.controller.animesh || 2000) / 100;
const sliceWidth = stock.x / sliceCount; const sliceWidth = stock.x / sliceCount;
if (showStock)
for (let i = 0; i < sliceCount; i++) { for (let i = 0; i < sliceCount; i++) {
let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2; let xmin = -(x / 2) + (i * sliceWidth) + sliceWidth / 2;
let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0); let slice = new Stock(sliceWidth, y, z).translate(xmin, 0, 0);
@ -74,7 +75,6 @@ export function init(worker) {
}; };
dispatch.animate2 = function (data, send) { dispatch.animate2 = function (data, send) {
renderPause = data.pause || renderPause;
renderSpeed = data.speed || 0; renderSpeed = data.speed || 0;
if (animating) { if (animating) {
return send.done(); return send.done();
@ -126,7 +126,6 @@ function renderPath(send) {
return; return;
} }
pathIndex++; pathIndex++;
// console.log(next.point.z);
if (next.tool && (!tool || tool.getID() !== next.tool.getID())) { if (next.tool && (!tool || tool.getID() !== next.tool.getID())) {
// on real tool change, go to safe Z first // on real tool change, go to safe Z first
@ -143,44 +142,50 @@ function renderPath(send) {
} }
const id = toolID; const id = toolID;
const rezstep = rez * 2; const rezstep = Math.min(tool.maxDiameter(), 1) / 4;
// console.log({ rezstep });
if (last) { if (last) {
// console.log(last.point, next.point);
const lp = last.point, np = next.point; const lp = last.point, np = next.point;
last = next; last = next;
// render move line on client
send.data({ line: [ lp, np ]});
// dwell ops have no point // dwell ops have no point
if (!np || !lp) { if (!np || !lp) {
return renderPath(send); return renderPath(send);
} }
let dx = np.x - lp.x, let dx = np.x - lp.x,
dy = np.y - lp.y, dy = np.y - lp.y,
dz = np.z - lp.z, dz = np.z - lp.z,
da = Math.abs((np.a || 0) - (lp.a || 0)), da = (np.a ?? 0) - (lp.a ?? 0), // delta rotation degrees
dr = (da / 360) * (2 * Math.PI * Math.max(np.z, lp.z)), AD = maxR * 2, // arc diameter @ max z
dist = Math.sqrt(dx * dx + dy * dy + dz * dz + dr * dr); dr = (Math.abs(da) / 360) * AD, // delta rotation arc
dist = Math.max(Math.hypot(dx, dy, dz), dr); // motion distance
moveDist += dist;
// skip moves that are less than grid resolution
if (moveDist < rezstep) {
// console.log('skip', moveDist, rezstep, next);
renderPath(send);
return;
}
// 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 md = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), dr);
const st = Math.ceil(md / rezstep); const st = Math.ceil(md / rezstep); // steps
const mx = dx / st, my = dy / st, mz = dz / st; const mx = dx / st, my = dy / st, mz = dz / st, ma = da / st;
// const sd = Math.sqrt(mx*mx + my*my + mz*mz + dr*dr); const sd = Math.sqrt(mx * mx + my * my + mz * mz + ma * ma);
const sd = Math.sqrt(mx * mx + my * my + Math.min(1, mz * mz) + dr * dr); const sp = next.speed;
const moves = []; const moves = [];
for (let i = 0, x = lp.x, y = lp.y, z = lp.z; i < st; i++) { 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: lp.a, md: sd, dx, dy, dz }); moves.push({ x, y, z, a, md: sd, dx, dy, dz, sp });
x += mx; x += mx;
y += my; y += my;
z += mz; z += mz;
a += ma;
} }
moveDist = 0; // console.log({ dist: md, steps: st, speed: next.speed, moves });
moves.push({ ...next.point, md: sd });
// push last point
moves.push({ ...next.point, md: sd, sp });
// console.log(...moves);
renderMoves(id, moves, send); renderMoves(id, moves, send);
} else { } else {
last = next; last = next;
@ -197,7 +202,7 @@ function renderMoves(id, moves, send, seed = 0) {
if (!pos) { if (!pos) {
throw `no pos @ ${index} of ${moves.length}`; throw `no pos @ ${index} of ${moves.length}`;
} }
const { dx, dy, dz } = pos; const { dx, dy, dz, sp, md } = pos;
toolMove(pos); toolMove(pos);
// console.log('renderMoves', {id, moves, seed}); // console.log('renderMoves', {id, moves, seed});
let subs = 0; let subs = 0;
@ -208,15 +213,14 @@ function renderMoves(id, moves, send, seed = 0) {
subs++; subs++;
} }
} }
// console.log({ index, subs });
renderDist += pos.md;
// pause renderer at specified offsets // pause renderer at specified offsets
if (renderSpeed && renderDist >= renderSpeed) { if (renderSpeed) {
renderDist = 0; let time = sp ? (md / sp) * 60000 : 10;
// console.log({ time, ...pos });
renderUpdate(send); renderUpdate(send);
setTimeout(() => { setTimeout(() => {
renderMoves(id, moves, send, index + 1); renderMoves(id, moves, send, index + 1);
}, renderPause); }, time / renderSpeed);
return; return;
} }
} }

View file

@ -10,24 +10,26 @@ const { client } = api;
let meshes = {}, let meshes = {},
button = {}, button = {},
label = {}, label = {},
lineTracker,
material, material,
speedPauses = [0, 0, 0, 0, 0],
speedValues = [1, 2, 4, 8, 32], speedValues = [1, 2, 4, 8, 32],
speedNames = ["1x", "2x", "4x", "8x", "!!"], speedNames = ["1x", "2x", "4x", "8x", "!!"],
speedMax = speedValues.length - 1, speedMax = speedValues.length - 1,
speedIndex = 0, speedIndex = 0,
speed, speed,
origin, origin,
color = 0; color = 0,
A2R = Math.PI / 180;
export function animate_clear2(api) { export function animate_clear2(api) {
let { anim } = api.ui; let { anim } = api.ui;
client.animate_cleanup2(); lineTracker?.clear();
Object.keys(meshes).forEach(id => deleteMesh(id)); Object.keys(meshes).forEach(id => deleteMesh(id));
api.widgets.setAxisIndex(0); api.widgets.setAxisIndex(0);
api.uc.setVisible(anim.laba, true); api.uc.setVisible(anim.laba, true);
api.uc.setVisible(anim.vala, true); api.uc.setVisible(anim.vala, true);
anim.vala.value = "0.0"; anim.vala.value = "0.0";
client.animate_cleanup2();
} }
export function animate2(api, delay) { export function animate2(api, delay) {
@ -86,8 +88,9 @@ Object.assign(client, {
}, },
animate_setup2(settings, ondone) { animate_setup2(settings, ondone) {
initPathMesh();
color = settings.controller.dark ? 0x888888 : 0; color = settings.controller.dark ? 0x888888 : 0;
material = new THREE.MeshPhongMaterial({ material = material ?? new THREE.MeshPhongMaterial({
flatShading: true, flatShading: true,
transparent: false, transparent: false,
opacity: 0.5, 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) { function meshAdd(id, ind, pos, ilen, plen) {
const geo = new THREE.BufferGeometry(); const geo = new THREE.BufferGeometry();
const pa = plen ? pos.subarray(0, plen * 3) : pos; const pa = plen ? pos.subarray(0, plen * 3) : pos;
@ -132,6 +195,7 @@ function meshUpdate(id, ind, pos, ilen, plen) {
function deleteMesh(id) { function deleteMesh(id) {
space.world.remove(meshes[id]); space.world.remove(meshes[id]);
meshes[id].geometry.dispose();
delete meshes[id]; delete meshes[id];
} }
@ -165,8 +229,7 @@ function play(opts) {
} }
client.animate2({ client.animate2({
speed, speed,
steps: steps || Infinity, steps: steps || Infinity
pause: speedPauses[speedIndex]
}, handleUpdate); }, handleUpdate);
} }
@ -177,8 +240,7 @@ function fast(opts) {
button.pause.style.display = ''; button.pause.style.display = '';
client.animate2({ client.animate2({
speed, speed,
steps: steps || Infinity, steps: steps || Infinity
pause: speedPauses[speedIndex]
}, handleUpdate); }, handleUpdate);
} }
@ -216,6 +278,7 @@ function handleUpdate(data) {
if (data.stock_index !== undefined) { if (data.stock_index !== undefined) {
api.widgets.setAxisIndex(data.stock_index); api.widgets.setAxisIndex(data.stock_index);
label.a.value = -data.stock_index.toFixed(1); label.a.value = -data.stock_index.toFixed(1);
lineTracker.rotate(-data.stock_index);
} }
if (data.mesh_index) { if (data.mesh_index) {
const { id, index } = 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; const { id, ind, pos, ilen, plen } = data.mesh_update;
meshUpdate(id, ind, pos, ilen, plen); meshUpdate(id, ind, pos, ilen, plen);
} }
if (data && data.progress) { if (data.progress) {
label.progress.value = (data.progress * 100).toFixed(1); label.progress.value = (data.progress * 100).toFixed(1);
} }
if (data.line) {
lineTracker.add(...data.line);
}
} }
function updateSpeed(inc = 0) { function updateSpeed(inc = 0) {

View file

@ -29,6 +29,7 @@ const { widgets: WIDGETS } = api;
class Client { class Client {
animVer = 0; animVer = 0;
camStock; camStock;
showStock = true;
current; current;
currentIndex; currentIndex;
flipping; flipping;
@ -640,7 +641,8 @@ export function init() {
}); });
api.event.on("cam.stock.toggle", (bool) => { 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); api.event.on("boolean.click", api.platform.update_bounds);

View file

@ -358,7 +358,6 @@ export function cam_export(print, online) {
// } // }
append(nl.join('')); append(nl.join(''));
points++; points++;
if (dz < 0) console.log(nl);
} }
// look for SCALE header directive // look for SCALE header directive

View file

@ -1378,7 +1378,7 @@ details[open] summary::after {
padding: 0 0 0 0; padding: 0 0 0 0;
} }
#oplist { #oplist {
max-height: 70vh; max-height: calc(70vh - 200px);
min-height: 0; min-height: 0;
overflow-y: auto; overflow-y: auto;
padding-top: 1px; padding-top: 1px;