@gkucmierz/utils 5.1.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -70,3 +70,167 @@ export const getRotationMatrixFromVectors = (u, v) => {
70
70
  return axisAngleToMatrix4(axis, angle);
71
71
  };
72
72
 
73
+ export const normalizeQuat = (q) => {
74
+ const len = Math.hypot(q.w, q.x, q.y, q.z);
75
+ if (len === 0) return { w: 1, x: 0, y: 0, z: 0 };
76
+ return {
77
+ w: q.w / len,
78
+ x: q.x / len,
79
+ y: q.y / len,
80
+ z: q.z / len
81
+ };
82
+ };
83
+
84
+ export const axisAngleToQuat = (axis, angle) => {
85
+ const [x, y, z] = normalize(axis);
86
+ const half = angle * 0.5;
87
+ const s = Math.sin(half);
88
+ return normalizeQuat({
89
+ w: Math.cos(half),
90
+ x: x * s,
91
+ y: y * s,
92
+ z: z * s
93
+ });
94
+ };
95
+
96
+ export const multiplyQuat = (a, b) => ({
97
+ w: a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
98
+ x: a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
99
+ y: a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
100
+ z: a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w
101
+ });
102
+
103
+ export const rotateVecByQuat = (v, q) => {
104
+ const [vx, vy, vz] = v;
105
+ const { w, x, y, z } = q;
106
+ const tx = 2 * (y * vz - z * vy);
107
+ const ty = 2 * (z * vx - x * vz);
108
+ const tz = 2 * (x * vy - y * vx);
109
+ return [
110
+ vx + w * tx + (y * tz - z * ty),
111
+ vy + w * ty + (z * tx - x * tz),
112
+ vz + w * tz + (x * ty - y * tx)
113
+ ];
114
+ };
115
+
116
+ export const matToQuat = (m) => {
117
+ const trace = m[0] + m[5] + m[10];
118
+ let w, x, y, z;
119
+ if (trace > 0) {
120
+ const s = 0.5 / Math.sqrt(trace + 1);
121
+ w = 0.25 / s;
122
+ x = (m[6] - m[9]) * s;
123
+ y = (m[8] - m[2]) * s;
124
+ z = (m[1] - m[4]) * s;
125
+ } else if (m[0] > m[5] && m[0] > m[10]) {
126
+ const s = 2 * Math.sqrt(1 + m[0] - m[5] - m[10]);
127
+ w = (m[6] - m[9]) / s;
128
+ x = 0.25 * s;
129
+ y = (m[4] + m[1]) / s;
130
+ z = (m[8] + m[2]) / s;
131
+ } else if (m[5] > m[10]) {
132
+ const s = 2 * Math.sqrt(1 + m[5] - m[0] - m[10]);
133
+ w = (m[8] - m[2]) / s;
134
+ x = (m[4] + m[1]) / s;
135
+ y = 0.25 * s;
136
+ z = (m[6] + m[9]) / s;
137
+ } else {
138
+ const s = 2 * Math.sqrt(1 + m[10] - m[0] - m[5]);
139
+ w = (m[1] - m[4]) / s;
140
+ x = (m[8] + m[2]) / s;
141
+ y = (m[6] + m[9]) / s;
142
+ z = 0.25 * s;
143
+ }
144
+ return normalizeQuat({ w, x, y, z });
145
+ };
146
+
147
+ export const quatToMat = (q) => {
148
+ const { w, x, y, z } = normalizeQuat(q);
149
+ const xx = x * x, yy = y * y, zz = z * z;
150
+ const xy = x * y, xz = x * z, yz = y * z;
151
+ const wx = w * x, wy = w * y, wz = w * z;
152
+ return [
153
+ 1 - 2 * (yy + zz), 2 * (xy + wz), 2 * (xz - wy), 0,
154
+ 2 * (xy - wz), 1 - 2 * (xx + zz), 2 * (yz + wx), 0,
155
+ 2 * (xz + wy), 2 * (yz - wx), 1 - 2 * (xx + yy), 0,
156
+ 0, 0, 0, 1
157
+ ];
158
+ };
159
+
160
+ export const slerp = (q1, q2, t) => {
161
+ let dot = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
162
+ let q2n = q2;
163
+ if (dot < 0) {
164
+ q2n = { w: -q2.w, x: -q2.x, y: -q2.y, z: -q2.z };
165
+ dot = -dot;
166
+ }
167
+ if (dot > 0.9995) {
168
+ return normalizeQuat({
169
+ w: q1.w + t * (q2n.w - q1.w),
170
+ x: q1.x + t * (q2n.x - q1.x),
171
+ y: q1.y + t * (q2n.y - q1.y),
172
+ z: q1.z + t * (q2n.z - q1.z)
173
+ });
174
+ }
175
+ const theta = Math.acos(Math.max(-1, Math.min(1, dot)));
176
+ const sinTheta = Math.sin(theta);
177
+ const a = Math.sin((1 - t) * theta) / sinTheta;
178
+ const b = Math.sin(t * theta) / sinTheta;
179
+ return {
180
+ w: a * q1.w + b * q2n.w,
181
+ x: a * q1.x + b * q2n.x,
182
+ y: a * q1.y + b * q2n.y,
183
+ z: a * q1.z + b * q2n.z
184
+ };
185
+ };
186
+
187
+ export const matToEulerYXZ = (m) => {
188
+ if (!m || m.length < 16) return { pitch: 0, yaw: 0, roll: 0 };
189
+ const m11 = m[0], m13 = m[8];
190
+ const m21 = m[1], m22 = m[5], m23 = m[9];
191
+ const m31 = m[2], m33 = m[10];
192
+
193
+ const clamp = (v) => Math.max(-1, Math.min(1, v));
194
+ const x = Math.asin(-clamp(m23));
195
+ let y, z;
196
+ if (Math.abs(m23) < 0.9999999) {
197
+ y = Math.atan2(m13, m33);
198
+ z = Math.atan2(m21, m22);
199
+ } else {
200
+ y = Math.atan2(-m31, m11);
201
+ z = 0;
202
+ }
203
+ const radToDeg = 180 / Math.PI;
204
+ return {
205
+ pitch: x * radToDeg,
206
+ yaw: y * radToDeg,
207
+ roll: z * radToDeg
208
+ };
209
+ };
210
+
211
+ export const eulerYXZToQuat = (pitchDeg, yawDeg, rollDeg) => {
212
+ const degToRad = Math.PI / 180;
213
+ const hx = (pitchDeg * degToRad) * 0.5;
214
+ const hy = (yawDeg * degToRad) * 0.5;
215
+ const hz = (rollDeg * degToRad) * 0.5;
216
+
217
+ const c1 = Math.cos(hx), s1 = Math.sin(hx);
218
+ const c2 = Math.cos(hy), s2 = Math.sin(hy);
219
+ const c3 = Math.cos(hz), s3 = Math.sin(hz);
220
+
221
+ return normalizeQuat({
222
+ w: c1 * c2 * c3 + s1 * s2 * s3,
223
+ x: s1 * c2 * c3 + c1 * s2 * s3,
224
+ y: c1 * s2 * c3 - s1 * c2 * s3,
225
+ z: c1 * c2 * s3 - s1 * s2 * c3
226
+ });
227
+ };
228
+
229
+ export const applyScreenDragQuat = (currentQuat, dx, dy, sensitivity = 0.007) => {
230
+ const qY = axisAngleToQuat([0, 1, 0], dx * sensitivity);
231
+ const qX = axisAngleToQuat([1, 0, 0], dy * sensitivity);
232
+ const qDelta = multiplyQuat(qX, qY);
233
+ return normalizeQuat(multiplyQuat(qDelta, currentQuat));
234
+ };
235
+
236
+