Add RTCP simulation QA updates
This commit is contained in:
@@ -33,21 +33,23 @@ export function renderFiveAxisScene(canvas, state) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pointCount = preview.previewPath.geometry.getAttribute("position").count;
|
||||
const executedPointCount = preview.executedPath.geometry.getAttribute("position").count;
|
||||
const pointCount = geometryPointCount(preview.previewPath.geometry);
|
||||
const executedPointCount = geometryPointCount(preview.executedPath.geometry);
|
||||
exposePreviewDataset(canvas, state, {
|
||||
pointCount,
|
||||
executedPointCount,
|
||||
feedPointCount: preview.feedPath.geometry.getAttribute("position").count,
|
||||
rapidPointCount: preview.rapidPath.geometry.getAttribute("position").count,
|
||||
arcPointCount: preview.arcPath.geometry.getAttribute("position").count,
|
||||
currentSegmentPointCount: preview.currentSegmentPath.geometry.getAttribute("position").count,
|
||||
feedPointCount: geometryPointCount(preview.feedPath.geometry),
|
||||
rapidPointCount: geometryPointCount(preview.rapidPath.geometry),
|
||||
arcPointCount: geometryPointCount(preview.arcPath.geometry),
|
||||
currentSegmentPointCount: geometryPointCount(preview.currentSegmentPath.geometry),
|
||||
sceneObjectCount: countSceneObjects(preview.scene),
|
||||
toolhead: preview.currentToolhead,
|
||||
renderer: "webgl",
|
||||
sceneMode: "program-preview-and-tool-execution",
|
||||
machineReferenceModel: "webgl-five-axis-reference",
|
||||
cameraControls: preview.controls.enabled,
|
||||
toolExecutionMarker: preview.toolMarker.visible,
|
||||
toolAxisMarker: preview.toolAxis.visible,
|
||||
pathFitBounds: preview.pathFitBoundsReady,
|
||||
});
|
||||
}
|
||||
@@ -72,6 +74,9 @@ function createScene(canvas) {
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
|
||||
|
||||
const machineModel = createMachineReferenceModel();
|
||||
scene.add(machineModel.root);
|
||||
|
||||
const previewPath = createLine(0x808892, 0.56);
|
||||
const feedPath = createLine(0x4fb3ff, 0.92);
|
||||
const executedPath = createLine(0x1ffff4, 1);
|
||||
@@ -103,6 +108,7 @@ function createScene(canvas) {
|
||||
rapidPath,
|
||||
arcPath,
|
||||
currentSegmentPath,
|
||||
machineModel,
|
||||
toolMarker,
|
||||
toolAxis,
|
||||
currentToolhead: new THREE.Vector3(),
|
||||
@@ -133,6 +139,35 @@ function renderFallbackPreview(preview, state) {
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
const previewPoints = buildProgramPreviewPoints(state);
|
||||
const executedPoints = buildExecutedProgramPoints(state, previewPoints);
|
||||
const rapidPoints = buildRapidPreviewPoints(state);
|
||||
const feedPoints = buildTypedPreviewPoints(state, "STRAIGHT_FEED");
|
||||
const arcPoints = buildTypedPreviewPoints(state, "ARC_FEED");
|
||||
const currentSegmentPoints = buildCurrentSegmentPoints(state);
|
||||
const pointCount = previewPoints.length;
|
||||
const executedPointCount = executedPoints.length;
|
||||
const toolPosition = executionToolPosition(state, previewPoints);
|
||||
|
||||
exposePreviewDataset(canvas, state, {
|
||||
pointCount,
|
||||
executedPointCount,
|
||||
sceneObjectCount: 8 + (pointCount > 0 ? 1 : 0) + (executedPointCount > 0 ? 1 : 0),
|
||||
toolhead: toolPosition || { x: 0, y: 0, z: 0 },
|
||||
renderer: "2d-fallback",
|
||||
sceneMode: "program-preview-and-tool-execution",
|
||||
machineReferenceModel: "2d-five-axis-reference",
|
||||
cameraControls: false,
|
||||
toolExecutionMarker: Boolean(toolPosition),
|
||||
toolAxisMarker: Boolean(toolPosition),
|
||||
feedPointCount: feedPoints.length,
|
||||
rapidPointCount: rapidPoints.length,
|
||||
arcPointCount: arcPoints.length,
|
||||
currentSegmentPointCount: currentSegmentPoints.length,
|
||||
pathFitBounds: computePointBounds(previewPoints.concat(executedPoints, currentSegmentPoints)) !== null,
|
||||
});
|
||||
canvas.dataset.threeFallbackReason = preview.errorMessage;
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
@@ -144,14 +179,8 @@ function renderFallbackPreview(preview, state) {
|
||||
const cy = height * 0.53;
|
||||
const scale = Math.min(width / 7.2, height / 4.8);
|
||||
|
||||
const previewPoints = buildProgramPreviewPoints(state);
|
||||
const executedPoints = buildExecutedProgramPoints(state, previewPoints);
|
||||
const rapidPoints = buildRapidPreviewPoints(state);
|
||||
const feedPoints = buildTypedPreviewPoints(state, "STRAIGHT_FEED");
|
||||
const arcPoints = buildTypedPreviewPoints(state, "ARC_FEED");
|
||||
const currentSegmentPoints = buildCurrentSegmentPoints(state);
|
||||
const pointCount = previewPoints.length;
|
||||
const executedPointCount = executedPoints.length;
|
||||
drawFallbackMachineReference(ctx, cx, cy, scale, state);
|
||||
|
||||
if (pointCount > 0) {
|
||||
ctx.strokeStyle = "#8d95a0";
|
||||
ctx.lineWidth = 2;
|
||||
@@ -175,16 +204,6 @@ function renderFallbackPreview(preview, state) {
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
const toolPosition = executionToolPosition(state, previewPoints);
|
||||
const fitPoints = collectFitPoints(previewPoints, executedPoints, currentSegmentPoints, toolPosition);
|
||||
const fitKey = [
|
||||
previewPoints.length,
|
||||
executedPoints.length,
|
||||
currentSegmentPoints.length,
|
||||
previewSourceMode(state),
|
||||
state.programExecutionMotionIndex || 0,
|
||||
state.programExecutionSampleIndex || 0,
|
||||
].join(":");
|
||||
if (toolPosition) {
|
||||
const toolX = cx + toolPosition.x * scale;
|
||||
const toolY = cy - toolPosition.y * scale;
|
||||
@@ -197,23 +216,6 @@ function renderFallbackPreview(preview, state) {
|
||||
ctx.fillStyle = "#b7c7b8";
|
||||
ctx.font = "12px Courier New, monospace";
|
||||
ctx.fillText("2D RTCP fallback", 12, height - 14);
|
||||
|
||||
exposePreviewDataset(canvas, state, {
|
||||
pointCount,
|
||||
executedPointCount,
|
||||
sceneObjectCount: (pointCount > 0 ? 1 : 0) + (executedPointCount > 0 ? 1 : 0),
|
||||
toolhead: toolPosition || { x: 0, y: 0, z: 0 },
|
||||
renderer: "2d-fallback",
|
||||
sceneMode: "program-preview-and-tool-execution",
|
||||
cameraControls: false,
|
||||
toolExecutionMarker: Boolean(toolPosition),
|
||||
feedPointCount: feedPoints.length,
|
||||
rapidPointCount: rapidPoints.length,
|
||||
arcPointCount: arcPoints.length,
|
||||
currentSegmentPointCount: currentSegmentPoints.length,
|
||||
pathFitBounds: computePointBounds(previewPoints.concat(executedPoints, currentSegmentPoints)) !== null,
|
||||
});
|
||||
canvas.dataset.threeFallbackReason = preview.errorMessage;
|
||||
}
|
||||
|
||||
function exposePreviewDataset(canvas, state, preview) {
|
||||
@@ -230,9 +232,15 @@ function exposePreviewDataset(canvas, state, preview) {
|
||||
canvas.dataset.threeFrameApi = state.rtcpFrame.apiName;
|
||||
canvas.dataset.threeRenderer = preview.renderer;
|
||||
canvas.dataset.threeSceneMode = preview.sceneMode;
|
||||
canvas.dataset.threePreviewScope = preview.machineReferenceModel
|
||||
? "machine-reference-and-toolpath"
|
||||
: "toolpath-only";
|
||||
canvas.dataset.threeMachineReferenceModel = preview.machineReferenceModel || "none";
|
||||
canvas.dataset.threeCameraControls = preview.cameraControls ? "orbit-pan-zoom" : "none";
|
||||
canvas.dataset.threeProgramPreviewSource = previewSourceMode(state);
|
||||
canvas.dataset.threeToolExecutionMarker = preview.toolExecutionMarker ? "true" : "false";
|
||||
canvas.dataset.threeTcpMarker = preview.toolExecutionMarker ? "sphere" : "hidden";
|
||||
canvas.dataset.threeToolAxisMarker = preview.toolAxisMarker ? "line" : "hidden";
|
||||
canvas.dataset.threeToolpathPreviewSource = toolpathPreviewSource(state);
|
||||
canvas.dataset.threeToolExecutionTraceSource = toolExecutionTraceSource(state);
|
||||
canvas.dataset.threePathFitBounds = preview.pathFitBounds ? "ok" : "pending";
|
||||
@@ -257,6 +265,70 @@ function createLine(color, opacity) {
|
||||
);
|
||||
}
|
||||
|
||||
function createMachineReferenceModel() {
|
||||
const root = new THREE.Group();
|
||||
root.name = "five-axis-machine-reference";
|
||||
|
||||
const base = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(4.8, 3.2, 0.08),
|
||||
new THREE.MeshBasicMaterial({ color: 0x222930 }),
|
||||
);
|
||||
base.position.z = -0.16;
|
||||
|
||||
const table = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(3.7, 2.35, 0.05),
|
||||
new THREE.MeshBasicMaterial({ color: 0x3a444d, transparent: true, opacity: 0.78 }),
|
||||
);
|
||||
table.position.z = -0.08;
|
||||
|
||||
const xAxis = createStaticLine([new THREE.Vector3(-2.2, 0, 0), new THREE.Vector3(2.25, 0, 0)], 0xff4d4d);
|
||||
const yAxis = createStaticLine([new THREE.Vector3(0, -1.55, 0), new THREE.Vector3(0, 1.6, 0)], 0x70df7d);
|
||||
const zAxis = createStaticLine([new THREE.Vector3(0, 0, -0.08), new THREE.Vector3(0, 0, 1.75)], 0x5aa7ff);
|
||||
|
||||
const rotaryA = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(0.88, 0.018, 8, 72),
|
||||
new THREE.MeshBasicMaterial({ color: 0x1ffff4, transparent: true, opacity: 0.92 }),
|
||||
);
|
||||
rotaryA.rotation.y = Math.PI / 2;
|
||||
|
||||
const rotaryC = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(1.1, 0.016, 8, 72),
|
||||
new THREE.MeshBasicMaterial({ color: 0xffd166, transparent: true, opacity: 0.9 }),
|
||||
);
|
||||
rotaryC.rotation.x = Math.PI / 2;
|
||||
rotaryC.position.z = 0.04;
|
||||
|
||||
const toolHolder = new THREE.Group();
|
||||
const holderBody = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.08, 0.08, 0.42, 18),
|
||||
new THREE.MeshBasicMaterial({ color: 0xf1f5f9 }),
|
||||
);
|
||||
holderBody.rotation.x = Math.PI / 2;
|
||||
holderBody.position.z = 0.32;
|
||||
const cutter = new THREE.Mesh(
|
||||
new THREE.ConeGeometry(0.06, 0.25, 18),
|
||||
new THREE.MeshBasicMaterial({ color: 0xfff176 }),
|
||||
);
|
||||
cutter.rotation.x = Math.PI;
|
||||
cutter.position.z = 0.08;
|
||||
toolHolder.add(holderBody, cutter);
|
||||
|
||||
root.add(base, table, xAxis, yAxis, zAxis, rotaryA, rotaryC, toolHolder);
|
||||
return {
|
||||
root,
|
||||
rotaryA,
|
||||
rotaryC,
|
||||
toolHolder,
|
||||
};
|
||||
}
|
||||
|
||||
function createStaticLine(points, color) {
|
||||
return new THREE.Line(
|
||||
new THREE.BufferGeometry().setFromPoints(points),
|
||||
new THREE.LineBasicMaterial({ color, transparent: true, opacity: 0.95 }),
|
||||
);
|
||||
}
|
||||
|
||||
function updateToolpathPreview(preview, state) {
|
||||
const previewPoints = buildProgramPreviewPoints(state);
|
||||
const executedPoints = buildExecutedProgramPoints(state, previewPoints);
|
||||
@@ -265,6 +337,15 @@ function updateToolpathPreview(preview, state) {
|
||||
const arcPoints = buildTypedPreviewPoints(state, "ARC_FEED");
|
||||
const currentSegmentPoints = buildCurrentSegmentPoints(state);
|
||||
const toolPosition = executionToolPosition(state, previewPoints);
|
||||
const fitPoints = collectFitPoints(previewPoints, executedPoints, currentSegmentPoints, toolPosition);
|
||||
const fitKey = [
|
||||
previewPoints.length,
|
||||
executedPoints.length,
|
||||
currentSegmentPoints.length,
|
||||
previewSourceMode(state),
|
||||
state.programExecutionMotionIndex || 0,
|
||||
state.programExecutionSampleIndex || 0,
|
||||
].join(":");
|
||||
|
||||
updateLineGeometry(preview.previewPath, previewPoints);
|
||||
updateLineGeometry(preview.feedPath, feedPoints);
|
||||
@@ -273,6 +354,7 @@ function updateToolpathPreview(preview, state) {
|
||||
updateLineGeometry(preview.arcPath, arcPoints);
|
||||
updateLineGeometry(preview.currentSegmentPath, currentSegmentPoints);
|
||||
updateToolExecutionMarker(preview, state, toolPosition);
|
||||
updateMachineReferenceModel(preview, state, toolPosition);
|
||||
|
||||
const cameraRevision = state.preview.cameraRevision ?? 0;
|
||||
if (
|
||||
@@ -309,6 +391,22 @@ function updateToolExecutionMarker(preview, state, toolPosition) {
|
||||
]);
|
||||
}
|
||||
|
||||
function updateMachineReferenceModel(preview, state, toolPosition) {
|
||||
const model = preview.machineModel;
|
||||
if (!model) return;
|
||||
const a = degreesToRadians(state.axisPose?.a);
|
||||
const b = degreesToRadians(state.axisPose?.b);
|
||||
const c = degreesToRadians(state.axisPose?.c);
|
||||
model.rotaryA.rotation.x = a;
|
||||
model.rotaryA.rotation.y = Math.PI / 2 + b;
|
||||
model.rotaryC.rotation.z = c;
|
||||
|
||||
const tcpPosition = toolPosition || toPreviewVector(state.tcpPose || state.axisPose);
|
||||
model.toolHolder.position.copy(tcpPosition);
|
||||
const toolVector = toToolVector(state.toolAxisVector);
|
||||
model.toolHolder.lookAt(tcpPosition.clone().add(toolVector));
|
||||
}
|
||||
|
||||
function updateLineGeometry(line, points) {
|
||||
line.visible = points.length > 0;
|
||||
line.geometry.dispose();
|
||||
@@ -317,6 +415,10 @@ function updateLineGeometry(line, points) {
|
||||
: EMPTY_GEOMETRY.clone();
|
||||
}
|
||||
|
||||
function geometryPointCount(geometry) {
|
||||
return geometry?.getAttribute("position")?.count || 0;
|
||||
}
|
||||
|
||||
function buildProgramPreviewPoints(state) {
|
||||
const motion = state.programExecution?.motion;
|
||||
if (Array.isArray(motion) && motion.length > 0 && state.preview.pathPoints !== 0) {
|
||||
@@ -384,7 +486,6 @@ function buildFixturePreviewPoints(pointCount, tcpPosition) {
|
||||
}
|
||||
|
||||
function executionToolPosition(state, previewPoints) {
|
||||
if (state.preview.pathPoints === 0) return null;
|
||||
const feedbackAxes = state.programRuntimeFeedback?.axisPose || state.programRuntimeFeedback;
|
||||
if (feedbackAxes && hasLinearAxes(feedbackAxes)) return vectorFromAxes(feedbackAxes);
|
||||
if (hasLinearAxes(state.axisPose)) return vectorFromAxes(state.axisPose);
|
||||
@@ -505,6 +606,56 @@ function drawFallbackPolyline(ctx, points, cx, cy, scale) {
|
||||
}
|
||||
}
|
||||
|
||||
function drawFallbackMachineReference(ctx, cx, cy, scale, state) {
|
||||
const tableWidth = 4.8 * scale;
|
||||
const tableHeight = 3.2 * scale;
|
||||
ctx.fillStyle = "#20272e";
|
||||
ctx.strokeStyle = "#56616b";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.fillRect(cx - tableWidth / 2, cy - tableHeight / 2, tableWidth, tableHeight);
|
||||
ctx.strokeRect(cx - tableWidth / 2, cy - tableHeight / 2, tableWidth, tableHeight);
|
||||
|
||||
drawFallbackAxis(ctx, cx - 2.25 * scale, cy, cx + 2.25 * scale, cy, "#ff4d4d");
|
||||
drawFallbackAxis(ctx, cx, cy + 1.55 * scale, cx, cy - 1.6 * scale, "#70df7d");
|
||||
drawFallbackAxis(ctx, cx, cy + 0.2 * scale, cx, cy - 1.15 * scale, "#5aa7ff");
|
||||
|
||||
ctx.strokeStyle = "#1ffff4";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(cx, cy, 0.92 * scale, 0.42 * scale, degreesToRadians(state.axisPose?.a), 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle = "#ffd166";
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, 0.7 * scale, 0, Math.PI * 2);
|
||||
ctx.stroke();
|
||||
|
||||
const tcp = executionToolPosition(state, []);
|
||||
if (tcp) {
|
||||
const toolX = cx + tcp.x * scale;
|
||||
const toolY = cy - tcp.y * scale;
|
||||
ctx.strokeStyle = "#f1f5f9";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(toolX, toolY - 0.38 * scale);
|
||||
ctx.lineTo(toolX, toolY - 0.08 * scale);
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = "#1ffff4";
|
||||
ctx.beginPath();
|
||||
ctx.arc(toolX, toolY, 0.07 * scale, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
function drawFallbackAxis(ctx, x1, y1, x2, y2, color) {
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = 3;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function createToolpathCameraControls(canvas, camera, renderFrame) {
|
||||
const controls = {
|
||||
enabled: true,
|
||||
@@ -677,7 +828,7 @@ function applyCameraControls(controls) {
|
||||
}
|
||||
|
||||
function resizeRenderer(preview) {
|
||||
const { canvas } = preview.renderer.domElement;
|
||||
const canvas = preview.renderer.domElement;
|
||||
const width = Math.max(canvas.clientWidth, 320);
|
||||
const height = Math.max(canvas.clientHeight, 240);
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
@@ -717,6 +868,10 @@ function round(value) {
|
||||
return Math.round(Number(value) * 1000) / 1000;
|
||||
}
|
||||
|
||||
function degreesToRadians(value) {
|
||||
return (Number(value) || 0) * Math.PI / 180;
|
||||
}
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user