import assert from 'node:assert/strict'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { createHash } from 'node:crypto'; import createModule from '../build-wasm/linuxcnc-sim-core.js'; import { WasmXyzacKinematics } from '../web/worker/wasm-xyzac-kinematics.mjs'; const out = new URL('../docs/teaching/rtcp-five-axis/', import.meta.url); await mkdir(new URL('assets/', out), { recursive: true }); const wasm = await readFile(new URL('../build-wasm/linuxcnc-sim-core.wasm', import.meta.url)); const core = await createModule({ wasmBinary: wasm }); const basePose = { x: 100, y: 0, z: 20, a: 0, b: 0, c: 0, u: 0, v: 0, w: 0 }; const cases = []; const close = (a, b) => assert.ok(Math.abs(a - b) < 1e-9, `${a} != ${b}`); for (const coordinates of ['XYZAC', 'XYZBC']) { const k = new WasmXyzacKinematics(core, { coordinates, limits: Object.fromEntries([...coordinates.toLowerCase()].map((a) => [a, [-100000, 100000]])) }); const samples = []; for (const [tilt, c] of [[0, 0], [0, 45], [0, 90], [30, 0], [30, 45], [30, 90]]) { const p = { ...basePose, c, [coordinates[3].toLowerCase()]: tilt }; const ik = k.inverseMode(1, p); assert.equal(ik.status, 'verified'); k.selectPhysical(1, ik.joints); const fk = k.forward(ik.joints); assert.equal(fk.status, 'verified'); for (const axis of ['x', 'y', 'z']) close(fk.pose[axis], p[axis]); samples.push({ tilt, c, programmed: p, joints: ik.joints, forward: fk.pose }); } close(samples[2].joints[0], 0); close(samples[2].joints[1], 100); close(samples[2].joints[2], 20); const direction = k.inverseMode(1, { ...basePose, c: 45, x: 110 }).joints.map((v, i) => v - samples[1].joints[i]); close(direction[0], 10 / Math.sqrt(2)); close(direction[1], 10 / Math.sqrt(2)); cases.push({ coordinates, samples, xw_increment_10_at_c45: direction }); k.destroy(); } const offsets = { x_rot_point: -18, y_rot_point: 14, z_rot_point: 12, x_offset: 22, y_offset: 0, z_offset: -40, tool_offset: 0 }; const k = new WasmXyzacKinematics(core, { coordinates: 'XYZBC', ...offsets, limits: Object.fromEntries(['x', 'y', 'z', 'b', 'c'].map((a) => [a, [-100000, 100000]])) }); const physical = [0, 0, 44.5858, 4.8675, 179.88]; const tcp = k.selectPhysical(1, physical); const tcpPose = { ...tcp.pose }; const identity = k.selectPhysical(0, physical); physical.forEach((value, i) => { close(tcp.joints[i], value); close(identity.joints[i], value); }); const report = { report_id: 'rtcp-teaching-numerics-v1', status: 'verified', wasm_sha256: createHash('sha256').update(wasm).digest('hex'), convention: 'Native AC: Rx(A) Rz(C); BC: Ry(B) Rz(C). Main teaching examples P=D=tool_offset=0, mm/deg.', cases, switch_example: { offsets, physical, tcp_pose: tcpPose, identity_pose: identity.pose }, chord_midpoint: { compensated: [100 / Math.sqrt(2), 100 / Math.sqrt(2), 20], uncompensated_joint_interpolation: [50, 50, 20], distance: 100 - 50 * Math.sqrt(2) }, }; k.destroy(); await writeFile(new URL('numeric-evidence.json', out), JSON.stringify(report, null, 2) + '\n'); console.log(JSON.stringify({ report_id: report.report_id, status: report.status, poses: 12, fk_ik_roundtrips: 12, source: report.wasm_sha256 }));