Implement AXIS parity UI and execution baseline

This commit is contained in:
2026-08-23 07:28:37 +08:00
parent 1325504544
commit 06f96f85e5
463 changed files with 84893 additions and 692 deletions

View 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>;
}