Use independent XYZAC and XYZBC boat programs and models
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@ import * as THREE from "three";
|
||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
|
||||
import { axisIcon } from "./sourceAssets";
|
||||
import { buildXyzacMachine } from "./xyzac-machine-model";
|
||||
import { buildXyzacMachine, buildXyzbcMachine } from "./xyzac-machine-model";
|
||||
import { projectXyzacVisual, type XyzacVisualSource, type XyzacVisualState } from "./xyzac-visual-adapter";
|
||||
|
||||
type Point = Record<string, number>;
|
||||
@@ -47,7 +47,7 @@ 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 = buildXyzacMachine(coordinates); scene.add(assembly.machine);
|
||||
const assembly = coordinates === "XYZBC" ? buildXyzbcMachine() : buildXyzacMachine(); scene.add(assembly.machine);
|
||||
assembly.update(visualRef.current);
|
||||
const nameplateCanvas = document.createElement("canvas"); nameplateCanvas.width = 512; nameplateCanvas.height = 128;
|
||||
const label = nameplateCanvas.getContext("2d");
|
||||
@@ -199,5 +199,5 @@ export function XyzacMachineView({ visual, toolpath, view = "p", zoom = 1, rotat
|
||||
|
||||
export function XyzacMachineViewFromSnapshot({ snapshot, ...props }: Omit<Props, "visual"> & { snapshot: XyzacVisualSource | null | undefined }) {
|
||||
const visual = projectXyzacVisual(snapshot);
|
||||
return <XyzacMachineView key={visual.machineModel?.id ?? "machine-model-pending"} visual={visual} {...props} />;
|
||||
return <XyzacMachineView key={`${visual.machineModel?.id ?? "machine-model-pending"}:${visual.machineModel?.source_hash ?? ""}`} visual={visual} {...props} />;
|
||||
}
|
||||
|
||||
2
web/react/src/legacy.d.ts
vendored
2
web/react/src/legacy.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
declare module "*.mjs" {
|
||||
export type MachineDefinition = { id: string; label: string; coordinates: string; axes: readonly string[]; joint_count: number; entrypoint: string; program: string; kinematics_type: number; runtime: string; model: string; source_path: string; offsets: Record<string, number>; axis_limits: Record<string, number[]> };
|
||||
export type MachineDefinition = { id: string; label: string; coordinates: string; axes: readonly string[]; joint_count: number; entrypoint: string; program: string; switchkins_program: string; kinematics_type: number; runtime: string; model: string; source_path: string; offsets: Record<string, number>; joint_limits: Record<string, number[]>; axis_limits: Record<string, number[]> };
|
||||
export const MACHINE_DEFINITIONS: readonly MachineDefinition[];
|
||||
export const DEFAULT_MACHINE: MachineDefinition;
|
||||
export function machineDefinition(value?: string): MachineDefinition | null;
|
||||
|
||||
@@ -179,6 +179,9 @@ export type XyzacMachineModel = {
|
||||
source_path: string;
|
||||
source_hash: string | null;
|
||||
coordinate_system: "XYZAC" | "XYZBC";
|
||||
geometry_variant?: "moving-knee-x-tilt" | "moving-head-y-tilt";
|
||||
tilt_axis?: "X" | "Y";
|
||||
rotary_axis?: "Z";
|
||||
joints: string[];
|
||||
components: string[];
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { projectXyzacVisual, XYZAC_MACHINE_GROUPS } from "./xyzac-visual-adapter.ts";
|
||||
import * as THREE from "three";
|
||||
import { buildXyzacMachine } from "./xyzac-machine-model.ts";
|
||||
import { buildXyzacMachine, buildXyzbcMachine } from "./xyzac-machine-model.ts";
|
||||
|
||||
const source = await readFile(new URL("./XyzacMachineView.tsx", import.meta.url), "utf8") + await readFile(new URL("./xyzac-machine-model.ts", import.meta.url), "utf8");
|
||||
const panelSource = await readFile(new URL("./XyzacTrtPanel.tsx", import.meta.url), "utf8");
|
||||
@@ -52,10 +52,18 @@ for (const token of ["J0", "J1", "J2", "J3", "J4"]) {
|
||||
assert.ok(toolSource.includes(token), `tool preview ${token}`);
|
||||
assert.ok(source.includes("J${index}"), "machine preview joint label template");
|
||||
}
|
||||
const close = (actual, expected) => assert.ok(Math.abs(actual - expected) < 1e-7, `${actual} != ${expected}`);
|
||||
const model = buildXyzacMachine();
|
||||
const xyzbcModel = buildXyzbcMachine();
|
||||
assert.equal(xyzbcModel.machine.name, "xyzbc-machine");
|
||||
assert.ok(xyzbcModel.machine.getObjectByName("tilt-b"));
|
||||
assert.equal(xyzbcModel.machine.getObjectByName("tilt-a"), undefined);
|
||||
xyzbcModel.update({ joints: [0, 0, 0, 30, 45], offsets: { x_rot_point: 0, y_rot_point: 0, z_rot_point: 0, x_offset: -20, y_offset: 0, z_offset: -15, tool_offset: 0 } });
|
||||
close(xyzbcModel.joints.tilt.rotation.z, -30 * Math.PI / 180);
|
||||
assert.equal(xyzbcModel.joints.tilt.rotation.y, Math.PI / 2);
|
||||
xyzbcModel.dispose();
|
||||
const original = structuredClone(visual);
|
||||
const zero = { joints: [0, 0, 0, 0, 0], offsets: { x_rot_point: 0, y_offset: 20, z_offset: 10, tool_offset: 0 } };
|
||||
const close = (actual, expected) => assert.ok(Math.abs(actual - expected) < 1e-7, `${actual} != ${expected}`);
|
||||
const position = (node) => node.getWorldPosition(new THREE.Vector3());
|
||||
model.update(zero);
|
||||
close(position(model.toolTip).y, 2);
|
||||
|
||||
@@ -6,9 +6,10 @@ const MM = .01;
|
||||
const DEG = Math.PI / 180;
|
||||
|
||||
// Source coordinates remain millimetres, Z up. The existing viewer uses Y up.
|
||||
export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
// XYZAC retains the established tilt-a group identity; XYZBC uses tilt-b.
|
||||
const rotaryAxis = coordinates[3]?.toLowerCase() ?? "a";
|
||||
function buildTrtMachine(coordinates = "XYZAC") {
|
||||
const isXyzbc = coordinates === "XYZBC";
|
||||
// Separate source identities: XYZAC uses tilt-a, XYZBC uses tilt-b.
|
||||
const rotaryAxis = isXyzbc ? "b" : "a";
|
||||
const geometries = new Map<string, THREE.BufferGeometry>();
|
||||
const materials = {
|
||||
enamel: new THREE.MeshStandardMaterial({ color: 0xd9e1e1, roughness: .32, metalness: .25 }),
|
||||
@@ -58,7 +59,7 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
cylinder(machine, "levelling-pad", x, y, -259, 17, 9, "rubber");
|
||||
cylinder(machine, "levelling-stud", x, y, -249, 8, 15, "steel");
|
||||
}
|
||||
box(machine, "base", 0, 30, -178, 240, 260, 146, "enamel", 9);
|
||||
box(machine, "base", 0, 30, isXyzbc ? -115 : -178, 240, 260, isXyzbc ? 170 : 146, "enamel", 9);
|
||||
box(machine, "base-plinth", 0, 30, -239, 242, 262, 20, "dark", 4);
|
||||
box(machine, "base-front-panel", 0, -101, -177, 205, 4, 96, "teal", 4);
|
||||
box(machine, "service-door", 68, -104, -177, 48, 2, 71, "dark", 2);
|
||||
@@ -69,7 +70,7 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
for (const x of [-123, 123]) box(machine, "tray-side-lip", x, 18, -93, 5, 244, 16, "dark");
|
||||
box(machine, "tray-front-lip", 0, -102, -93, 246, 5, 16, "dark");
|
||||
|
||||
box(machine, "column", 0, 150, 5, 100, 100, 510, "enamel", 5);
|
||||
box(machine, "column", 0, isXyzbc ? 170 : 150, isXyzbc ? 80 : 5, 100, isXyzbc ? 100 : 100, isXyzbc ? 560 : 510, "enamel", 5);
|
||||
for (const x of [-51, 51]) {
|
||||
box(machine, "column-side-insert", x, 155, 20, 3, 62, 413, "teal");
|
||||
box(machine, "column-rib", x, 190, -120, 10, 16, 244, "dark", 2);
|
||||
@@ -83,21 +84,24 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
box(machine, "z-servo", 0, -105, -169, 49, 21, 50, "dark", 3);
|
||||
box(machine, "z-servo-cover", 0, -117, -169, 40, 3, 38, "teal", 2);
|
||||
|
||||
const head = group(machine, "head", 0, 0, 200);
|
||||
// Native head spans Y=-30..240; this bridge physically connects the spindle to the column.
|
||||
box(head, "head-body", 0, 105, 97.5, 70, 270, 75, "enamel", 5);
|
||||
box(head, "head-top-cover", 0, 107, 137, 72, 269, 9, "teal", 3);
|
||||
const head = group(machine, "head", 0, 0, isXyzbc ? 150 : 200);
|
||||
// XYZAC has the long moving knee bridge; XYZBC has the shorter moving
|
||||
// spindle head from xyzbc-trt-gui.py.
|
||||
const headDepth = isXyzbc ? 130 : 270;
|
||||
box(head, "head-body", 0, isXyzbc ? 50 : 105, 97.5, 70, headDepth, 75, "enamel", 5);
|
||||
box(head, "head-top-cover", 0, isXyzbc ? 52 : 107, 137, 72, headDepth - 1, 9, "teal", 3);
|
||||
box(head, "spindle-front-panel", 0, -31, 96, 57, 3, 54, "teal", 2);
|
||||
for (const x of [-23, 23]) for (const z of [75, 119]) bolt(head, x, -34, z, "y");
|
||||
for (let i = 0; i < 6; i++) box(head, "head-cooling-vent", 36, 70 + i * 11, 103, 1, 6, 29, "recess", .2);
|
||||
cylinder(head, "motor", 0, 200, 168, 29, 61, "dark");
|
||||
cylinder(head, "motor-cap", 0, 200, 200, 30, 7, "teal");
|
||||
cylinder(head, "motor", 0, isXyzbc ? 60 : 200, 168, 29, 61, "dark");
|
||||
cylinder(head, "motor-cap", 0, isXyzbc ? 60 : 200, 200, 30, 7, "teal");
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const angle = i * Math.PI / 8;
|
||||
const fin = box(head, "motor-cooling-fin", 30 * Math.cos(angle), 200 + 30 * Math.sin(angle), 170, 3, 4, 48, "steel", .5);
|
||||
const motorY = isXyzbc ? 60 : 200;
|
||||
const fin = box(head, "motor-cooling-fin", 30 * Math.cos(angle), motorY + 30 * Math.sin(angle), 170, 3, 4, 48, "steel", .5);
|
||||
fin.rotation.y = -angle;
|
||||
}
|
||||
box(head, "motor-terminal-box", 32, 200, 168, 16, 32, 24, "dark", 2);
|
||||
box(head, "motor-terminal-box", 32, isXyzbc ? 60 : 200, 168, 16, 32, 24, "dark", 2);
|
||||
|
||||
const spindle = group(head, "spindle", 0, 0, 20);
|
||||
lathe(spindle, "spindle-housing", [[0,20],[20,20],[22,23],[22,38],[20,40],[20,115],[0,115]], "steel");
|
||||
@@ -121,10 +125,12 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
const shank = cylinder(tool, "ground-tool-shank", 0, 0, 8, 3, 1, "polished");
|
||||
|
||||
const zslide = group(machine, "spindle-z");
|
||||
box(zslide, "knee-casting", 0, 10, -142.5, 100, 220, 75, "enamel", 5);
|
||||
box(zslide, "knee-front-cover", 0, -102, -143, 85, 5, 56, "teal", 3);
|
||||
for (const x of [-34, 34]) bolt(zslide, x, -106, -144, "y");
|
||||
for (const x of [-33, 33]) box(zslide, "y-guideway", x, 8, -103, 14, 181, 6, "steel");
|
||||
box(zslide, isXyzbc ? "y-slide" : "knee-casting", 0, 10, -142.5, 100, 220, 75, isXyzbc ? "teal" : "enamel", 5);
|
||||
if (!isXyzbc) {
|
||||
box(zslide, "knee-front-cover", 0, -102, -143, 85, 5, 56, "teal", 3);
|
||||
for (const x of [-34, 34]) bolt(zslide, x, -106, -144, "y");
|
||||
for (const x of [-33, 33]) box(zslide, "y-guideway", x, 8, -103, 14, 181, 6, "steel");
|
||||
}
|
||||
const saddle = group(zslide, "saddle-y");
|
||||
box(saddle, "saddle-body", 0, 0, -89, 150, 106, 32, "dark", 3);
|
||||
for (const y of [-32, 32]) box(saddle, "x-guideway", 0, y, -73, 148, 12, 5, "polished");
|
||||
@@ -147,6 +153,11 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
cylinder(brackets, `${rotaryAxis}-axis-end-cap`, -107, 0, 0, 16, 3, "dark", "x");
|
||||
|
||||
const tilt = group(tableX, `tilt-${rotaryAxis}`);
|
||||
// The XYZBC source model applies a fixed 90 degree Z rotation before the
|
||||
// B (source-Y) tilt. In the Three.js source-coordinate mapping that is a
|
||||
// fixed Y rotation followed by a Z-axis dynamic tilt.
|
||||
if (isXyzbc) tilt.rotation.y = Math.PI / 2;
|
||||
if (isXyzbc) brackets.rotation.y = Math.PI / 2;
|
||||
box(tilt, "yoke-plate", 0, 0, -30, 130, 80, 10, "teal", 2);
|
||||
for (const x of [-60, 60]) {
|
||||
box(tilt, "yoke-side-plate", x, 0, -15, 10, 80, 34, "teal", 2);
|
||||
@@ -191,7 +202,7 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
// around machine Y. The Web scene maps source Y to Three.js Z.
|
||||
head.rotation.set(0, 0, 0);
|
||||
zslide.position.y = 0;
|
||||
tilt.rotation.set(0, 0, -tiltAJoint * DEG);
|
||||
tilt.rotation.z = -tiltAJoint * DEG;
|
||||
// xyzbc-trt-gui applies x-offset to both sides of the B table and
|
||||
// applies z-offset with opposite signs to the inner/outer groups.
|
||||
tilt.position.set(xOffset * MM, zOffset * MM, yOffset * MM);
|
||||
@@ -201,6 +212,7 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
zslide.position.y = (200 - spindleZJoint) * MM;
|
||||
head.position.y = 2;
|
||||
head.rotation.set(0, 0, 0);
|
||||
// XYZAC source transform: tilt.rotation.x = -tiltAJoint * DEG.
|
||||
tilt.rotation.x = -tiltAJoint * DEG;
|
||||
tilt.position.set(0, zOffset * MM, yOffset * MM);
|
||||
brackets.position.copy(tilt.position);
|
||||
@@ -224,6 +236,12 @@ export function buildXyzacMachine(coordinates = "XYZAC") {
|
||||
return { machine, update, dispose, toolTip, workOrigin, joints: { tableX, saddle, zslide, tilt, rotate }, tool };
|
||||
}
|
||||
|
||||
// Keep the two machine architectures as explicit entry points. They share
|
||||
// only primitive material/geometry helpers; each entry point selects its own
|
||||
// source model, rotary axis and moving assembly.
|
||||
export function buildXyzacMachine() { return buildTrtMachine("XYZAC"); }
|
||||
export function buildXyzbcMachine() { return buildTrtMachine("XYZBC"); }
|
||||
|
||||
// Four helical reliefs with narrow cutting lands, closed end faces, and a flat cutter tip.
|
||||
function createFlutedCutter() {
|
||||
const radial = 96, axial = 32;
|
||||
|
||||
Reference in New Issue
Block a user