Add AC/BC RTCP programs and 3D machine manager

This commit is contained in:
2026-09-20 12:31:12 +08:00
parent 1c1e5f9672
commit 8aab531b02
363 changed files with 53213 additions and 58 deletions

View File

@@ -5,18 +5,19 @@ const CAMERA = VIEWER.camera;
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
import { buildImportedMachine, type ImportedMachine } from "./imported-machine";
import { axisIcon } from "./sourceAssets";
import { buildXyzacMachine, buildXyzbcMachine } from "./xyzac-machine-model";
import { projectXyzacVisual, type XyzacVisualSource, type XyzacVisualState } from "./xyzac-visual-adapter";
type Point = Record<string, number>;
type Segment = { id?: number; active?: boolean; motion_type?: number; samples?: Point[]; start?: Point; end?: Point };
type Props = { visual: XyzacVisualState; toolpath?: Segment[]; view?: "z" | "z2" | "x" | "y" | "y2" | "p"; zoom?: number; rotateMode?: boolean; showPath?: boolean; showLive?: boolean; showLimits?: boolean; showVelocity?: boolean; showOffsets?: boolean; showExtents?: boolean };
type Props = { importedMachine?: ImportedMachine | null; visual: XyzacVisualState; toolpath?: Segment[]; view?: "z" | "z2" | "x" | "y" | "y2" | "p"; zoom?: number; rotateMode?: boolean; showPath?: boolean; showLive?: boolean; showLimits?: boolean; showVelocity?: boolean; showOffsets?: boolean; showExtents?: boolean };
type Controller = Record<string, () => void>;
type Host = HTMLDivElement & { xyzacMachineController?: Controller };
const toView = (point: Point) => new THREE.Vector3(Number(point.x ?? 0) * VIEWER.mm_scale, Number(point.z ?? 0) * VIEWER.mm_scale, Number(point.y ?? 0) * VIEWER.mm_scale);
export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotateMode = false, showPath = false, showLive = false, showLimits = false, showVelocity = false, showOffsets = false, showExtents = false }: Props) {
export function XyzacMachineView({ importedMachine, visual, toolpath, view = "p", zoom = 1, rotateMode = false, showPath = false, showLive = false, showLimits = false, showVelocity = false, showOffsets = false, showExtents = false }: Props) {
const host = useRef<Host>(null);
const visualRef = useRef(visual); visualRef.current = visual;
const settings = useRef({ toolpath, view, zoom, rotateMode, showPath, showLive, showLimits, showOffsets, showExtents });
@@ -30,7 +31,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.type = THREE.PCFShadowMap;
renderer.domElement.dataset.engine = "three.js r185 / xyzac-trt-vismach";
element.appendChild(renderer.domElement);
@@ -50,7 +51,9 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
floor.rotation.x = -Math.PI / 2; floor.position.y = -2.64; floor.receiveShadow = true; scene.add(floor);
const coordinates = visualRef.current.machineModel?.coordinate_system ?? "XYZAC";
const assembly = coordinates === "XYZBC" ? buildXyzbcMachine() : buildXyzacMachine(); scene.add(assembly.machine);
const assembly = importedMachine ? buildImportedMachine(importedMachine) : coordinates === "XYZBC" ? buildXyzbcMachine() : buildXyzacMachine();
const pathPoint = importedMachine ? (point: Point) => new THREE.Vector3(Number(point.x ?? 0), Number(point.y ?? 0), Number(point.z ?? 0)).multiplyScalar(.001) : toView;
scene.add(assembly.machine);
assembly.update(visualRef.current);
const nameplateCanvas = document.createElement("canvas"); nameplateCanvas.width = 512; nameplateCanvas.height = 128;
const label = nameplateCanvas.getContext("2d");
@@ -61,7 +64,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
}
const nameplateTexture = new THREE.CanvasTexture(nameplateCanvas); nameplateTexture.colorSpace = THREE.SRGBColorSpace;
const nameplate = new THREE.Mesh(new THREE.PlaneGeometry(.84, .21), new THREE.MeshBasicMaterial({ map: nameplateTexture }));
nameplate.position.set(-.57, -1.48, -1.046); nameplate.rotation.y = Math.PI; assembly.machine.add(nameplate);
nameplate.position.set(-.57, -1.48, -1.046); nameplate.rotation.y = Math.PI; if (!importedMachine) assembly.machine.add(nameplate);
const camera = new THREE.PerspectiveCamera(CAMERA.fov, 1, CAMERA.near, CAMERA.far);
const controls = new OrbitControls(camera, renderer.domElement);
@@ -78,6 +81,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
controls.touches.TWO = THREE.TOUCH.DOLLY_PAN;
let frame = 0, lastPreset = "", lastGeometry: Segment[] | null = null, lastPathKey = "";
const initialBounds = new THREE.Box3().setFromObject(assembly.machine);
if (importedMachine) floor.position.y = initialBounds.min.y - .03;
const modelCenter = initialBounds.getCenter(new THREE.Vector3());
const presetCamera = () => {
const { view: selected, zoom: scale } = settings.current;
@@ -156,7 +160,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
if (config.showPath || config.showLive) for (const raw of paths) {
const live = raw.active || raw.id === state.activeSegmentId;
if (live ? !config.showLive : !config.showPath) continue;
const points = raw.samples?.length ? raw.samples.map(toView) : [toView(raw.start ?? {}), toView(raw.end ?? {})];
const points = raw.samples?.length ? raw.samples.map(pathPoint) : [pathPoint(raw.start ?? {}), pathPoint(raw.end ?? {})];
(live ? liveGroup : pathGroup).add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(points), pathMaterials[live ? 1 : raw.motion_type === 0 ? 2 : 0]));
}
lastGeometry = paths; lastPathKey = pathKey;
@@ -188,12 +192,13 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
floor.geometry.dispose(); floor.material.dispose(); key.shadow.map?.dispose(); environment.dispose();
renderer.dispose(); renderer.domElement.remove();
};
}, []);
}, [importedMachine]);
const model = visual.machineModel;
const coordinates = model?.coordinate_system ?? "XYZAC";
const managerGeometry = importedMachine?.scene.userData.linuxcncMachineGeometry;
const controllerButton = (action: string, label: string, asset?: string, glyph?: string) => <button type="button" data-axis-control={`machine-${action}`} data-xyzac-machine-controller={action} title={label} aria-label={label} onClick={() => host.current?.xyzacMachineController?.[action]?.()}>{asset ? <img src={axisIcon(asset)} alt="" /> : glyph}</button>;
return <div className="three-viewer xyzac-machine-view" ref={host} data-xyzac-preview="machine" data-xyzac-model-source="linuxcnc-wasm" data-xyzac-mouse-controls="left-rotate-middle-zoom-right-pan-wheel-zoom" data-xyzac-model-id={model?.id ?? ""} data-xyzac-model-source-path={model?.source_path ?? ""} data-xyzac-model-source-hash={model?.source_hash ?? ""} data-xyzac-authoritative={String(visual.authoritative)} data-xyzac-tick={visual.tick} data-xyzac-state-hash={visual.stateHash} data-xyzac-active-segment={visual.activeSegmentId ?? ""} data-xyzac-joints={JSON.stringify(visual.joints)} data-xyzac-offsets={JSON.stringify(visual.offsets)} aria-label={`${coordinates} TRT Vismach machine model`}>
return <div className="three-viewer xyzac-machine-view" ref={host} data-xyzac-preview="machine" data-machine-glb={importedMachine?.hash ?? ""} data-machine-manager-schema={managerGeometry?.schema ?? ""} data-machine-manager-geometry={managerGeometry ? JSON.stringify(managerGeometry) : ""} data-xyzac-model-source={importedMachine ? "glb-urdf/linuxcnc-wasm" : "linuxcnc-wasm"} data-xyzac-mouse-controls="left-rotate-middle-zoom-right-pan-wheel-zoom" data-xyzac-model-id={model?.id ?? ""} data-xyzac-model-source-path={importedMachine?.filename ?? model?.source_path ?? ""} data-xyzac-model-source-hash={importedMachine?.hash ?? model?.source_hash ?? ""} data-xyzac-authoritative={String(visual.authoritative)} data-xyzac-tick={visual.tick} data-xyzac-state-hash={visual.stateHash} data-xyzac-active-segment={visual.activeSegmentId ?? ""} data-xyzac-joints={JSON.stringify(visual.joints)} data-xyzac-offsets={JSON.stringify(visual.offsets)} aria-label={`${coordinates} ${importedMachine ? "URDF" : "TRT Vismach"} machine model`}>
<div className="xyzac-view-controller xyzac-machine-controller" role="toolbar" aria-label="Machine 3D view controller">
{controllerButton("zoomIn", "Zoom in", "tool_zoomin.gif")}{controllerButton("zoomOut", "Zoom out", "tool_zoomout.gif")}
{controllerButton("panLeft", "Pan left", undefined, "\u2190")}{controllerButton("panRight", "Pan right", undefined, "\u2192")}