Align AXIS Web control lifecycle with LinuxCNC
This commit is contained in:
@@ -110,6 +110,7 @@ export function XyzacToolPreview({ visual, view = "p", zoom = 1, rotateMode = fa
|
||||
let paintedProgress = 0;
|
||||
let liveHistoryLine: THREE.Line | null = null;
|
||||
let activeLiveLine: THREE.Line | null = null;
|
||||
let livePathEpoch = -1;
|
||||
let lastHistoryTick = -1;
|
||||
let lastActiveKey = "";
|
||||
let lastPreset = "";
|
||||
@@ -145,6 +146,29 @@ export function XyzacToolPreview({ visual, view = "p", zoom = 1, rotateMode = fa
|
||||
const segments = state.toolpath as Segment[];
|
||||
const layer = layersRef.current;
|
||||
const now = performance.now();
|
||||
if (state.livePathEpoch !== livePathEpoch) {
|
||||
livePathEpoch = state.livePathEpoch;
|
||||
clearLines(liveGroup);
|
||||
liveTrace = [];
|
||||
playbackQueue = [];
|
||||
playbackStart = 0;
|
||||
queuedLastTick = state.tick;
|
||||
renderedTick = state.tick;
|
||||
renderedPose = state.livePose;
|
||||
renderedJoints = state.joints;
|
||||
renderedActiveSegment = state.activeSegmentId ?? 0;
|
||||
renderedProgress = state.activeProgress;
|
||||
paintedTick = renderedTick;
|
||||
paintedPose = renderedPose;
|
||||
paintedJoints = renderedJoints;
|
||||
paintedActiveSegment = renderedActiveSegment;
|
||||
paintedProgress = renderedProgress;
|
||||
liveTick = renderedTick;
|
||||
liveHistoryLine = null;
|
||||
activeLiveLine = null;
|
||||
lastHistoryTick = -1;
|
||||
lastActiveKey = "";
|
||||
}
|
||||
// React can deliver a full snapshot just behind an already queued
|
||||
// motion-delta response. Keep the rendered pose monotonic until the
|
||||
// newer samples arrive; otherwise the tool visibly jumps backward.
|
||||
@@ -217,28 +241,6 @@ export function XyzacToolPreview({ visual, view = "p", zoom = 1, rotateMode = fa
|
||||
segmentCandidatesBySourceLine.set(sourceLine, candidates);
|
||||
}
|
||||
clearLines(pathGroup);
|
||||
clearLines(liveGroup);
|
||||
// A new PROGRAM_LOAD, including the transient empty snapshot while
|
||||
// loading, starts a new live-trace epoch. Clear internal playback and
|
||||
// history buffers as well as rendered geometry so the previous
|
||||
// program's yellow/orange path cannot be painted again.
|
||||
liveTrace = [];
|
||||
playbackQueue = [];
|
||||
playbackStart = 0;
|
||||
queuedLastTick = -1;
|
||||
renderedTick = state.tick;
|
||||
renderedPose = state.livePose;
|
||||
renderedJoints = state.joints;
|
||||
renderedActiveSegment = state.activeSegmentId ?? 0;
|
||||
renderedProgress = state.activeProgress;
|
||||
paintedTick = renderedTick;
|
||||
paintedPose = renderedPose;
|
||||
paintedJoints = renderedJoints;
|
||||
paintedActiveSegment = renderedActiveSegment;
|
||||
paintedProgress = renderedProgress;
|
||||
// Seed the new epoch without adding a visible history point. The
|
||||
// first post-load motion delta will create the first live segment.
|
||||
liveTick = renderedTick;
|
||||
bounds = fit(segments);
|
||||
// Long programs can contain thousands of linear blocks. Batch their
|
||||
// vertices so preview loading does not block the UI on one WebGL
|
||||
@@ -270,10 +272,6 @@ export function XyzacToolPreview({ visual, view = "p", zoom = 1, rotateMode = fa
|
||||
addSegments(batched.rapid, rapid, true);
|
||||
addSegments(batched.completed, completed);
|
||||
for (const sampled of sampledLines) addLine(sampled.points, sampled.rapid ? rapid : completed, sampled.rapid);
|
||||
liveHistoryLine = null;
|
||||
activeLiveLine = null;
|
||||
lastHistoryTick = -1;
|
||||
lastActiveKey = "";
|
||||
}
|
||||
const presetKey = `${viewRef.current}:${zoomRef.current}:${bounds.radius}:${bounds.center.x}:${bounds.center.y}:${bounds.center.z}`;
|
||||
if (presetKey !== lastPreset) { presetCamera(viewRef.current, zoomRef.current); lastPreset = presetKey; }
|
||||
@@ -289,6 +287,7 @@ export function XyzacToolPreview({ visual, view = "p", zoom = 1, rotateMode = fa
|
||||
liveTick = renderedTick;
|
||||
}
|
||||
element.dataset.xyzacLiveTraceCount = String(liveTrace.length);
|
||||
element.dataset.xyzacLivePathEpoch = String(livePathEpoch);
|
||||
element.dataset.xyzacRenderedTick = String(renderedTick);
|
||||
const historyDue = liveTrace.length > 1 && (lastHistoryTick < 0 || renderedTick - lastHistoryTick >= 4);
|
||||
if (historyDue) {
|
||||
@@ -346,7 +345,7 @@ export function XyzacToolPreview({ visual, view = "p", zoom = 1, rotateMode = fa
|
||||
const pose = visual.pose;
|
||||
const helixCount = visual.toolpath.filter((segment) => (segment as { kind?: string }).kind === "helix").length;
|
||||
const sampleCount = visual.toolpath.reduce((count, segment) => count + (Array.isArray((segment as { samples?: unknown[] }).samples) ? (segment as { samples: unknown[] }).samples.length : 0), 0);
|
||||
const controllerButton = (action: string, label: string, glyph: string) => <button type="button" data-xyzac-controller={action} title={label} aria-label={label} onClick={() => { const controller = (host.current as (HTMLDivElement & { xyzacController?: Record<string, () => void> }) | null)?.xyzacController; controller?.[action]?.(); }}>{glyph}</button>;
|
||||
const controllerButton = (action: string, label: string, glyph: string) => ["zoomIn", "zoomOut"].includes(action) ? null : <button type="button" data-xyzac-controller={action} title={label} aria-label={label} onClick={() => { const controller = (host.current as (HTMLDivElement & { xyzacController?: Record<string, () => void> }) | null)?.xyzacController; controller?.[action]?.(); }}>{glyph}</button>;
|
||||
return <div className="three-viewer xyzac-tool-preview" ref={host} data-xyzac-authoritative={String(visual.authoritative)} data-xyzac-preview="toolpath" data-xyzac-controller="orbit" data-xyzac-origin="0,0,0" data-xyzac-origin-axes="X,Y,Z" data-xyzac-toolpath-count={visual.toolpath.length} data-xyzac-helix-count={helixCount} data-xyzac-sample-count={sampleCount} data-xyzac-samples-source="linuxcnc-wasm-sim_posemath_segment_point" data-xyzac-live-source="linuxcnc-source-tp" data-xyzac-live-progress={visual.activeProgress} data-xyzac-live-pose={JSON.stringify(visual.livePose)} data-xyzac-tick={visual.tick} data-xyzac-state-hash={visual.stateHash} data-xyzac-active-segment={visual.activeSegmentId ?? ""} aria-label="XYZAC authoritative tool preview"><div className="xyzac-view-controller" role="toolbar" aria-label="3D preview controller">{controllerButton("zoomIn", "Zoom in", "+")}{controllerButton("zoomOut", "Zoom out", "-")}<span className="xyzac-controller-divider" aria-hidden="true" />{controllerButton("panLeft", "Pan left", "L")}{controllerButton("panRight", "Pan right", "R")}{controllerButton("panUp", "Pan up", "U")}{controllerButton("panDown", "Pan down", "D")}<span className="xyzac-controller-divider" aria-hidden="true" />{controllerButton("reset", "Reset 3D view", "0")}</div><pre ref={hudRef} className="axis-preview-hud" aria-label="XYZAC tool preview state">{visual.authoritative ? `TOOL PREVIEW\nX ${Number(pose.x).toFixed(3)} Y ${Number(pose.y).toFixed(3)} Z ${Number(pose.z).toFixed(3)}\nA ${Number(pose.a).toFixed(3)} C ${Number(pose.c).toFixed(3)}\nJ0 ${Number(visual.joints[0]).toFixed(3)} J1 ${Number(visual.joints[1]).toFixed(3)} J2 ${Number(visual.joints[2]).toFixed(3)}\nJ3 ${Number(visual.joints[3]).toFixed(3)} J4 ${Number(visual.joints[4]).toFixed(3)}\nHelix ${helixCount} / Samples ${sampleCount}\nLive ${visual.activeSegmentId ?? "-"} / ${visual.activeProgress.toFixed(3)}\nTick ${visual.tick}\nHash ${visual.stateHash.slice(0, 18)}` : "TOOL PREVIEW\nWASM state pending"}</pre></div>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user