glmaths 0.0.1

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.
package/src/quat.ts ADDED
@@ -0,0 +1,819 @@
1
+ import glm from '.'
2
+ import { vec3, Vec3 } from './vec3'
3
+ import { vec4, Vec4 } from './vec4'
4
+ import { Mat3 } from './mat3'
5
+ import { Mat4 } from './mat4'
6
+
7
+ /**
8
+ * Quaternion for 3D rotations
9
+ * @extends Vec4
10
+ */
11
+ export class Quat extends Float32Array {
12
+
13
+ static get identity() { return new Quat(0, 0, 0, 1) }
14
+ static get Identity() { return new Quat(0, 0, 0, 1) }
15
+ static get IDENTITY() { return new Quat(0, 0, 0, 1) }
16
+
17
+ /**
18
+ * Creates a new quaternion
19
+ *
20
+ * @param {Number} x X component, defaults to 0
21
+ * @param {Number} y Y component, defaults to 0
22
+ * @param {Number} z Z component, defaults to 0
23
+ * @param {Number} w W component, defaults to 1
24
+ */
25
+ constructor(x = 0, y = 0, z = 0, w = 1) {
26
+ super(4)
27
+ this[0] = x
28
+ this[1] = y
29
+ this[2] = z
30
+ this[3] = w
31
+ }
32
+
33
+ /**
34
+ * Calculates the Hamilton product of two quaternions
35
+ *
36
+ * @param {Quat} b the second operand
37
+ * @param {Quat} out the receiving quaternion, defaults to this
38
+ * @returns {Quat} out
39
+ */
40
+ multiply(b: Quat | number, out: Quat = glm.ALWAYS_COPY ? new Quat() : this): Quat {
41
+ if (typeof b === 'number') {
42
+ out[0] = this[0] * b
43
+ out[1] = this[1] * b
44
+ out[2] = this[2] * b
45
+ out[3] = this[3] * b
46
+ return out!
47
+ }
48
+ const ax = this[0], ay = this[1], az = this[2], aw = this[3]
49
+ const bx = b[0], by = b[1], bz = b[2], bw = b[3]
50
+ out[0] = ax * bw + aw * bx + ay * bz - az * by
51
+ out[1] = ay * bw + aw * by + az * bx - ax * bz
52
+ out[2] = az * bw + aw * bz + ax * by - ay * bx
53
+ out[3] = aw * bw - ax * bx - ay * by - az * bz
54
+ return out
55
+ }
56
+
57
+ /**
58
+ * Creates a quaternion from the given axis and angle of rotation
59
+ *
60
+ * @param {Vec3} axis the axis around which to rotate
61
+ * @param {Number} rad the angle in radians
62
+ * @param {Quat} out the receiving quaternion, defaults to quat()
63
+ * @returns {Quat} out
64
+ */
65
+ static fromAxisAngle(axis: Vec3, rad: number, out = quat()): Quat {
66
+ rad *= 0.5
67
+ const s = Math.sin(rad)
68
+ out[0] = s * axis[0]
69
+ out[1] = s * axis[1]
70
+ out[2] = s * axis[2]
71
+ out[3] = Math.cos(rad)
72
+ return out
73
+ }
74
+
75
+ /**
76
+ * Sets a quaternion to the given axis and angle of rotation
77
+ *
78
+ * @param {Vec3} axis the axis around which to rotate
79
+ * @param {Number} rad the angle in radians
80
+ * @param {Quat} out the receiving quaternion, defaults to this
81
+ * @returns {Quat} out
82
+ */
83
+ setAxisAngle(axis: Vec3, rad: number, out = glm.ALWAYS_COPY ? quat() : this): Quat {
84
+ rad *= 0.5
85
+ const s = Math.sin(rad)
86
+ out[0] = s * axis[0]
87
+ out[1] = s * axis[1]
88
+ out[2] = s * axis[2]
89
+ out[3] = Math.cos(rad)
90
+ return out
91
+ }
92
+
93
+ /**
94
+ * Gets the rotation axis and angle for a given
95
+ * quaternion. If a quaternion is created with
96
+ * setAxisAngle, this method will return the same
97
+ * values as providied in the original parameter list
98
+ * OR functionally equivalent values.
99
+ * Example: The quaternion formed by axis [0, 0, 1] and
100
+ * angle -90 is the same as the quaternion formed by
101
+ * [0, 0, 1] and 270. This method favors the latter.
102
+ * @param {Vec3} out_axis axis to return of the rotation
103
+ * @returns {Number} angle, in radians, of the rotation
104
+ */
105
+ getAxisAngle(out_axis: Vec3) {
106
+ const rad = Math.acos(this[3]) * 2.0
107
+ const s = Math.sin(rad / 2.0)
108
+ if (out_axis) {
109
+ if (s > glm.EPSILON) {
110
+ out_axis[0] = this[0] / s
111
+ out_axis[1] = this[1] / s
112
+ out_axis[2] = this[2] / s
113
+ } else {
114
+ out_axis[0] = 1
115
+ out_axis[1] = out_axis[2] = 0
116
+ }
117
+ }
118
+ return rad
119
+ }
120
+
121
+ /**
122
+ * Gets the angular distance between two unit quaternions
123
+ *
124
+ * @param {Quat} a Origin unit quaternion
125
+ * @param {Quat} b Destination unit quaternion
126
+ * @returns {Number} Angle, in radians, between the two quaternions
127
+ */
128
+ static angle(a: Quat, b: Quat) {
129
+ const dotproduct = Quat.dot(a, b)
130
+ return Math.acos(2 * dotproduct * dotproduct - 1)
131
+ }
132
+
133
+ /**
134
+ * Gets the angular distance between two unit quaternions
135
+ *
136
+ * @param {Quat} a Origin unit quaternion
137
+ * @param {Quat} b Destination unit quaternion
138
+ * @return {Number} Angle, in radians, between the two quaternions
139
+ */
140
+ static getAngle: (a: Quat, b: Quat) => number
141
+
142
+ /**
143
+ * Rotates a quaternion by the given angle about the X axis
144
+ *
145
+ * @param {Number} rad angle in radians to rotate
146
+ * @param {Quat} out the receiving quaternion, defaults to this
147
+ * @returns {Quat} out
148
+ */
149
+ rotateX(rad: number, out = glm.ALWAYS_COPY ? quat() : this) {
150
+ rad *= 0.5
151
+ const ax = this[0], ay = this[1], az = this[2], aw = this[3]
152
+ const bx = Math.sin(rad), bw = Math.cos(rad)
153
+ out[0] = ax * bw + aw * bx
154
+ out[1] = ay * bw + az * bx
155
+ out[2] = az * bw - ay * bx
156
+ out[3] = aw * bw - ax * bx
157
+ return out
158
+ }
159
+
160
+ /**
161
+ * Rotates a quaternion by the given angle about the Y axis
162
+ *
163
+ * @param {Number} rad angle in radians to rotate
164
+ * @param {Quat} out the receiving quaternion, defaults to this
165
+ * @returns {Quat} out
166
+ */
167
+ rotateY(rad: number, out = glm.ALWAYS_COPY ? quat() : this) {
168
+ rad *= 0.5
169
+ const ax = this[0], ay = this[1], az = this[2], aw = this[3]
170
+ const by = Math.sin(rad), bw = Math.cos(rad)
171
+ out[0] = ax * bw - az * by
172
+ out[1] = ay * bw + aw * by
173
+ out[2] = az * bw + ax * by
174
+ out[3] = aw * bw - ay * by
175
+ return out
176
+ }
177
+ /**
178
+ * Rotates a quaternion by the given angle about the Z axis
179
+ *
180
+ * @param {Number} rad angle in radians to rotate
181
+ * @param {Quat} out the receiving quaternion, defaults to this
182
+ * @returns {Quat} out
183
+ */
184
+ rotateZ(rad: number, out = glm.ALWAYS_COPY ? quat() : this) {
185
+ rad *= 0.5
186
+ const ax = this[0], ay = this[1], az = this[2], aw = this[3]
187
+ const bz = Math.sin(rad), bw = Math.cos(rad)
188
+ out[0] = ax * bw + ay * bz;
189
+ out[1] = ay * bw - ax * bz;
190
+ out[2] = az * bw + aw * bz;
191
+ out[3] = aw * bw - az * bz;
192
+ return out
193
+ }
194
+
195
+ /**
196
+ * Calculates the W component of a quaternion from the X, Y, and Z components
197
+ *
198
+ * @returns {Number} the W component
199
+ */
200
+ calculateW() {
201
+ const x = this[0], y = this[1], z = this[2]
202
+ return Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z))
203
+ }
204
+
205
+ /**
206
+ * Calculates the exponential of a unit quaternion
207
+ *
208
+ * @param {Quat} q the quaternion to exponentiate
209
+ * @param {Quat} out the receiving quaternion, defaults to quat()
210
+ * @returns {Quat} out
211
+ */
212
+ static exp(q: Quat, out = quat()): Quat {
213
+ const x = q[0], y = q[1], z = q[2], w = q[3]
214
+ const r = Math.sqrt(x * x + y * y + z * z)
215
+ const et = Math.exp(w)
216
+ const s = r > 0 ? (et * Math.sin(r)) / r : 0
217
+ out[0] = x * s
218
+ out[1] = y * s
219
+ out[2] = z * s
220
+ out[3] = et * Math.cos(r)
221
+ return out
222
+ }
223
+ /**
224
+ * Calculates the exponential of the unit quaternion
225
+ *
226
+ * @param {Quat} out the receiving quaternion, defaults to this
227
+ * @returns {Quat} out
228
+ */
229
+ exp(out = glm.ALWAYS_COPY ? quat() : this): Quat {
230
+ const x = this[0], y = this[1], z = this[2], w = this[3]
231
+ const r = Math.sqrt(x * x + y * y + z * z)
232
+ const et = Math.exp(w)
233
+ const s = r > 0 ? (et * Math.sin(r)) / r : 0
234
+ out[0] = x * s
235
+ out[1] = y * s
236
+ out[2] = z * s
237
+ out[3] = et * Math.cos(r)
238
+ return out
239
+ }
240
+
241
+ /**
242
+ * Calculates the natural logarithm of a unit quaternion
243
+ *
244
+ * @param {Quat} q the quaternion to take the logarithm of
245
+ * @param {Quat} out the receiving quaternion, defaults to quat()
246
+ * @returns {Quat} out
247
+ */
248
+ static ln(q: Quat, out = quat()): Quat {
249
+ const x = q[0], y = q[1], z = q[2], w = q[3]
250
+ const r = Math.sqrt(x * x + y * y + z * z)
251
+ const t = r > 0 ? Math.atan2(r, w) / r : 0
252
+ out[0] = x * t
253
+ out[1] = y * t
254
+ out[2] = z * t
255
+ out[3] = 0.5 * Math.log(x * x + y * y + z * z + w * w)
256
+ return out
257
+ }
258
+ /**
259
+ * Calculates the natural logarithm of the unit quaternion
260
+ *
261
+ * @param {Quat} out the receiving quaternion, defaults to this
262
+ * @returns {Quat} out
263
+ */
264
+ ln(out = glm.ALWAYS_COPY ? quat() : this): Quat {
265
+ const x = this[0], y = this[1], z = this[2], w = this[3]
266
+ const r = Math.sqrt(x * x + y * y + z * z)
267
+ const t = r > 0 ? Math.atan2(r, w) / r : 0
268
+ out[0] = x * t
269
+ out[1] = y * t
270
+ out[2] = z * t
271
+ out[3] = 0.5 * Math.log(x * x + y * y + z * z + w * w)
272
+ return out
273
+ }
274
+
275
+ /**
276
+ * Raises a quaternion to a scalar power
277
+ *
278
+ * @param {Number} b the power to raise the quaternion to
279
+ * @returns {Quat} this
280
+ */
281
+ pow(b: number) {
282
+ this.ln()
283
+ this.scale(b)
284
+ this.exp()
285
+ return this
286
+ }
287
+
288
+ /**
289
+ * Performs a spherical linear interpolation between two quaternions
290
+ *
291
+ * @param {Quat} a the first operand
292
+ * @param {Quat} b the second operand
293
+ * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
294
+ * @param {Quat} out the receiving quaternion, defaults to quat()
295
+ * @returns {Quat} out
296
+ */
297
+ static slerp(a: Quat, b: Quat, t: number, out = quat()): Quat {
298
+ let ax = a[0], ay = a[1], az = a[2], aw = a[3]
299
+ let bx = b[0], by = b[1], bz = b[2], bw = b[3]
300
+
301
+ let omega, cosom, sinom, scale0, scale1;
302
+ // calc cosine
303
+ cosom = ax * bx + ay * by + az * bz + aw * bw;
304
+ // adjust signs (if necessary)
305
+ if (cosom < 0.0) {
306
+ cosom = -cosom;
307
+ bx = -bx;
308
+ by = -by;
309
+ bz = -bz;
310
+ bw = -bw;
311
+ }
312
+ // calculate coefficients
313
+ if (1.0 - cosom > glm.EPSILON) {
314
+ // standard case (slerp)
315
+ omega = Math.acos(cosom);
316
+ sinom = Math.sin(omega);
317
+ scale0 = Math.sin((1.0 - t) * omega) / sinom;
318
+ scale1 = Math.sin(t * omega) / sinom;
319
+ } else {
320
+ // "from" and "to" quaternions are very close
321
+ // ... so we can do a linear interpolation
322
+ scale0 = 1.0 - t;
323
+ scale1 = t;
324
+ }
325
+ // calculate final values
326
+ out[0] = scale0 * ax + scale1 * bx
327
+ out[1] = scale0 * ay + scale1 * by
328
+ out[2] = scale0 * az + scale1 * bz
329
+ out[3] = scale0 * aw + scale1 * bw
330
+ return out
331
+ }
332
+ /**
333
+ * Performs a spherical linear interpolation between a quaternion and b
334
+ *
335
+ * @param {Quat} b the second operand
336
+ * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
337
+ * @param {Quat} out the receiving quaternion, defaults to this
338
+ * @returns {Quat} out
339
+ */
340
+ slerp(b: Quat, t: number, out = glm.ALWAYS_COPY ? quat() : this): Quat {
341
+ let ax = this[0], ay = this[1], az = this[2], aw = this[3]
342
+ let bx = b[0], by = b[1], bz = b[2], bw = b[3]
343
+
344
+ let omega, cosom, sinom, scale0, scale1;
345
+ // calc cosine
346
+ cosom = ax * bx + ay * by + az * bz + aw * bw;
347
+ // adjust signs (if necessary)
348
+ if (cosom < 0.0) {
349
+ cosom = -cosom;
350
+ bx = -bx;
351
+ by = -by;
352
+ bz = -bz;
353
+ bw = -bw;
354
+ }
355
+ // calculate coefficients
356
+ if (1.0 - cosom > glm.EPSILON) {
357
+ // standard case (slerp)
358
+ omega = Math.acos(cosom);
359
+ sinom = Math.sin(omega);
360
+ scale0 = Math.sin((1.0 - t) * omega) / sinom;
361
+ scale1 = Math.sin(t * omega) / sinom;
362
+ } else {
363
+ // "from" and "to" quaternions are very close
364
+ // ... so we can do a linear interpolation
365
+ scale0 = 1.0 - t;
366
+ scale1 = t;
367
+ }
368
+ // calculate final values
369
+ out[0] = scale0 * ax + scale1 * bx
370
+ out[1] = scale0 * ay + scale1 * by
371
+ out[2] = scale0 * az + scale1 * bz
372
+ out[3] = scale0 * aw + scale1 * bw
373
+ return out
374
+ }
375
+
376
+ /**
377
+ * Generates a random unit quaternion
378
+ *
379
+ * @param {Quat} out the receiving quaternion, defaults to quat()
380
+ * @returns {Quat} out
381
+ */
382
+ static random(out = quat()) {
383
+ // Implementation of http://planning.cs.uiuc.edu/node198.html
384
+ // TODO: Calling random 3 times is probably not the fastest solution
385
+ let u1 = glm.RANDOM()
386
+ let u2 = glm.RANDOM()
387
+ let u3 = glm.RANDOM()
388
+ let sqrt1MinusU1 = Math.sqrt(1 - u1)
389
+ let sqrtU1 = Math.sqrt(u1)
390
+ out[0] = sqrt1MinusU1 * Math.sin(2.0 * Math.PI * u2)
391
+ out[1] = sqrt1MinusU1 * Math.cos(2.0 * Math.PI * u2)
392
+ out[2] = sqrtU1 * Math.sin(2.0 * Math.PI * u3)
393
+ out[3] = sqrtU1 * Math.cos(2.0 * Math.PI * u3)
394
+ return out
395
+ }
396
+
397
+ /**
398
+ * Calculates the inverse of a quaternion
399
+ *
400
+ * @param {Quat} q the source quaternion
401
+ * @param {Quat} out the receiving quaternion, defaults to quat()
402
+ * @returns {Quat} out
403
+ */
404
+ static invert(q: Quat, out = quat()) {
405
+ const a0 = q[0], a1 = q[1], a2 = q[2], a3 = q[3]
406
+ const dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
407
+ const invDot = dot ? 1.0 / dot : 0;
408
+ // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
409
+ out[0] = -a0 * invDot
410
+ out[1] = -a1 * invDot
411
+ out[2] = -a2 * invDot
412
+ out[3] = a3 * invDot
413
+ return out
414
+ }
415
+ /**
416
+ * Calculates the inverse of a quaternion
417
+ *
418
+ * @param {Quat} out the receiving quaternion, defaults to this
419
+ * @returns {Quat} out
420
+ */
421
+ invert(out = glm.ALWAYS_COPY ? quat() : this) {
422
+ const a0 = this[0], a1 = this[1], a2 = this[2], a3 = this[3]
423
+ const dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
424
+ const invDot = dot ? 1.0 / dot : 0;
425
+ // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
426
+ out[0] = -a0 * invDot
427
+ out[1] = -a1 * invDot
428
+ out[2] = -a2 * invDot
429
+ out[3] = a3 * invDot
430
+ return out
431
+ }
432
+
433
+ /**
434
+ * Calculates the conjugate of a quaternion
435
+ *
436
+ * @param {Quat} q the source quaternion
437
+ * @param {Quat} out the receiving quaternion, defaults to quat()
438
+ * @returns {Quat} out
439
+ */
440
+ static conjugate(q: Quat, out = quat()): Quat {
441
+ out[0] = -q[0]
442
+ out[1] = -q[1]
443
+ out[2] = -q[2]
444
+ out[3] = q[3]
445
+ return out
446
+ }
447
+ /**
448
+ * Calculates the conjugate of a quaternion
449
+ *
450
+ * @param {Quat} out the receiving quaternion, defaults to this
451
+ * @returns {Quat} out
452
+ */
453
+ conjugate(out = glm.ALWAYS_COPY ? quat() : this): Quat {
454
+ out[0] = -this[0]
455
+ out[1] = -this[1]
456
+ out[2] = -this[2]
457
+ out[3] = this[3]
458
+ return out
459
+ }
460
+
461
+ /**
462
+ * Creates a quaternion from the given 3x3 rotation matrix
463
+ *
464
+ * @param {Mat3} m the rotation matrix
465
+ * @param {Quat} out the receiving quaternion, defaults to quat()
466
+ * @returns {Quat} out
467
+ */
468
+ static fromMat3(m: Mat3, out = quat()): Quat {
469
+ // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
470
+ // article "Quaternion Calculus and Fast Animation".
471
+ const fTrace = m[0] + m[4] + m[8]
472
+ let fRoot
473
+ if (fTrace > 0.0) {
474
+ // |w| > 1/2, may as well choose w > 1/2
475
+ fRoot = Math.sqrt(fTrace + 1.0); // 2w
476
+ out[3] = 0.5 * fRoot
477
+ fRoot = 0.5 / fRoot // 1/(4w)
478
+ out[0] = (m[5] - m[7]) * fRoot
479
+ out[1] = (m[6] - m[2]) * fRoot
480
+ out[2] = (m[1] - m[3]) * fRoot
481
+ } else {
482
+ // |w| <= 1/2
483
+ let i = 0
484
+ if (m[4] > m[0]) i = 1
485
+ if (m[8] > m[i * 3 + i]) i = 2
486
+ let j = (i + 1) % 3
487
+ let k = (i + 2) % 3
488
+ fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0)
489
+ out[i] = 0.5 * fRoot
490
+ fRoot = 0.5 / fRoot
491
+ out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot
492
+ out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot
493
+ out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;
494
+ }
495
+ return out;
496
+ }
497
+ /**
498
+ * Creates a quaternion from the given Euler angle x, y, z using the given order
499
+ *
500
+ * @param {Number} x rotation around X axis in degrees
501
+ * @param {Number} y rotation around Y axis in degrees
502
+ * @param {Number} z rotation around Z axis in degrees
503
+ * @param {String} order angle order, defaults to ANGLE_ORDER
504
+ * @param {Quat} out the receiving quaternion, defaults to quat()
505
+ * @returns {Quat} out
506
+ */
507
+ static fromEuler(x: number, y: number, z: number, order = glm.ANGLE_ORDER, out = quat()) {
508
+ let halfToRad = Math.PI / 360
509
+ x *= halfToRad
510
+ z *= halfToRad
511
+ y *= halfToRad
512
+ let sx = Math.sin(x)
513
+ let cx = Math.cos(x)
514
+ let sy = Math.sin(y)
515
+ let cy = Math.cos(y)
516
+ let sz = Math.sin(z)
517
+ let cz = Math.cos(z)
518
+ switch (order) {
519
+ case "xyz":
520
+ out[0] = sx * cy * cz + cx * sy * sz;
521
+ out[1] = cx * sy * cz - sx * cy * sz;
522
+ out[2] = cx * cy * sz + sx * sy * cz;
523
+ out[3] = cx * cy * cz - sx * sy * sz;
524
+ break;
525
+ case "xzy":
526
+ out[0] = sx * cy * cz - cx * sy * sz;
527
+ out[1] = cx * sy * cz - sx * cy * sz;
528
+ out[2] = cx * cy * sz + sx * sy * cz;
529
+ out[3] = cx * cy * cz + sx * sy * sz;
530
+ break;
531
+ case "yxz":
532
+ out[0] = sx * cy * cz + cx * sy * sz;
533
+ out[1] = cx * sy * cz - sx * cy * sz;
534
+ out[2] = cx * cy * sz - sx * sy * cz;
535
+ out[3] = cx * cy * cz + sx * sy * sz;
536
+ break;
537
+ case "yzx":
538
+ out[0] = sx * cy * cz + cx * sy * sz;
539
+ out[1] = cx * sy * cz + sx * cy * sz;
540
+ out[2] = cx * cy * sz - sx * sy * cz;
541
+ out[3] = cx * cy * cz - sx * sy * sz;
542
+ break;
543
+ case "zxy":
544
+ out[0] = sx * cy * cz - cx * sy * sz;
545
+ out[1] = cx * sy * cz + sx * cy * sz;
546
+ out[2] = cx * cy * sz + sx * sy * cz;
547
+ out[3] = cx * cy * cz - sx * sy * sz;
548
+ break;
549
+ case "zyx":
550
+ out[0] = sx * cy * cz - cx * sy * sz;
551
+ out[1] = cx * sy * cz + sx * cy * sz;
552
+ out[2] = cx * cy * sz - sx * sy * cz;
553
+ out[3] = cx * cy * cz + sx * sy * sz;
554
+ break;
555
+ default:
556
+ throw new Error('Unknown angle order ' + order);
557
+ }
558
+ return out
559
+ }
560
+
561
+ /**
562
+ * Returns a string representation of a quaternion
563
+ *
564
+ * @returns {String} string representation of the quaternion
565
+ */
566
+ toString() {
567
+ return `quat(${this[0]}, ${this[1]}, ${this[2]}, ${this[3]})`
568
+ }
569
+
570
+ /**
571
+ * Returns dot product of two quaternions
572
+ *
573
+ * @param {Quat} a the first quaternion
574
+ * @param {Quat} b the second quaternion
575
+ * @returns {Number} the dot product
576
+ */
577
+ static dot(a: Quat, b: Quat): number {
578
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
579
+ }
580
+
581
+ /**
582
+ * Returns dot product of this and other quaternion
583
+ *
584
+ * @param {Quat} a the first quaternion
585
+ * @param {Quat} b the second quaternion
586
+ * @returns {Number} the dot product
587
+ */
588
+ dot(b: Quat): number {
589
+ return this[0] * b[0] + this[1] * b[1] + this[2] * b[2] + this[3] * b[3]
590
+ }
591
+
592
+ /**
593
+ * Returns whether two quaternions represent the same rotation
594
+ *
595
+ * @param {Quat} b the second operand
596
+ * @returns {Boolean} true if the quaternions represent the same rotation
597
+ */
598
+ equals(b: Quat) {
599
+ return Math.abs(Quat.dot(this, b)) >= 1 - glm.EPSILON
600
+ }
601
+
602
+ private static tmpVec3 = vec3()
603
+ /**
604
+ * Sets a quaternion to represent the shortest rotation from one vector to another
605
+ *
606
+ * @param {Vec3} a the initial vector
607
+ * @param {Vec3} b the destination vector
608
+ * @param {Quat} out the receiving quaternion, defaults to quat()
609
+ * @returns {Quat} out
610
+ */
611
+ static rotationTo(a: Vec3, b: Vec3, out = quat()): Quat {
612
+ const dot = vec3.dot(a, b)
613
+ if (dot < -0.999999) {
614
+ vec3.cross(vec3.unitX, a, Quat.tmpVec3)
615
+ if (Quat.tmpVec3.len() < 0.000001)
616
+ vec3.cross(vec3.unitY, a, Quat.tmpVec3)
617
+ return this.fromAxisAngle(Quat.tmpVec3.normalize(), Math.PI, out)
618
+ } else if (dot > 0.999999) {
619
+ out[0] = out[1] = out[2] = 0
620
+ out[3] = 1
621
+ return out
622
+ } else {
623
+ vec3.cross(a, b, Quat.tmpVec3)
624
+ out[0] = Quat.tmpVec3[0]
625
+ out[1] = Quat.tmpVec3[1]
626
+ out[2] = Quat.tmpVec3[2]
627
+ out[3] = 1 + dot
628
+ return out.normalize(out)
629
+ }
630
+ }
631
+
632
+ private static tmp1 = new Quat()
633
+ private static tmp2 = new Quat()
634
+ /**
635
+ * Performs a spherical linear interpolation with two control points
636
+ *
637
+ * @param {Quat} a the first operand
638
+ * @param {Quat} b the second operand
639
+ * @param {Quat} c the third operand
640
+ * @param {Quat} d the fourth operand
641
+ * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
642
+ * @param {Quat} out the receiving quaternion, defaults to quat()
643
+ * @returns {Quat} out
644
+ */
645
+ static sqlerp(a: Quat, b: Quat, c: Quat, d: Quat, t: number, out = quat()): Quat {
646
+ Quat.slerp(a, d, t, Quat.tmp1)
647
+ Quat.slerp(b, c, t, Quat.tmp2)
648
+ Quat.slerp(Quat.tmp1, Quat.tmp2, 2 * t * (1 - t), out)
649
+ return out
650
+ }
651
+
652
+ private static tmpMat3 = new Mat3()
653
+ /**
654
+ * Sets the specified quaternion with values corresponding to the given axes
655
+ *
656
+ * @param {Vec3} view the vector representing the viewing direction
657
+ * @param {Vec3} right the vector representing the local right direction
658
+ * @param {Vec3} up the vector representing the local up direction
659
+ * @param {Quat} out the receiving quaternion, defaults to quat()
660
+ * @returns {Quat} out
661
+ */
662
+ static setAxes(view: Vec3, right: Vec3, up: Vec3, out = quat()) {
663
+ Quat.tmpMat3[0] = right[0]
664
+ Quat.tmpMat3[3] = right[1]
665
+ Quat.tmpMat3[6] = right[2]
666
+
667
+ Quat.tmpMat3[1] = up[0]
668
+ Quat.tmpMat3[4] = up[1]
669
+ Quat.tmpMat3[7] = up[2]
670
+
671
+ const vs = glm.LEFT_HANDED ? 1 : -1
672
+ Quat.tmpMat3[2] = vs * view[0]
673
+ Quat.tmpMat3[5] = vs * view[1]
674
+ Quat.tmpMat3[8] = vs * view[2]
675
+
676
+ return Quat.fromMat3(Quat.tmpMat3, out).normalize()
677
+ }
678
+
679
+ /**
680
+ * Normalizes a quaternion
681
+ *
682
+ * @param {Quat} out the receiving vector, defaults to this
683
+ * @returns {Quat} out
684
+ */
685
+ normalize(out = glm.ALWAYS_COPY ? new Quat() : this): Quat {
686
+ const x = this[0], y = this[1], z = this[2], w = this[3]
687
+ let len = x * x + y * y + z * z + w * w
688
+ if (len > 0) {
689
+ len = 1.0 / Math.sqrt(len)
690
+ }
691
+ out[0] = x * len
692
+ out[1] = y * len
693
+ out[2] = z * len
694
+ out[3] = w * len
695
+ return out
696
+ }
697
+
698
+ /**
699
+ * Creates a quaternion that looks along the given direction vector
700
+ *
701
+ * @param {Vec3} direction the direction to look along
702
+ * @param {Vec3} up the up vector
703
+ * @param {Quat} out the receiving quaternion, defaults to quat()
704
+ * @returns {Quat} out
705
+ */
706
+ static quatLookAt(direction: Vec3, up: Vec3, out = new Quat()): Quat {
707
+ const f = vec3(direction[0], direction[1], direction[2]).normalize()
708
+ const s = Vec3.cross(f, up).normalize()
709
+ const u = Vec3.cross(s, f)
710
+ const m = new Mat3()
711
+ const vs = glm.LEFT_HANDED ? 1 : -1
712
+ m[0] = s[0]; m[1] = u[0]; m[2] = vs * f[0]
713
+ m[3] = s[1]; m[4] = u[1]; m[5] = vs * f[1]
714
+ m[6] = s[2]; m[7] = u[2]; m[8] = vs * f[2]
715
+ return Quat.fromMat3(m, out)
716
+ }
717
+
718
+ /**
719
+ * Extracts the pitch (rotation around X axis) from a quaternion
720
+ *
721
+ * @returns {Number} pitch in radians
722
+ */
723
+ pitch(): number {
724
+ const x = this[0], y = this[1], z = this[2], w = this[3]
725
+ return Math.atan2(2 * (y * z + w * x), w * w - x * x - y * y + z * z)
726
+ }
727
+
728
+ /**
729
+ * Extracts the yaw (rotation around Y axis) from a quaternion
730
+ *
731
+ * @returns {Number} yaw in radians
732
+ */
733
+ yaw(): number {
734
+ return Math.asin(Math.min(Math.max(-2 * (this[0] * this[2] - this[3] * this[1]), -1), 1))
735
+ }
736
+
737
+ /**
738
+ * Extracts the roll (rotation around Z axis) from a quaternion
739
+ *
740
+ * @returns {Number} roll in radians
741
+ */
742
+ roll(): number {
743
+ const x = this[0], y = this[1], z = this[2], w = this[3]
744
+ return Math.atan2(2 * (x * y + w * z), w * w + x * x - y * y - z * z)
745
+ }
746
+
747
+ /**
748
+ * Extracts Euler angles (pitch, yaw, roll) from a quaternion
749
+ *
750
+ * @param {Vec3} out the receiving vector, defaults to vec3()
751
+ * @returns {Vec3} out with [pitch, yaw, roll] in radians
752
+ */
753
+ eulerAngles(out = new Vec3()): Vec3 {
754
+ out[0] = this.pitch()
755
+ out[1] = this.yaw()
756
+ out[2] = this.roll()
757
+ return out
758
+ }
759
+
760
+ /**
761
+ * Converts a quaternion to a 3x3 rotation matrix
762
+ *
763
+ * @param {Mat3} out the receiving matrix, defaults to mat3()
764
+ * @returns {Mat3} out
765
+ */
766
+ toMat3(out = new Mat3()): Mat3 {
767
+ const x = this[0], y = this[1], z = this[2], w = this[3]
768
+ const x2 = x + x, y2 = y + y, z2 = z + z
769
+ const xx = x * x2, xy = x * y2, xz = x * z2
770
+ const yy = y * y2, yz = y * z2, zz = z * z2
771
+ const wx = w * x2, wy = w * y2, wz = w * z2
772
+ out[0] = 1 - (yy + zz); out[1] = xy + wz; out[2] = xz - wy
773
+ out[3] = xy - wz; out[4] = 1 - (xx + zz); out[5] = yz + wx
774
+ out[6] = xz + wy; out[7] = yz - wx; out[8] = 1 - (xx + yy)
775
+ return out
776
+ }
777
+
778
+ /**
779
+ * Converts a quaternion to a 4x4 rotation matrix
780
+ *
781
+ * @param {Mat4} out the receiving matrix, defaults to mat4()
782
+ * @returns {Mat4} out
783
+ */
784
+ toMat4(out = new Mat4()): Mat4 {
785
+ const x = this[0], y = this[1], z = this[2], w = this[3]
786
+ const x2 = x + x, y2 = y + y, z2 = z + z
787
+ const xx = x * x2, xy = x * y2, xz = x * z2
788
+ const yy = y * y2, yz = y * z2, zz = z * z2
789
+ const wx = w * x2, wy = w * y2, wz = w * z2
790
+ out[0] = 1 - (yy + zz); out[1] = xy + wz; out[2] = xz - wy; out[3] = 0
791
+ out[4] = xy - wz; out[5] = 1 - (xx + zz); out[6] = yz + wx; out[7] = 0
792
+ out[8] = xz + wy; out[9] = yz - wx; out[10] = 1 - (xx + yy); out[11] = 0
793
+ out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1
794
+ return out
795
+ }
796
+ }
797
+
798
+ // @ts-ignore
799
+ export interface Quat {
800
+ multiply: (b: Quat | number, out?: Quat) => Quat
801
+ mult: (b: Quat | number, out?: Quat) => Quat
802
+ mul: (b: Quat | number, out?: Quat) => Quat
803
+ scale: (b: Quat | number, out?: Quat) => Quat
804
+ times: (b: Quat | number, out?: Quat) => Quat
805
+ str: () => string
806
+ }
807
+
808
+ // @aliases
809
+ Quat.getAngle = Quat.angle
810
+ Quat.prototype.mult = Quat.prototype.multiply
811
+ Quat.prototype.mul = Quat.prototype.multiply
812
+ Quat.prototype.scale = Quat.prototype.multiply
813
+ Quat.prototype.times = Quat.prototype.multiply
814
+ Quat.prototype.str = Quat.prototype.toString
815
+
816
+ export const quat = Object.assign(
817
+ (x = 0, y = 0, z = 0, w = 1) => new Quat(x, y, z, w),
818
+ Quat
819
+ )