Make machine configuration authoritative and restore native checkpoints exactly

This commit is contained in:
2026-09-18 09:09:41 +08:00
parent 6a69511276
commit 4a568f1f4c
94 changed files with 10882 additions and 301 deletions

View File

@@ -1,4 +1,7 @@
import { useEffect, useRef } from "react";
import { WEB_SETTINGS } from "../../workspace/web-settings.mjs";
const VIEWER = WEB_SETTINGS.viewer;
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";
@@ -11,7 +14,7 @@ type Segment = { id?: number; active?: boolean; motion_type?: number; samples?:
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 Controller = Record<string, () => void>;
type Host = HTMLDivElement & { xyzacMachineController?: Controller };
const toView = (point: Point) => new THREE.Vector3(Number(point.x ?? 0) * .01, Number(point.z ?? 0) * .01, Number(point.y ?? 0) * .01);
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) {
const host = useRef<Host>(null);
@@ -23,7 +26,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
const element = host.current;
if (!element) return;
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false, preserveDrawingBuffer: true });
renderer.setPixelRatio(Math.min(globalThis.devicePixelRatio || 1, 2));
renderer.setPixelRatio(Math.min(globalThis.devicePixelRatio || 1, VIEWER.pixel_ratio_limit));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.shadowMap.enabled = true;
@@ -31,7 +34,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
renderer.domElement.dataset.engine = "three.js r185 / xyzac-trt-vismach";
element.appendChild(renderer.domElement);
const scene = new THREE.Scene(); scene.background = new THREE.Color(0x252b2c);
const scene = new THREE.Scene(); scene.background = new THREE.Color(VIEWER.colors.background);
const studio = new RoomEnvironment();
const pmrem = new THREE.PMREMGenerator(renderer);
const environment = pmrem.fromScene(studio, .04);
@@ -60,11 +63,11 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
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);
const camera = new THREE.PerspectiveCamera(36, 1, .02, 150);
const camera = new THREE.PerspectiveCamera(CAMERA.fov, 1, CAMERA.near, CAMERA.far);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; controls.dampingFactor = .12;
controls.enableDamping = true; controls.dampingFactor = CAMERA.damping;
controls.enableRotate = true;
controls.screenSpacePanning = true; controls.minDistance = .25; controls.maxDistance = 65;
controls.screenSpacePanning = true; controls.minDistance = CAMERA.min_distance; controls.maxDistance = CAMERA.max_distance;
// Keep the standard Three.js mouse contract: left-drag orbits, middle
// drag zooms, right-drag pans, and the wheel zooms. The machine canvas
// must remain directly navigable without first toggling a mode button.
@@ -96,7 +99,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
const point = new THREE.Vector3(x, y, z).sub(modelCenter);
distance = Math.max(distance, point.dot(direction) + Math.max(Math.abs(point.dot(right)) / (tanV * camera.aspect), Math.abs(point.dot(up)) / tanV));
}
controls.target.copy(modelCenter); camera.position.copy(modelCenter).addScaledVector(direction, distance * 1.12 / Math.max(.5, scale));
controls.target.copy(modelCenter); camera.position.copy(modelCenter).addScaledVector(direction, distance * CAMERA.fit_margin / Math.max(.5, scale));
camera.lookAt(controls.target); controls.update();
};
const resize = () => {
@@ -106,7 +109,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
};
const observer = new ResizeObserver(resize); observer.observe(element); resize();
const pan = (x: number, y: number) => {
const amount = camera.position.distanceTo(controls.target) * .035;
const amount = camera.position.distanceTo(controls.target) * CAMERA.pan_fraction;
camera.updateMatrix();
const offset = new THREE.Vector3().setFromMatrixColumn(camera.matrix, 0).multiplyScalar(x * amount)
.addScaledVector(new THREE.Vector3().setFromMatrixColumn(camera.matrix, 1), y * amount);
@@ -118,12 +121,12 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
camera.position.copy(controls.target).add(offset); controls.update();
};
element.xyzacMachineController = {
zoomIn: () => zoomCamera(.8), zoomOut: () => zoomCamera(1.25),
zoomIn: () => zoomCamera(CAMERA.zoom_in), zoomOut: () => zoomCamera(CAMERA.zoom_out),
panLeft: () => pan(-1, 0), panRight: () => pan(1, 0), panUp: () => pan(0, 1), panDown: () => pan(0, -1),
rotateLeft: () => { controls.rotateLeft(Math.PI / 12); controls.update(); },
rotateRight: () => { controls.rotateLeft(-Math.PI / 12); controls.update(); },
rotateUp: () => { controls.rotateUp(Math.PI / 12); controls.update(); },
rotateDown: () => { controls.rotateUp(-Math.PI / 12); controls.update(); },
rotateLeft: () => { controls.rotateLeft(THREE.MathUtils.degToRad(CAMERA.rotate_degrees)); controls.update(); },
rotateRight: () => { controls.rotateLeft(-THREE.MathUtils.degToRad(CAMERA.rotate_degrees)); controls.update(); },
rotateUp: () => { controls.rotateUp(THREE.MathUtils.degToRad(CAMERA.rotate_degrees)); controls.update(); },
rotateDown: () => { controls.rotateUp(-THREE.MathUtils.degToRad(CAMERA.rotate_degrees)); controls.update(); },
reset: () => { lastPreset = ""; },
tool: () => {
const target = assembly.toolTip.getWorldPosition(new THREE.Vector3()).add(new THREE.Vector3(0, .2, 0));
@@ -136,11 +139,11 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
// workpiece path together with the physical table.
const pathGroup = new THREE.Group(); pathGroup.name = "xyzac-toolpath"; assembly.joints.rotate.add(pathGroup);
const liveGroup = new THREE.Group(); liveGroup.name = "xyzac-live-toolpath"; assembly.joints.rotate.add(liveGroup);
const pathMaterials = [0xffff00, 0xff9a55, 0x5c9d9a].map((color) => new THREE.LineBasicMaterial({ color }));
const pathMaterials = [VIEWER.colors.path, VIEWER.colors.live, VIEWER.colors.rapid].map((color) => new THREE.LineBasicMaterial({ color }));
const clearLines = (group: THREE.Group) => { group.children.forEach((child) => (child as THREE.Line).geometry.dispose()); group.clear(); };
const limits = new THREE.Box3Helper(new THREE.Box3(), 0xdd7566); scene.add(limits);
const limits = new THREE.Box3Helper(new THREE.Box3(), VIEWER.colors.limits); scene.add(limits);
const offsets = new THREE.AxesHelper(.8); scene.add(offsets);
const extents = new THREE.Box3Helper(initialBounds, 0xe6c56b); scene.add(extents);
const extents = new THREE.Box3Helper(initialBounds, VIEWER.colors.extents); scene.add(extents);
const render = () => {
const state = visualRef.current, config = settings.current;
assembly.update(state);
@@ -165,7 +168,7 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
}
offsets.visible = config.showOffsets;
const coordinates = state.machineModel?.coordinate_system ?? "XYZAC";
offsets.position.set(coordinates === "XYZBC" ? Number(state.offsets.x_offset ?? 0) * .01 : 0, state.offsets.z_offset * .01, state.offsets.y_offset * .01);
offsets.position.set(coordinates === "XYZBC" ? Number(state.offsets.x_offset ?? 0) * VIEWER.mm_scale : 0, state.offsets.z_offset * VIEWER.mm_scale, state.offsets.y_offset * VIEWER.mm_scale);
extents.visible = config.showExtents;
const preset = `${config.view}:${config.zoom}`;
if (preset !== lastPreset) { presetCamera(); lastPreset = preset; }