Implement AXIS parity UI and execution baseline
This commit is contained in:
79
web/react/src/ThreeMachineView.tsx
Normal file
79
web/react/src/ThreeMachineView.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import * as THREE from "three";
|
||||
|
||||
type ViewMode = "z" | "z2" | "x" | "y" | "y2" | "p";
|
||||
type Props = { axes: number[]; view?: ViewMode; zoom?: number; rotateMode?: boolean };
|
||||
|
||||
export function ThreeMachineView({ axes, view = "p", zoom = 1, rotateMode = false }: Props) {
|
||||
const host = useRef<HTMLDivElement>(null);
|
||||
const axesRef = useRef(axes);
|
||||
const viewRef = useRef(view);
|
||||
const zoomRef = useRef(zoom);
|
||||
const rotateRef = useRef(rotateMode);
|
||||
axesRef.current = axes;
|
||||
viewRef.current = view;
|
||||
zoomRef.current = zoom;
|
||||
rotateRef.current = rotateMode;
|
||||
|
||||
useEffect(() => {
|
||||
const element = host.current;
|
||||
if (!element) return;
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x080e13);
|
||||
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
|
||||
camera.position.set(6, 5, 7);
|
||||
camera.lookAt(0, 1, 0);
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
|
||||
renderer.setPixelRatio(Math.min(globalThis.devicePixelRatio || 1, 2));
|
||||
element.appendChild(renderer.domElement);
|
||||
|
||||
const grid = new THREE.GridHelper(8, 16, 0x405765, 0x1e303b);
|
||||
scene.add(grid);
|
||||
scene.add(new THREE.AxesHelper(2.2));
|
||||
const table = new THREE.Mesh(new THREE.BoxGeometry(5.2, 0.18, 3.6), new THREE.MeshStandardMaterial({ color: 0x263740, roughness: 0.8 }));
|
||||
table.position.y = -0.1;
|
||||
scene.add(table);
|
||||
const gantry = new THREE.Mesh(new THREE.BoxGeometry(0.18, 2.2, 0.18), new THREE.MeshStandardMaterial({ color: 0x647c88 }));
|
||||
gantry.position.set(0, 1, 0);
|
||||
scene.add(gantry);
|
||||
const tool = new THREE.Mesh(new THREE.ConeGeometry(0.12, 0.55, 16), new THREE.MeshStandardMaterial({ color: 0xc9e86d, emissive: 0x243200 }));
|
||||
scene.add(tool);
|
||||
const ambient = new THREE.HemisphereLight(0xc8e4ec, 0x111820, 1.8);
|
||||
scene.add(ambient);
|
||||
const key = new THREE.DirectionalLight(0xffffff, 2.2);
|
||||
key.position.set(3, 6, 4);
|
||||
scene.add(key);
|
||||
|
||||
const resize = () => {
|
||||
const width = Math.max(element.clientWidth, 320);
|
||||
const height = Math.max(element.clientHeight, 280);
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(width, height, false);
|
||||
};
|
||||
const observer = new ResizeObserver(resize);
|
||||
observer.observe(element);
|
||||
resize();
|
||||
let frame = 0;
|
||||
const render = () => {
|
||||
const [x = 0, y = 0, z = 0] = axesRef.current;
|
||||
tool.position.set(THREE.MathUtils.clamp(Number(x) || 0, -2.3, 2.3), 0.35 + THREE.MathUtils.clamp(Number(z) || 0, -1, 2), THREE.MathUtils.clamp(Number(y) || 0, -1.4, 1.4));
|
||||
const distance = 10 / Math.max(0.5, zoomRef.current);
|
||||
const selectedView = viewRef.current;
|
||||
if (selectedView === "z") camera.position.set(0, distance, 0.01);
|
||||
else if (selectedView === "z2") camera.position.set(distance * 0.3, distance * 0.9, distance * 0.3);
|
||||
else if (selectedView === "x") camera.position.set(distance, 2, 0.01);
|
||||
else if (selectedView === "y") camera.position.set(0.01, 2, distance);
|
||||
else if (selectedView === "y2") camera.position.set(0.01, 2, -distance);
|
||||
else camera.position.set(6 / Math.max(0.5, zoomRef.current), 5 / Math.max(0.5, zoomRef.current), 7 / Math.max(0.5, zoomRef.current));
|
||||
camera.lookAt(0, 1, 0);
|
||||
scene.rotation.y = rotateRef.current ? (performance.now() / 5000) % (Math.PI * 2) : 0;
|
||||
renderer.render(scene, camera);
|
||||
frame = requestAnimationFrame(render);
|
||||
};
|
||||
render();
|
||||
return () => { cancelAnimationFrame(frame); observer.disconnect(); renderer.dispose(); element.replaceChildren(); };
|
||||
}, []);
|
||||
|
||||
return <div className="three-viewer" ref={host} aria-label="Three.js LinuxCNC machine visualization"><span className="three-viewer-label">Three.js / authoritative pose</span></div>;
|
||||
}
|
||||
Reference in New Issue
Block a user