Apply hierarchy node transforms relative to parents
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import * as THREE from "../web/react/node_modules/three/build/three.module.js";
|
||||
import { GLTFExporter } from "../web/react/node_modules/three/examples/jsm/exporters/GLTFExporter.js";
|
||||
|
||||
@@ -43,16 +43,34 @@ try {
|
||||
await page.locator(".machine-manager-dialog").screenshot({ path: new URL("manager-ac-config.png", output).pathname });
|
||||
report.checks.push("origin visibility, numeric translation/rotation and TransformControls mode update");
|
||||
|
||||
// Editing an imported mesh must preserve the enclosing GLB placement.
|
||||
const meshNode = control("machine-tree-node").filter({ hasText: "mesh_0" }).first();
|
||||
const meshPath = await meshNode.locator("xpath=ancestor::li[1]").getAttribute("data-machine-tree-path");
|
||||
await meshNode.dispatchEvent("click");
|
||||
await control("machine-manager-translation").getByLabel("X", { exact: true }).fill("35");
|
||||
await control("machine-manager-rotation").getByLabel("Z", { exact: true }).fill("30");
|
||||
const baseRoot = page.locator('[data-machine-tree-path="scene"]').first();
|
||||
await baseRoot.locator('[data-axis-control="machine-tree-node"]').first().dispatchEvent("click");
|
||||
assert.equal(await translation.getByLabel("X", { exact: true }).inputValue(), "10");
|
||||
assert.equal(await rotation.getByLabel("Z", { exact: true }).inputValue(), "15");
|
||||
|
||||
const calibrations = page.locator(".machine-manager-calibration fieldset.machine-vector");
|
||||
const cOrigin = calibrations.filter({ hasText: "C 轴原点" }); await cOrigin.getByLabel("X", { exact: true }).fill("24"); await cOrigin.getByLabel("Y", { exact: true }).fill("-12"); await cOrigin.getByLabel("Z", { exact: true }).fill("8");
|
||||
const offset = calibrations.filter({ hasText: "A 到 C 轴偏置" }); await offset.getByLabel("X", { exact: true }).fill("0"); await offset.getByLabel("Y", { exact: true }).fill("18"); await offset.getByLabel("Z", { exact: true }).fill("-45");
|
||||
const zeros = page.locator("fieldset.machine-zero"); await zeros.getByLabel("X", { exact: true }).fill("5"); await zeros.getByLabel("A", { exact: true }).fill("10"); await zeros.getByLabel("C", { exact: true }).fill("20");
|
||||
assert.equal(await control("machine-manager-export").isDisabled(), false); assert.equal(await page.locator("[data-machine-manager-preview]").getAttribute("data-machine-manager-coordinate-system"), "machine-z-up"); assert.equal(await page.locator("[data-machine-manager-preview]").getAttribute("data-machine-manager-axis-map"), "X:x,Y:y,Z:z"); assert.equal(await page.locator("[data-machine-manager-preview]").getAttribute("data-machine-manager-camera-up"), "0,0,1"); const downloadPromise = page.waitForEvent("download"); await control("machine-manager-export").click(); const download = await downloadPromise; assert.equal(download.suggestedFilename(), "managed-xyzac.glb");
|
||||
const exportedBytes = await readFile(await download.path());
|
||||
const exported = JSON.parse(exportedBytes.toString("utf8", 20, 20 + exportedBytes.readUInt32LE(12)));
|
||||
const exportedMesh = exported.nodes.find((node) => node.name === "mesh_0");
|
||||
assert.ok(Math.abs(exportedMesh.translation[0] - .035) < 1e-7);
|
||||
assert.ok(Math.abs(exportedMesh.rotation[2] - Math.sin(Math.PI / 12)) < 1e-7);
|
||||
report.checks.push("internal mesh parent-relative position and rotation survive GLB export without moving its parent");
|
||||
report.checks.push("validated assembly exports a self-contained managed GLB");
|
||||
assert.equal(await control("machine-manager-apply").isDisabled(), false); await control("machine-manager-apply").click();
|
||||
await page.waitForFunction(() => document.querySelector(".xyzac-machine-view")?.dataset.machineManagerSchema === "linuxcnc.machine-manager/1" && document.querySelector("main.axis-app")?.dataset.axisProgramLoading === "false", null, { timeout: 45000 });
|
||||
const geometry = JSON.parse(await page.locator(".xyzac-machine-view").getAttribute("data-machine-manager-geometry"));
|
||||
assert.deepEqual(geometry.cOriginMm, [24, -12, 8]); assert.deepEqual(geometry.tiltOffsetMm, [0, 18, -45]); assert.equal(geometry.physicalZero.x, 5); assert.equal(geometry.physicalZero.a, 10); assert.equal(geometry.physicalZero.c, 20); assert.equal(geometry.parts.length, 8); assert.equal(geometry.parts[0].translationMm[0], 10); assert.equal(geometry.parts[0].rotationDeg[2], 15);
|
||||
assert.deepEqual(geometry.parts[0].nodeTransforms[meshPath], { translationMm: [35, 0, 0], rotationDeg: [0, 0, 30] });
|
||||
assert.match(await page.locator(".xyzac-machine-view").getAttribute("data-xyzac-model-source-path"), /^managed-xyzac\.glb$/);
|
||||
report.checks.push("validated hierarchy applied through LOAD_WORKSPACE with geometry and physical zero in userData");
|
||||
|
||||
@@ -77,7 +95,7 @@ try {
|
||||
const projection = await page.evaluate(async () => {
|
||||
const manager = await import("/src/machine-manager.ts"); const importedMachine = await import("/src/imported-machine.ts");
|
||||
const sources = await Promise.all(Array.from({ length: 8 }, async (_, index) => manager.loadMachinePart(await (await fetch("/machines/demo-ac-xyz-head.glb")).arrayBuffer(), `source-${index}.glb`)));
|
||||
const roles = manager.MACHINE_PART_ROLES; const drafts = sources.map((source, index) => { const role = roles[index]; const parentRole = manager.expectedParentRole(role); return { ...source, role, parentId: parentRole ? sources[roles.indexOf(parentRole)].id : null, visible: true, showOrigin: true, originMm: [0, 0, 0], translationMm: [0, 0, 0], rotationDeg: [0, 0, 0] }; });
|
||||
const roles = manager.MACHINE_PART_ROLES; const drafts = sources.map((source, index) => { const role = roles[index]; const parentRole = manager.expectedParentRole(role); return { ...source, role, parentId: parentRole ? sources[roles.indexOf(parentRole)].id : null, visible: true, showOrigin: true, originMm: [0, 0, 0], translationMm: [0, 0, 0], rotationDeg: [0, 0, 0], nodeParents: {}, nodeRoles: {}, nodeTransforms: {} }; });
|
||||
const run = async (coordinates, cOriginMm, tiltOffsetMm, physicalZero, joints) => {
|
||||
const asset = await manager.buildManagedMachine({ coordinates, parts: drafts, cOriginMm, tiltOffsetMm, physicalZero }); const built = importedMachine.buildImportedMachine(asset);
|
||||
built.update({ joints, offsets: { x_rot_point: cOriginMm[0], y_rot_point: cOriginMm[1], z_rot_point: cOriginMm[2], x_offset: tiltOffsetMm[0], y_offset: tiltOffsetMm[1], z_offset: tiltOffsetMm[2], tool_offset: 0 } });
|
||||
|
||||
Reference in New Issue
Block a user