refactor: extract matrix, moveMapping, easing, cubeProjection utils from SmartCube

This commit is contained in:
2026-02-24 13:40:20 +00:00
parent b71594d0ab
commit 9b02b1d9d6
5 changed files with 340 additions and 329 deletions

133
src/utils/matrix.js Normal file
View File

@@ -0,0 +1,133 @@
// 4x4 matrix operations for 3D transformations (column-major, CSS/WebGL convention)
export const identityMatrix = () => [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
export const rotateXMatrix = (deg) => {
const rad = (deg * Math.PI) / 180;
const c = Math.cos(rad);
const s = Math.sin(rad);
return [
1, 0, 0, 0,
0, c, s, 0,
0, -s, c, 0,
0, 0, 0, 1
];
};
export const rotateYMatrix = (deg) => {
const rad = (deg * Math.PI) / 180;
const c = Math.cos(rad);
const s = Math.sin(rad);
return [
c, 0, -s, 0,
0, 1, 0, 0,
s, 0, c, 0,
0, 0, 0, 1
];
};
export const rotateZMatrix = (deg) => {
const rad = (deg * Math.PI) / 180;
const c = Math.cos(rad);
const s = Math.sin(rad);
return [
c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
};
export const multiplyMatrices = (a, b) => {
const result = new Array(16).fill(0);
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
for (let k = 0; k < 4; k++) {
result[c * 4 + r] += a[k * 4 + r] * b[c * 4 + k];
}
}
}
return result;
};
// --- Quaternion helpers for distortion-free rotation interpolation (SLERP) ---
export const matToQuat = (m) => {
const trace = m[0] + m[5] + m[10];
let w, x, y, z;
if (trace > 0) {
const s = 0.5 / Math.sqrt(trace + 1);
w = 0.25 / s;
x = (m[6] - m[9]) * s;
y = (m[8] - m[2]) * s;
z = (m[1] - m[4]) * s;
} else if (m[0] > m[5] && m[0] > m[10]) {
const s = 2 * Math.sqrt(1 + m[0] - m[5] - m[10]);
w = (m[6] - m[9]) / s;
x = 0.25 * s;
y = (m[4] + m[1]) / s;
z = (m[8] + m[2]) / s;
} else if (m[5] > m[10]) {
const s = 2 * Math.sqrt(1 + m[5] - m[0] - m[10]);
w = (m[8] - m[2]) / s;
x = (m[4] + m[1]) / s;
y = 0.25 * s;
z = (m[6] + m[9]) / s;
} else {
const s = 2 * Math.sqrt(1 + m[10] - m[0] - m[5]);
w = (m[1] - m[4]) / s;
x = (m[8] + m[2]) / s;
y = (m[6] + m[9]) / s;
z = 0.25 * s;
}
return { w, x, y, z };
};
export const slerp = (q1, q2, t) => {
let dot = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
let q2n = q2;
if (dot < 0) {
q2n = { w: -q2.w, x: -q2.x, y: -q2.y, z: -q2.z };
dot = -dot;
}
if (dot > 0.9995) {
const len = Math.sqrt(
(q1.w + t * (q2n.w - q1.w)) ** 2 + (q1.x + t * (q2n.x - q1.x)) ** 2 +
(q1.y + t * (q2n.y - q1.y)) ** 2 + (q1.z + t * (q2n.z - q1.z)) ** 2
);
return {
w: (q1.w + t * (q2n.w - q1.w)) / len,
x: (q1.x + t * (q2n.x - q1.x)) / len,
y: (q1.y + t * (q2n.y - q1.y)) / len,
z: (q1.z + t * (q2n.z - q1.z)) / len,
};
}
const theta = Math.acos(dot);
const sinTheta = Math.sin(theta);
const a = Math.sin((1 - t) * theta) / sinTheta;
const b = Math.sin(t * theta) / sinTheta;
return {
w: a * q1.w + b * q2n.w,
x: a * q1.x + b * q2n.x,
y: a * q1.y + b * q2n.y,
z: a * q1.z + b * q2n.z,
};
};
export const quatToMat = (q) => {
const { w, x, y, z } = q;
const xx = x * x, yy = y * y, zz = z * z;
const xy = x * y, xz = x * z, yz = y * z;
const wx = w * x, wy = w * y, wz = w * z;
return [
1 - 2 * (yy + zz), 2 * (xy + wz), 2 * (xz - wy), 0,
2 * (xy - wz), 1 - 2 * (xx + zz), 2 * (yz + wx), 0,
2 * (xz + wy), 2 * (yz - wx), 1 - 2 * (xx + yy), 0,
0, 0, 0, 1,
];
};