继续完成 web-rtcp-5axis-sim-plan
结论:完成 LinuxCNC kinematics WASM ABI 覆盖,并将 web-rtcp-5axis-sim-plan 的 RTCP frame/boundary adapter 接到 xyzac-trt kinematics SDK;Node、build、browser smoke 验证通过。
This commit is contained in:
616
web-rtcp-5axis-sim-plan/app/src/state/store.js
Normal file
616
web-rtcp-5axis-sim-plan/app/src/state/store.js
Normal file
@@ -0,0 +1,616 @@
|
||||
import { buildRtcpFrame } from "../runtime/rtcp-frame.js";
|
||||
import { createLinuxCncBoundaryAdapter, createLinuxCncBoundaryReadiness } from "../runtime/linuxcnc-boundary-adapter.js";
|
||||
import { xyzacTrtProfile } from "../profiles/xyzac-trt.js";
|
||||
|
||||
const initialLinuxCncBoundaryAdapter = createLinuxCncBoundaryAdapter({
|
||||
profile: xyzacTrtProfile,
|
||||
});
|
||||
const initialLinuxCncBoundaryReadiness = createLinuxCncBoundaryReadiness(initialLinuxCncBoundaryAdapter);
|
||||
|
||||
const initialAxisPose = {
|
||||
x: 43.0,
|
||||
y: -32.15,
|
||||
z: -11.306,
|
||||
a: 0.0,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
machineProfile: "xyzac-trt",
|
||||
profile: xyzacTrtProfile,
|
||||
sessionName: "gmoccapy-web-session",
|
||||
sourceMode: "fixture-ui-only",
|
||||
frameSourceMode: "fixture-ui-only",
|
||||
machine: {
|
||||
powerOn: false,
|
||||
estopActive: false,
|
||||
mode: "manual",
|
||||
jogAxis: "x",
|
||||
jogIncrement: 1,
|
||||
mdiCommand: "G0 X0 Y0 Z0",
|
||||
resetCount: 0,
|
||||
},
|
||||
runState: "idle",
|
||||
activeProgram: "../../../linuxcnc/nc_files/3D_Chips.ngc",
|
||||
programSource: "fixture",
|
||||
programStartLine: 496,
|
||||
activeLine: 501,
|
||||
lineCount: 4711,
|
||||
fileSizeBytes: 200509,
|
||||
kinsType: "identity",
|
||||
rtcpState: "off",
|
||||
axisPose: initialAxisPose,
|
||||
jointPose: [],
|
||||
tcpPose: {
|
||||
x: 43.0,
|
||||
y: -32.15,
|
||||
z: -11.306,
|
||||
a: 0.0,
|
||||
c: 0.0,
|
||||
},
|
||||
toolAxisVector: {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 1.0,
|
||||
},
|
||||
rtcpFrame: null,
|
||||
kinematicsRuntime: null,
|
||||
kinematicsRuntimeReadiness: null,
|
||||
lastKinematicsResult: null,
|
||||
linuxCncBoundaryAdapter: initialLinuxCncBoundaryAdapter,
|
||||
linuxCncBoundaryReadiness: initialLinuxCncBoundaryReadiness,
|
||||
dro: {
|
||||
x: 43.0,
|
||||
y: -32.15,
|
||||
z: -11.306,
|
||||
a: 0.0,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
tcpX: 43.0,
|
||||
tcpY: -32.15,
|
||||
tcpZ: -11.306,
|
||||
dtgX: 0.0,
|
||||
dtgY: 0.01,
|
||||
dtgZ: 2.25,
|
||||
},
|
||||
feed: {
|
||||
currentVelocity: 2621,
|
||||
rapidOverride: 100,
|
||||
feedRate: 4500000,
|
||||
feedOverride: 100,
|
||||
},
|
||||
spindle: {
|
||||
rpm: 1600,
|
||||
override: 100,
|
||||
enabled: true,
|
||||
},
|
||||
coolant: {
|
||||
flood: true,
|
||||
mist: false,
|
||||
},
|
||||
preview: {
|
||||
pathPoints: 64,
|
||||
selectedView: "iso",
|
||||
fullscreen: false,
|
||||
},
|
||||
toolPreview: {
|
||||
toolNumber: 1,
|
||||
diameter: 6,
|
||||
length: 84.019,
|
||||
units: "mm",
|
||||
holder: "CAT40",
|
||||
},
|
||||
operatorMessage: "ready",
|
||||
};
|
||||
|
||||
const programLines = [
|
||||
"N4860 Y[#<yscale>*-39.009]",
|
||||
"N4870 Y[#<yscale>*-32.524]",
|
||||
"N4880 Y[#<yscale>*-32.384]",
|
||||
"N4890 Y[#<yscale>*-32.267]",
|
||||
"N4900 Y[#<yscale>*-32.235] Z[#<zscale>*-11.306]",
|
||||
"N4910 Y[#<yscale>*-32.118] Z[#<zscale>*-11.312]",
|
||||
"N4920 Y[#<yscale>*-32.103] Z[#<zscale>*-11.314]",
|
||||
"N4930 Y[#<yscale>*-32.071] Z[#<zscale>*-11.316]",
|
||||
"N4940 Y[#<yscale>*-31.972] Z[#<zscale>*-11.318]",
|
||||
"N4950 Y[#<yscale>*-31.759] Z[#<zscale>*-11.320]",
|
||||
"N4960 Y[#<yscale>*-31.509] Z[#<zscale>*-11.324]",
|
||||
];
|
||||
|
||||
export function createSimulationStore(seed = {}) {
|
||||
const seedAxisPose = seed.axisPose || initialAxisPose;
|
||||
const seedKinsType = seed.kinsType || initialState.kinsType;
|
||||
const seedRtcpState = seed.rtcpState || initialState.rtcpState;
|
||||
const seedFrame = buildRtcpFrame({
|
||||
axisPose: seedAxisPose,
|
||||
activeLine: seed.activeLine || initialState.activeLine,
|
||||
kinsType: seedKinsType,
|
||||
rtcpEnabled: seedRtcpState === "on" || seedKinsType === "tcp-xyzac",
|
||||
sourceMode: seed.sourceMode || seed.frameSourceMode || initialState.sourceMode,
|
||||
});
|
||||
let state = {
|
||||
...initialState,
|
||||
...seed,
|
||||
axisPose: seedFrame.axisPose,
|
||||
jointPose: seedFrame.jointPose,
|
||||
tcpPose: seedFrame.tcpPose,
|
||||
toolAxisVector: seedFrame.toolAxisVector,
|
||||
rtcpState: seedFrame.rtcpState,
|
||||
rtcpFrame: seedFrame,
|
||||
dro: buildDroFromFrame(seedFrame),
|
||||
programLines: seed.programLines || programLines,
|
||||
};
|
||||
const listeners = new Set();
|
||||
|
||||
const notify = () => {
|
||||
for (const listener of listeners) {
|
||||
listener(state);
|
||||
}
|
||||
};
|
||||
|
||||
const setState = (patch) => {
|
||||
const next = { ...state, ...patch };
|
||||
const frameState = buildFrameForState(next, patch);
|
||||
const frame = patch.rtcpFrame || frameState.frame;
|
||||
state = {
|
||||
...next,
|
||||
sourceMode: frame.sourceMode,
|
||||
frameSourceMode: frame.sourceMode,
|
||||
axisPose: frame.axisPose,
|
||||
jointPose: frame.jointPose,
|
||||
tcpPose: frame.tcpPose,
|
||||
toolAxisVector: frame.toolAxisVector,
|
||||
rtcpState: frame.rtcpState,
|
||||
rtcpFrame: frame,
|
||||
lastKinematicsResult: frameState.lastKinematicsResult,
|
||||
dro: buildDroFromFrame(frame),
|
||||
};
|
||||
notify();
|
||||
};
|
||||
|
||||
const dispatch = (action) => {
|
||||
switch (action.type) {
|
||||
case "BOOT_READY":
|
||||
setState({ bootReady: true });
|
||||
break;
|
||||
case "ATTACH_KINEMATICS_RUNTIME":
|
||||
{
|
||||
const runtime = action.runtime || null;
|
||||
const readiness = runtime?.readiness ? runtime.readiness() : null;
|
||||
const runtimeDescriptor = runtime?.loaded
|
||||
? {
|
||||
apiName: runtime.apiName,
|
||||
moduleId: runtime.moduleId,
|
||||
wasmFile: runtime.wasmFile,
|
||||
supportedModules: runtime.supportedModules,
|
||||
loaded: runtime.loaded,
|
||||
sourceMode: runtime.sourceMode,
|
||||
semanticBoundary: runtime.semanticBoundary,
|
||||
switchkinsType: runtime.switchkinsType,
|
||||
switchRc: runtime.switchRc,
|
||||
}
|
||||
: null;
|
||||
const adapter = createLinuxCncBoundaryAdapter({
|
||||
profile: state.profile,
|
||||
runtime: {
|
||||
kinematicsWasm: runtimeDescriptor,
|
||||
interpreterWasm: null,
|
||||
},
|
||||
});
|
||||
setState({
|
||||
kinematicsRuntime: runtime,
|
||||
kinematicsRuntimeReadiness: readiness,
|
||||
linuxCncBoundaryAdapter: adapter,
|
||||
linuxCncBoundaryReadiness: createLinuxCncBoundaryReadiness(adapter),
|
||||
sourceMode: runtime?.loaded ? "source-derived-kinematics-wasm" : "fixture-ui-only",
|
||||
frameSourceMode: runtime?.loaded ? "source-derived-kinematics-wasm" : "fixture-ui-only",
|
||||
operatorMessage: runtime?.loaded
|
||||
? `LinuxCNC kinematics ${runtime.moduleId} ready`
|
||||
: "LinuxCNC kinematics runtime missing",
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "SET_FRAME_SOURCE":
|
||||
setState({
|
||||
sourceMode: action.sourceMode,
|
||||
frameSourceMode: action.sourceMode,
|
||||
operatorMessage: `frame source ${action.sourceMode}`,
|
||||
});
|
||||
break;
|
||||
case "REFRESH_KINEMATICS_FRAME":
|
||||
setState({
|
||||
sourceMode: "source-derived-kinematics-wasm",
|
||||
frameSourceMode: "source-derived-kinematics-wasm",
|
||||
operatorMessage: "LinuxCNC kinematics frame refreshed",
|
||||
});
|
||||
break;
|
||||
case "TOGGLE_POWER":
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: !state.machine.powerOn,
|
||||
},
|
||||
runState: state.machine.powerOn ? "idle" : "powered-off",
|
||||
operatorMessage: state.machine.powerOn ? "machine power off" : "machine power on",
|
||||
});
|
||||
break;
|
||||
case "ESTOP":
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
powerOn: false,
|
||||
estopActive: true,
|
||||
},
|
||||
runState: "estopped",
|
||||
feed: {
|
||||
...state.feed,
|
||||
currentVelocity: 0,
|
||||
},
|
||||
operatorMessage: "emergency stop active",
|
||||
});
|
||||
break;
|
||||
case "RESET":
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
estopActive: false,
|
||||
resetCount: state.machine.resetCount + 1,
|
||||
},
|
||||
runState: "idle",
|
||||
operatorMessage: "machine reset complete",
|
||||
});
|
||||
break;
|
||||
case "SET_MODE":
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: action.mode,
|
||||
},
|
||||
runState: state.runState === "running" ? "paused" : state.runState,
|
||||
operatorMessage: `mode ${action.mode}`,
|
||||
});
|
||||
break;
|
||||
case "JOG":
|
||||
if (!canMoveMachine(state)) {
|
||||
setState({ operatorMessage: "jog blocked: power or estop state" });
|
||||
break;
|
||||
}
|
||||
{
|
||||
const axis = action.axis || state.machine.jogAxis;
|
||||
const direction = Number(action.direction || 1);
|
||||
const increment = Number(action.increment || state.machine.jogIncrement);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "jog",
|
||||
jogAxis: axis,
|
||||
jogIncrement: increment,
|
||||
},
|
||||
axisPose: {
|
||||
...state.axisPose,
|
||||
[axis]: Number(state.axisPose[axis] || 0) + direction * increment,
|
||||
},
|
||||
runState: "jogging",
|
||||
operatorMessage: `jog ${axis.toUpperCase()} ${direction > 0 ? "+" : "-"}${increment}`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "RUN_MDI":
|
||||
if (!canMoveMachine(state)) {
|
||||
setState({ operatorMessage: "MDI blocked: power or estop state" });
|
||||
break;
|
||||
}
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "mdi",
|
||||
mdiCommand: action.command || state.machine.mdiCommand,
|
||||
},
|
||||
runState: "mdi",
|
||||
operatorMessage: `MDI ${action.command || state.machine.mdiCommand}`,
|
||||
});
|
||||
break;
|
||||
case "LOAD_PROGRAM":
|
||||
{
|
||||
const loadedProgram = buildLoadedProgram(action);
|
||||
setState({
|
||||
...loadedProgram,
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
},
|
||||
axisPose: initialAxisPose,
|
||||
runState: "idle",
|
||||
preview: {
|
||||
...state.preview,
|
||||
pathPoints: Math.max(loadedProgram.programLines.length, 1),
|
||||
},
|
||||
operatorMessage: `loaded ${loadedProgram.activeProgram}`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "RUN":
|
||||
if (!canMoveMachine(state)) {
|
||||
setState({ operatorMessage: "run blocked: power or estop state" });
|
||||
break;
|
||||
}
|
||||
{
|
||||
const nextLine = getNextProgramLine(state, 5);
|
||||
const nextAxisPose = buildFixtureAxisPoseForLine(state.axisPose, nextLine);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
},
|
||||
runState: nextLine >= getProgramEndLine(state) ? "complete" : "running",
|
||||
activeLine: nextLine,
|
||||
axisPose: nextAxisPose,
|
||||
operatorMessage: `executing line ${nextLine}`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "STOP":
|
||||
setState({
|
||||
runState: "stopped",
|
||||
operatorMessage: "program stopped",
|
||||
});
|
||||
break;
|
||||
case "PAUSE":
|
||||
setState({ runState: "paused", operatorMessage: "program paused" });
|
||||
break;
|
||||
case "STEP":
|
||||
if (!canMoveMachine(state)) {
|
||||
setState({ operatorMessage: "step blocked: power or estop state" });
|
||||
break;
|
||||
}
|
||||
{
|
||||
const nextLine = getNextProgramLine(state, 1);
|
||||
const nextAxisPose = buildFixtureAxisPoseForLine(state.axisPose, nextLine);
|
||||
setState({
|
||||
machine: {
|
||||
...state.machine,
|
||||
mode: "auto",
|
||||
},
|
||||
runState: "stepping",
|
||||
activeLine: nextLine,
|
||||
axisPose: nextAxisPose,
|
||||
operatorMessage: `stepped to line ${nextLine}`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "RUN_FRAME":
|
||||
if (!canMoveMachine(state)) {
|
||||
setState({ operatorMessage: "run frame blocked: power or estop state" });
|
||||
break;
|
||||
}
|
||||
{
|
||||
const nextLine = getNextProgramLine(state, 5);
|
||||
const nextAxisPose = buildFixtureAxisPoseForLine(state.axisPose, nextLine);
|
||||
setState({
|
||||
runState: "running",
|
||||
activeLine: nextLine,
|
||||
axisPose: nextAxisPose,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "SET_RTCP":
|
||||
setState({
|
||||
kinsType: action.enabled ? "tcp-xyzac" : "identity",
|
||||
rtcpState: action.enabled ? "on" : "off",
|
||||
});
|
||||
break;
|
||||
case "RESET_VIEW":
|
||||
setState({
|
||||
preview: { ...state.preview, selectedView: "iso" },
|
||||
operatorMessage: "preview fit to program",
|
||||
});
|
||||
break;
|
||||
case "CLEAR_PREVIEW":
|
||||
setState({
|
||||
preview: { ...state.preview, pathPoints: 0 },
|
||||
operatorMessage: "preview path cleared",
|
||||
});
|
||||
break;
|
||||
case "SET_VIEW":
|
||||
setState({
|
||||
preview: { ...state.preview, selectedView: action.view },
|
||||
operatorMessage: `preview view ${action.view}`,
|
||||
});
|
||||
break;
|
||||
case "TOGGLE_FULLSCREEN":
|
||||
setState({
|
||||
preview: { ...state.preview, fullscreen: !state.preview.fullscreen },
|
||||
operatorMessage: state.preview.fullscreen ? "fullscreen preview off" : "fullscreen preview on",
|
||||
});
|
||||
break;
|
||||
case "SET_KINS_TYPE":
|
||||
setState({
|
||||
kinsType: action.kinsType,
|
||||
rtcpState: action.kinsType === "tcp-xyzac" ? "on" : "off",
|
||||
operatorMessage: `kinematics ${action.kinsType}`,
|
||||
});
|
||||
break;
|
||||
case "ADJUST_OVERRIDE":
|
||||
setState({
|
||||
feed: {
|
||||
...state.feed,
|
||||
[`${action.target}Override`]: clampPercent(
|
||||
state.feed[`${action.target}Override`] + action.delta,
|
||||
0,
|
||||
200,
|
||||
),
|
||||
},
|
||||
operatorMessage: `${action.target} override adjusted`,
|
||||
});
|
||||
break;
|
||||
case "ADJUST_SPINDLE_OVERRIDE":
|
||||
setState({
|
||||
spindle: {
|
||||
...state.spindle,
|
||||
override: clampPercent(state.spindle.override + action.delta, 0, 150),
|
||||
},
|
||||
operatorMessage: "spindle override adjusted",
|
||||
});
|
||||
break;
|
||||
case "TOGGLE_COOLANT":
|
||||
setState({
|
||||
coolant: {
|
||||
...state.coolant,
|
||||
[action.kind]: !state.coolant[action.kind],
|
||||
},
|
||||
operatorMessage: `${action.kind} coolant toggled`,
|
||||
});
|
||||
break;
|
||||
case "HOME":
|
||||
if (!canMoveMachine(state)) {
|
||||
setState({ operatorMessage: "home blocked: power or estop state" });
|
||||
break;
|
||||
}
|
||||
setState({
|
||||
runState: "idle",
|
||||
axisPose: initialAxisPose,
|
||||
operatorMessage: "machine homed to fixture origin",
|
||||
});
|
||||
break;
|
||||
case "RELOAD_PROGRAM":
|
||||
setState({
|
||||
runState: "idle",
|
||||
activeLine: state.programStartLine === 496 ? 501 : state.programStartLine,
|
||||
axisPose: initialAxisPose,
|
||||
preview: { ...state.preview, pathPoints: Math.max(state.programLines.length, 1) },
|
||||
operatorMessage: "program reloaded",
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown action type: ${action.type}`);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getState: () => state,
|
||||
subscribe(listener) {
|
||||
listeners.add(listener);
|
||||
listener(state);
|
||||
return () => listeners.delete(listener);
|
||||
},
|
||||
dispatch,
|
||||
};
|
||||
}
|
||||
|
||||
function buildFrameForState(state, patch = {}) {
|
||||
const requestedSourceMode = state.frameSourceMode || state.sourceMode;
|
||||
let linuxCncKinematicsResult = patch.lastKinematicsResult || null;
|
||||
let sourceMode = requestedSourceMode;
|
||||
let operatorMessage = state.operatorMessage;
|
||||
|
||||
if (requestedSourceMode === "source-derived-kinematics-wasm") {
|
||||
if (state.kinematicsRuntime?.loaded) {
|
||||
linuxCncKinematicsResult = state.kinematicsRuntime.frameForJoints(
|
||||
jointsFromAxisPose(state.axisPose),
|
||||
{ jointCount: state.kinematicsRuntime.jointCount || 5 },
|
||||
);
|
||||
} else {
|
||||
sourceMode = "fixture-ui-only";
|
||||
linuxCncKinematicsResult = null;
|
||||
operatorMessage = "LinuxCNC kinematics runtime missing; using fixture frame";
|
||||
}
|
||||
}
|
||||
|
||||
const frame = buildRtcpFrame({
|
||||
axisPose: state.axisPose,
|
||||
activeLine: state.activeLine,
|
||||
kinsType: state.kinsType,
|
||||
rtcpEnabled: state.rtcpState === "on" || state.kinsType === "tcp-xyzac",
|
||||
sourceMode,
|
||||
profile: state.profile,
|
||||
linuxCncKinematicsResult,
|
||||
});
|
||||
|
||||
if (operatorMessage !== state.operatorMessage) {
|
||||
state.operatorMessage = operatorMessage;
|
||||
}
|
||||
|
||||
return {
|
||||
frame,
|
||||
lastKinematicsResult: linuxCncKinematicsResult,
|
||||
};
|
||||
}
|
||||
|
||||
function canMoveMachine(state) {
|
||||
return state.machine.powerOn && !state.machine.estopActive;
|
||||
}
|
||||
|
||||
function getProgramEndLine(state) {
|
||||
return state.programStartLine + Math.max(state.programLines.length - 1, 0);
|
||||
}
|
||||
|
||||
function getNextProgramLine(state, step) {
|
||||
return Math.min(state.activeLine + step, getProgramEndLine(state));
|
||||
}
|
||||
|
||||
function buildLoadedProgram(action) {
|
||||
const content = String(action.content || "");
|
||||
const lines = parseProgramLines(content);
|
||||
const filename = action.filename || "operator-program.ngc";
|
||||
return {
|
||||
activeProgram: filename,
|
||||
programSource: "operator-file",
|
||||
programStartLine: 1,
|
||||
activeLine: 1,
|
||||
lineCount: lines.length,
|
||||
fileSizeBytes: content.length,
|
||||
programLines: lines,
|
||||
};
|
||||
}
|
||||
|
||||
function parseProgramLines(content) {
|
||||
const lines = content
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trimEnd())
|
||||
.filter((line) => line.trim().length > 0);
|
||||
return lines.length > 0 ? lines : ["(empty program)"];
|
||||
}
|
||||
|
||||
function clampPercent(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function buildDroFromFrame(frame) {
|
||||
return {
|
||||
x: frame.axisPose.x,
|
||||
y: frame.axisPose.y,
|
||||
z: frame.axisPose.z,
|
||||
a: frame.axisPose.a,
|
||||
b: frame.axisPose.b,
|
||||
c: frame.axisPose.c,
|
||||
tcpX: frame.tcpPose.x,
|
||||
tcpY: frame.tcpPose.y,
|
||||
tcpZ: frame.tcpPose.z,
|
||||
dtgX: frame.rtcpEnabled ? Math.abs(frame.compensation.x) : 0,
|
||||
dtgY: frame.rtcpEnabled ? Math.abs(frame.compensation.y) : 0.01,
|
||||
dtgZ: frame.rtcpEnabled ? Math.abs(frame.compensation.z) : 2.25,
|
||||
};
|
||||
}
|
||||
|
||||
function jointsFromAxisPose(axisPose) {
|
||||
return [
|
||||
Number(axisPose.x || 0),
|
||||
Number(axisPose.y || 0),
|
||||
Number(axisPose.z || 0),
|
||||
Number(axisPose.a || 0),
|
||||
Number(axisPose.c || 0),
|
||||
];
|
||||
}
|
||||
|
||||
function buildFixtureAxisPoseForLine(axisPose, line) {
|
||||
const phase = (line - 496) * 0.17;
|
||||
return {
|
||||
...axisPose,
|
||||
x: 43 + Math.sin(phase) * 4,
|
||||
y: -32.15 + Math.cos(phase) * 2.5,
|
||||
z: -11.306 + Math.sin(phase * 0.7) * 1.2,
|
||||
a: Math.sin(phase * 0.45) * 18,
|
||||
c: Math.cos(phase * 0.33) * 32,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user