接入 LinuxCNC TP 运行反馈
This commit is contained in:
@@ -3,24 +3,43 @@ import * as THREE from "../vendor/three/three.module.js";
|
||||
const scenes = new WeakMap();
|
||||
|
||||
export function renderFiveAxisScene(canvas, state) {
|
||||
const preview = scenes.get(canvas) || createScene(canvas);
|
||||
scenes.set(canvas, preview);
|
||||
let preview = scenes.get(canvas);
|
||||
if (!preview) {
|
||||
preview = createPreview(canvas);
|
||||
scenes.set(canvas, preview);
|
||||
}
|
||||
|
||||
resizeRenderer(preview);
|
||||
updateMachinePose(preview, state);
|
||||
preview.renderer.render(preview.scene, preview.camera);
|
||||
if (preview.kind === "fallback") {
|
||||
renderFallbackPreview(preview, state);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resizeRenderer(preview);
|
||||
updateMachinePose(preview, state);
|
||||
preview.renderer.render(preview.scene, preview.camera);
|
||||
} catch (error) {
|
||||
const fallback = createFallbackPreview(canvas, error);
|
||||
scenes.set(canvas, fallback);
|
||||
renderFallbackPreview(fallback, state);
|
||||
return;
|
||||
}
|
||||
|
||||
const pointCount = preview.pathLine.geometry.getAttribute("position").count;
|
||||
canvas.dataset.threeReady = "true";
|
||||
canvas.dataset.threeRevision = THREE.REVISION;
|
||||
canvas.dataset.threePathPoints = String(pointCount);
|
||||
canvas.dataset.threeSceneObjects = String(countSceneObjects(preview.scene));
|
||||
canvas.dataset.threeToolhead = JSON.stringify(toRoundedVector(preview.toolGroup.position));
|
||||
canvas.dataset.threeToolAxis = JSON.stringify(toRoundedVector(state.toolAxisVector));
|
||||
canvas.dataset.threeTcpPose = JSON.stringify(toRoundedPose(state.tcpPose));
|
||||
canvas.dataset.threeRtcpState = state.rtcpState;
|
||||
canvas.dataset.threeSelectedView = state.preview.selectedView;
|
||||
canvas.dataset.threeFrameApi = state.rtcpFrame.apiName;
|
||||
exposePreviewDataset(canvas, state, {
|
||||
pointCount,
|
||||
sceneObjectCount: countSceneObjects(preview.scene),
|
||||
toolhead: preview.toolGroup.position,
|
||||
renderer: "webgl",
|
||||
});
|
||||
}
|
||||
|
||||
function createPreview(canvas) {
|
||||
try {
|
||||
return createScene(canvas);
|
||||
} catch (error) {
|
||||
return createFallbackPreview(canvas, error);
|
||||
}
|
||||
}
|
||||
|
||||
function createScene(canvas) {
|
||||
@@ -29,25 +48,36 @@ function createScene(canvas) {
|
||||
antialias: true,
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
renderer.setClearColor(0x050505, 1);
|
||||
renderer.setClearColor(0x07100d, 1);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(38, 1, 0.1, 100);
|
||||
camera.position.set(4.2, -6.4, 4.8);
|
||||
scene.fog = new THREE.Fog(0x07100d, 7, 15);
|
||||
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
|
||||
camera.position.set(3.4, -5.0, 3.2);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
const ambient = new THREE.AmbientLight(0xffffff, 0.46);
|
||||
const key = new THREE.DirectionalLight(0xffffff, 1.1);
|
||||
const ambient = new THREE.HemisphereLight(0xdffbff, 0x17110d, 0.86);
|
||||
const key = new THREE.DirectionalLight(0xffffff, 1.35);
|
||||
key.position.set(3, -5, 7);
|
||||
scene.add(ambient, key);
|
||||
const fill = new THREE.DirectionalLight(0x4fd5ff, 0.46);
|
||||
fill.position.set(-4, 3, 3);
|
||||
scene.add(ambient, key, fill);
|
||||
|
||||
const grid = new THREE.GridHelper(6.8, 12, 0x444444, 0x242424);
|
||||
const floor = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(6.2, 4.5, 0.08),
|
||||
new THREE.MeshStandardMaterial({ color: 0x151b1f, roughness: 0.78, metalness: 0.12 }),
|
||||
);
|
||||
floor.position.z = -0.98;
|
||||
scene.add(floor);
|
||||
|
||||
const grid = new THREE.GridHelper(6.2, 14, 0x4b555a, 0x252f32);
|
||||
grid.rotation.x = Math.PI / 2;
|
||||
grid.position.z = -0.93;
|
||||
scene.add(grid);
|
||||
|
||||
const axes = new THREE.AxesHelper(1.25);
|
||||
axes.position.set(-2.7, -2.25, -1.05);
|
||||
const axes = new THREE.AxesHelper(1.45);
|
||||
axes.position.set(-2.85, -2.0, -0.86);
|
||||
scene.add(axes);
|
||||
|
||||
const envelope = buildEnvelope();
|
||||
@@ -55,12 +85,12 @@ function createScene(canvas) {
|
||||
|
||||
const tableGroup = new THREE.Group();
|
||||
const table = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(2.55, 1.9, 0.16),
|
||||
new THREE.MeshStandardMaterial({ color: 0x353535, roughness: 0.72, metalness: 0.2 }),
|
||||
new THREE.BoxGeometry(3.0, 2.15, 0.18),
|
||||
new THREE.MeshStandardMaterial({ color: 0x42484e, roughness: 0.68, metalness: 0.22 }),
|
||||
);
|
||||
const platter = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.72, 0.72, 0.13, 48),
|
||||
new THREE.MeshStandardMaterial({ color: 0x4f5960, roughness: 0.62, metalness: 0.35 }),
|
||||
new THREE.CylinderGeometry(0.84, 0.84, 0.16, 64),
|
||||
new THREE.MeshStandardMaterial({ color: 0x6d7880, roughness: 0.48, metalness: 0.42 }),
|
||||
);
|
||||
platter.rotation.x = Math.PI / 2;
|
||||
platter.position.z = 0.14;
|
||||
@@ -72,14 +102,14 @@ function createScene(canvas) {
|
||||
|
||||
const toolGroup = new THREE.Group();
|
||||
const toolBody = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.035, 0.055, 0.86, 24),
|
||||
new THREE.MeshStandardMaterial({ color: 0x26d7df, emissive: 0x073a3d, roughness: 0.32 }),
|
||||
new THREE.CylinderGeometry(0.045, 0.07, 1.05, 28),
|
||||
new THREE.MeshStandardMaterial({ color: 0x29ecf0, emissive: 0x0a4f52, roughness: 0.28 }),
|
||||
);
|
||||
toolBody.rotation.x = Math.PI / 2;
|
||||
toolBody.position.z = 0.43;
|
||||
toolBody.position.z = 0.52;
|
||||
const tcpPoint = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.075, 24, 16),
|
||||
new THREE.MeshStandardMaterial({ color: 0x1ffff4, emissive: 0x094f4f, roughness: 0.2 }),
|
||||
new THREE.SphereGeometry(0.095, 28, 18),
|
||||
new THREE.MeshStandardMaterial({ color: 0x1ffff4, emissive: 0x0b6f6f, roughness: 0.18 }),
|
||||
);
|
||||
const toolAxis = new THREE.Line(
|
||||
new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(), new THREE.Vector3(0, 0, 1)]),
|
||||
@@ -89,6 +119,7 @@ function createScene(canvas) {
|
||||
scene.add(toolGroup);
|
||||
|
||||
const preview = {
|
||||
kind: "webgl",
|
||||
renderer,
|
||||
scene,
|
||||
camera,
|
||||
@@ -101,10 +132,136 @@ function createScene(canvas) {
|
||||
return preview;
|
||||
}
|
||||
|
||||
function createFallbackPreview(canvas, error) {
|
||||
return {
|
||||
kind: "fallback",
|
||||
canvas,
|
||||
errorMessage: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
|
||||
function renderFallbackPreview(preview, state) {
|
||||
const { canvas } = preview;
|
||||
const width = Math.max(canvas.clientWidth, 320);
|
||||
const height = Math.max(canvas.clientHeight, 240);
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
ctx.fillStyle = "#07100d";
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
const cx = width * 0.5;
|
||||
const cy = height * 0.53;
|
||||
const scale = Math.min(width / 7.2, height / 4.8);
|
||||
|
||||
drawFallbackGrid(ctx, cx, cy, scale);
|
||||
|
||||
ctx.strokeStyle = "#e43a35";
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.strokeRect(cx - 2.95 * scale, cy - 1.9 * scale, 5.9 * scale, 3.8 * scale);
|
||||
|
||||
ctx.fillStyle = "#42484e";
|
||||
ctx.strokeStyle = "#79838a";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(cx - 1.5 * scale, cy - 0.78 * scale, 3.0 * scale, 1.56 * scale, 4);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
|
||||
ctx.fillStyle = "#6d7880";
|
||||
ctx.strokeStyle = "#a3b0b8";
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, cy, 0.84 * scale, 0.48 * scale, 0, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle = "#ffffff";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
for (let index = 0; index < 72; index += 1) {
|
||||
const t = index / 71;
|
||||
const x = cx + (-2.45 + t * 4.9) * scale;
|
||||
const y = cy + Math.sin(t * Math.PI * 13) * 0.36 * scale;
|
||||
if (index === 0) ctx.moveTo(x, y);
|
||||
else ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
const tcp = state.tcpPose;
|
||||
const tool = state.toolAxisVector;
|
||||
const toolX = cx + clamp(tcp.x * 0.035, -2.7, 2.7) * scale;
|
||||
const toolY = cy - clamp(tcp.y * 0.035, -2.0, 2.0) * scale;
|
||||
const axisX = toolX + tool.x * 0.85 * scale;
|
||||
const axisY = toolY - (tool.y || 0.2) * 0.85 * scale;
|
||||
ctx.strokeStyle = "#21f2f2";
|
||||
ctx.fillStyle = "#1ffff4";
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(toolX, toolY);
|
||||
ctx.lineTo(axisX, axisY);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.arc(toolX, toolY, 0.09 * scale, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = "#b7c7b8";
|
||||
ctx.font = "12px Courier New, monospace";
|
||||
ctx.fillText("2D RTCP fallback", 12, height - 14);
|
||||
|
||||
exposePreviewDataset(canvas, state, {
|
||||
pointCount: 72,
|
||||
sceneObjectCount: 12,
|
||||
toolhead: {
|
||||
x: clamp(tcp.x * 0.035, -2.7, 2.7),
|
||||
y: clamp(tcp.y * 0.035, -2.0, 2.0),
|
||||
z: clamp(tcp.z * 0.04 + 0.35, -1.1, 1.9),
|
||||
},
|
||||
renderer: "2d-fallback",
|
||||
});
|
||||
canvas.dataset.threeFallbackReason = preview.errorMessage;
|
||||
}
|
||||
|
||||
function drawFallbackGrid(ctx, cx, cy, scale) {
|
||||
ctx.strokeStyle = "#273236";
|
||||
ctx.lineWidth = 1;
|
||||
for (let x = -3; x <= 3; x += 0.5) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx + x * scale, cy - 2.1 * scale);
|
||||
ctx.lineTo(cx + x * scale, cy + 2.1 * scale);
|
||||
ctx.stroke();
|
||||
}
|
||||
for (let y = -2; y <= 2; y += 0.5) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx - 3.1 * scale, cy + y * scale);
|
||||
ctx.lineTo(cx + 3.1 * scale, cy + y * scale);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function exposePreviewDataset(canvas, state, preview) {
|
||||
canvas.dataset.threeReady = "true";
|
||||
canvas.dataset.threeRevision = THREE.REVISION;
|
||||
canvas.dataset.threePathPoints = String(preview.pointCount);
|
||||
canvas.dataset.threeSceneObjects = String(preview.sceneObjectCount);
|
||||
canvas.dataset.threeToolhead = JSON.stringify(toRoundedVector(preview.toolhead));
|
||||
canvas.dataset.threeToolAxis = JSON.stringify(toRoundedVector(state.toolAxisVector));
|
||||
canvas.dataset.threeTcpPose = JSON.stringify(toRoundedPose(state.tcpPose));
|
||||
canvas.dataset.threeRtcpState = state.rtcpState;
|
||||
canvas.dataset.threeSelectedView = state.preview.selectedView;
|
||||
canvas.dataset.threeFrameApi = state.rtcpFrame.apiName;
|
||||
canvas.dataset.threeRenderer = preview.renderer;
|
||||
}
|
||||
|
||||
function buildEnvelope() {
|
||||
const geometry = new THREE.BoxGeometry(5.8, 4.3, 2.6);
|
||||
const geometry = new THREE.BoxGeometry(5.9, 4.25, 2.7);
|
||||
const edges = new THREE.EdgesGeometry(geometry);
|
||||
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xcc2525 }));
|
||||
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xe43a35 }));
|
||||
line.position.z = 0.2;
|
||||
return line;
|
||||
}
|
||||
@@ -113,13 +270,13 @@ function buildToolpath() {
|
||||
const points = [];
|
||||
for (let index = 0; index < 72; index += 1) {
|
||||
const t = index / 71;
|
||||
const x = -2.3 + t * 4.6;
|
||||
const y = Math.sin(t * Math.PI * 13) * 0.28;
|
||||
const z = -0.75 + Math.sin(t * Math.PI * 2) * 0.36;
|
||||
const x = -2.45 + t * 4.9;
|
||||
const y = Math.sin(t * Math.PI * 13) * 0.36;
|
||||
const z = -0.68 + Math.sin(t * Math.PI * 2) * 0.42;
|
||||
points.push(new THREE.Vector3(x, y, z));
|
||||
}
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints(points);
|
||||
return new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xf4f4f4 }));
|
||||
return new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xffffff }));
|
||||
}
|
||||
|
||||
function updateMachinePose(preview, state) {
|
||||
@@ -145,13 +302,13 @@ function updateMachinePose(preview, state) {
|
||||
|
||||
function setCameraView(camera, selectedView) {
|
||||
if (selectedView === "x") {
|
||||
camera.position.set(6, 0.02, 0.4);
|
||||
camera.position.set(6, 0.02, 0.45);
|
||||
} else if (selectedView === "y") {
|
||||
camera.position.set(0.02, -6, 0.6);
|
||||
camera.position.set(0.02, -6, 0.7);
|
||||
} else if (selectedView === "z") {
|
||||
camera.position.set(0.01, -0.02, 7);
|
||||
camera.position.set(0.01, -0.02, 6.6);
|
||||
} else {
|
||||
camera.position.set(4.2, -6.4, 4.8);
|
||||
camera.position.set(3.4, -5.0, 3.2);
|
||||
}
|
||||
camera.lookAt(0, 0, 0);
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
Reference in New Issue
Block a user