@galfus/engine 0.0.0-stage → 0.31.1-alpha

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.
Files changed (80) hide show
  1. package/README.md +190 -2
  2. package/package.json +59 -4
  3. package/src/anchors/cache.ts +101 -0
  4. package/src/anchors/component.ts +123 -0
  5. package/src/anchors/hierarchy.ts +192 -0
  6. package/src/anchors/index.ts +8 -0
  7. package/src/anchors/solver.ts +247 -0
  8. package/src/anchors/strategy.ts +142 -0
  9. package/src/animation/blending.ts +163 -0
  10. package/src/animation/clip.ts +132 -0
  11. package/src/animation/coordinator.ts +568 -0
  12. package/src/animation/index.ts +7 -0
  13. package/src/animation/native-targets.ts +37 -0
  14. package/src/animation/player.ts +287 -0
  15. package/src/audio/index.ts +4 -0
  16. package/src/audio/manager.ts +371 -0
  17. package/src/collision/components.ts +211 -0
  18. package/src/collision/index.ts +16 -0
  19. package/src/collision/query2d.ts +167 -0
  20. package/src/collision/query3d.ts +170 -0
  21. package/src/ecs/component.ts +130 -0
  22. package/src/ecs/entity.ts +75 -0
  23. package/src/ecs/index.ts +9 -0
  24. package/src/ecs/intent.ts +162 -0
  25. package/src/ecs/metadata.ts +44 -0
  26. package/src/ecs/system.ts +224 -0
  27. package/src/ecs/world.ts +291 -0
  28. package/src/index.ts +19 -0
  29. package/src/interaction/focus.ts +327 -0
  30. package/src/interaction/index.ts +9 -0
  31. package/src/interaction/listener.ts +58 -0
  32. package/src/interaction/snapshot2d.ts +210 -0
  33. package/src/interaction/snapshot3d.ts +214 -0
  34. package/src/interaction/system.ts +544 -0
  35. package/src/interaction/types.ts +27 -0
  36. package/src/math/common.ts +67 -0
  37. package/src/math/index.ts +17 -0
  38. package/src/math/mat2d.ts +320 -0
  39. package/src/math/mat4.ts +576 -0
  40. package/src/math/quat.ts +438 -0
  41. package/src/math/vec2.ts +161 -0
  42. package/src/math/vec3.ts +242 -0
  43. package/src/math/vec4.ts +171 -0
  44. package/src/multiverse/coordinator.ts +540 -0
  45. package/src/multiverse/index.ts +13 -0
  46. package/src/multiverse/presentation-graph.ts +306 -0
  47. package/src/multiverse/presentation-links.ts +134 -0
  48. package/src/multiverse/realm.ts +49 -0
  49. package/src/platform/index.ts +2 -0
  50. package/src/platform/platform.ts +149 -0
  51. package/src/platform/window.ts +181 -0
  52. package/src/realm2d/components.ts +254 -0
  53. package/src/realm2d/index.ts +12 -0
  54. package/src/realm2d/presets/index.ts +36 -0
  55. package/src/realm2d/realm.ts +522 -0
  56. package/src/realm2d/sync.ts +284 -0
  57. package/src/realm2d/utils/index.ts +32 -0
  58. package/src/realm3d/components.ts +341 -0
  59. package/src/realm3d/geometry.ts +224 -0
  60. package/src/realm3d/index.ts +12 -0
  61. package/src/realm3d/presets/index.ts +67 -0
  62. package/src/realm3d/realm.ts +588 -0
  63. package/src/realm3d/resource-ids.ts +62 -0
  64. package/src/realm3d/skinning.ts +128 -0
  65. package/src/realm3d/sync.ts +442 -0
  66. package/src/realm3d/utils/index.ts +26 -0
  67. package/src/resources/index.ts +4 -0
  68. package/src/resources/manager.ts +92 -0
  69. package/src/runtime.ts +588 -0
  70. package/src/spatial/dirty_registry.ts +217 -0
  71. package/src/spatial/flags.ts +14 -0
  72. package/src/spatial/index.ts +6 -0
  73. package/src/spatial/metrics.ts +124 -0
  74. package/src/text/editor.ts +224 -0
  75. package/src/text/encoding.ts +38 -0
  76. package/src/text/index.ts +13 -0
  77. package/src/text/input.ts +160 -0
  78. package/src/text/keyboard.ts +50 -0
  79. package/src/text/svg-decorations.ts +147 -0
  80. package/src/types.ts +85 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Quaternion operations.
3
+ * Stored as a 4-element Float32Array: [x, y, z, w]
4
+ */
5
+
6
+ import { EPSILON, equalsApprox } from './common';
7
+ import type { ReadonlyMat4 } from './mat4';
8
+ import type { ReadonlyVec3 } from './vec3';
9
+
10
+ export type Quat = Float32Array & {
11
+ 0: number;
12
+ 1: number;
13
+ 2: number;
14
+ 3: number;
15
+ };
16
+
17
+ export type ReadonlyQuat = ArrayLike<number> & {
18
+ 0: number;
19
+ 1: number;
20
+ 2: number;
21
+ 3: number;
22
+ };
23
+
24
+ export function create(): Quat {
25
+ const out = new Float32Array(4) as unknown as Quat;
26
+ out[3] = 1;
27
+ return out;
28
+ }
29
+
30
+ export function identity(out: Quat): Quat {
31
+ out[0] = 0;
32
+ out[1] = 0;
33
+ out[2] = 0;
34
+ out[3] = 1;
35
+ return out;
36
+ }
37
+
38
+ export function fromValues(x: number, y: number, z: number, w: number): Quat {
39
+ const out = new Float32Array(4) as unknown as Quat;
40
+ out[0] = x;
41
+ out[1] = y;
42
+ out[2] = z;
43
+ out[3] = w;
44
+ return out;
45
+ }
46
+
47
+ export function copy(out: Quat, a: ReadonlyQuat): Quat {
48
+ out[0] = a[0];
49
+ out[1] = a[1];
50
+ out[2] = a[2];
51
+ out[3] = a[3];
52
+ return out;
53
+ }
54
+
55
+ export function set(out: Quat, x: number, y: number, z: number, w: number): Quat {
56
+ out[0] = x;
57
+ out[1] = y;
58
+ out[2] = z;
59
+ out[3] = w;
60
+ return out;
61
+ }
62
+
63
+ export function setAxisAngle(out: Quat, axis: ReadonlyVec3, rad: number): Quat {
64
+ const half = rad * 0.5;
65
+ const s = Math.sin(half);
66
+ out[0] = s * axis[0];
67
+ out[1] = s * axis[1];
68
+ out[2] = s * axis[2];
69
+ out[3] = Math.cos(half);
70
+ return out;
71
+ }
72
+
73
+ export function multiply(out: Quat, a: ReadonlyQuat, b: ReadonlyQuat): Quat {
74
+ const ax = a[0];
75
+ const ay = a[1];
76
+ const az = a[2];
77
+ const aw = a[3];
78
+ const bx = b[0];
79
+ const by = b[1];
80
+ const bz = b[2];
81
+ const bw = b[3];
82
+
83
+ out[0] = ax * bw + aw * bx + ay * bz - az * by;
84
+ out[1] = ay * bw + aw * by + az * bx - ax * bz;
85
+ out[2] = az * bw + aw * bz + ax * by - ay * bx;
86
+ out[3] = aw * bw - ax * bx - ay * by - az * bz;
87
+ return out;
88
+ }
89
+
90
+ export function rotateX(out: Quat, a: ReadonlyQuat, rad: number): Quat {
91
+ const half = rad * 0.5;
92
+ const ax = a[0];
93
+ const ay = a[1];
94
+ const az = a[2];
95
+ const aw = a[3];
96
+ const bx = Math.sin(half);
97
+ const bw = Math.cos(half);
98
+
99
+ out[0] = ax * bw + aw * bx;
100
+ out[1] = ay * bw + az * bx;
101
+ out[2] = az * bw - ay * bx;
102
+ out[3] = aw * bw - ax * bx;
103
+ return out;
104
+ }
105
+
106
+ export function rotateY(out: Quat, a: ReadonlyQuat, rad: number): Quat {
107
+ const half = rad * 0.5;
108
+ const ax = a[0];
109
+ const ay = a[1];
110
+ const az = a[2];
111
+ const aw = a[3];
112
+ const by = Math.sin(half);
113
+ const bw = Math.cos(half);
114
+
115
+ out[0] = ax * bw - az * by;
116
+ out[1] = ay * bw + aw * by;
117
+ out[2] = az * bw + ax * by;
118
+ out[3] = aw * bw - ay * by;
119
+ return out;
120
+ }
121
+
122
+ export function rotateZ(out: Quat, a: ReadonlyQuat, rad: number): Quat {
123
+ const half = rad * 0.5;
124
+ const ax = a[0];
125
+ const ay = a[1];
126
+ const az = a[2];
127
+ const aw = a[3];
128
+ const bz = Math.sin(half);
129
+ const bw = Math.cos(half);
130
+
131
+ out[0] = ax * bw + ay * bz;
132
+ out[1] = ay * bw - ax * bz;
133
+ out[2] = az * bw + aw * bz;
134
+ out[3] = aw * bw - az * bz;
135
+ return out;
136
+ }
137
+
138
+ export function invert(out: Quat, a: ReadonlyQuat): Quat {
139
+ const a0 = a[0];
140
+ const a1 = a[1];
141
+ const a2 = a[2];
142
+ const a3 = a[3];
143
+ const dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
144
+ const invDot = dot ? 1.0 / dot : 0;
145
+
146
+ out[0] = -a0 * invDot;
147
+ out[1] = -a1 * invDot;
148
+ out[2] = -a2 * invDot;
149
+ out[3] = a3 * invDot;
150
+ return out;
151
+ }
152
+
153
+ export function conjugate(out: Quat, a: ReadonlyQuat): Quat {
154
+ out[0] = -a[0];
155
+ out[1] = -a[1];
156
+ out[2] = -a[2];
157
+ out[3] = a[3];
158
+ return out;
159
+ }
160
+
161
+ export function normalize(out: Quat, a: ReadonlyQuat): Quat {
162
+ const x = a[0];
163
+ const y = a[1];
164
+ const z = a[2];
165
+ const w = a[3];
166
+ let len = Math.hypot(x, y, z, w);
167
+ if (len === 0) {
168
+ out[0] = 0;
169
+ out[1] = 0;
170
+ out[2] = 0;
171
+ out[3] = 1;
172
+ } else {
173
+ len = 1 / len;
174
+ out[0] = x * len;
175
+ out[1] = y * len;
176
+ out[2] = z * len;
177
+ out[3] = w * len;
178
+ }
179
+ return out;
180
+ }
181
+
182
+ export function slerp(out: Quat, a: ReadonlyQuat, b: ReadonlyQuat, t: number): Quat {
183
+ const ax = a[0];
184
+ const ay = a[1];
185
+ const az = a[2];
186
+ let aw = a[3];
187
+ let bx = b[0];
188
+ let by = b[1];
189
+ let bz = b[2];
190
+ let bw = b[3];
191
+
192
+ let cosom = ax * bx + ay * by + az * bz + aw * bw;
193
+ if (cosom < 0.0) {
194
+ cosom = -cosom;
195
+ bx = -bx;
196
+ by = -by;
197
+ bz = -bz;
198
+ bw = -bw;
199
+ }
200
+
201
+ let scale0: number;
202
+ let scale1: number;
203
+
204
+ if (1.0 - cosom > EPSILON) {
205
+ const omega = Math.acos(cosom);
206
+ const sinom = Math.sin(omega);
207
+ scale0 = Math.sin((1.0 - t) * omega) / sinom;
208
+ scale1 = Math.sin(t * omega) / sinom;
209
+ } else {
210
+ scale0 = 1.0 - t;
211
+ scale1 = t;
212
+ }
213
+
214
+ out[0] = scale0 * ax + scale1 * bx;
215
+ out[1] = scale0 * ay + scale1 * by;
216
+ out[2] = scale0 * az + scale1 * bz;
217
+ out[3] = scale0 * aw + scale1 * bw;
218
+ return out;
219
+ }
220
+
221
+ /**
222
+ * Normalized linear interpolation (nlerp) between two quaternions along the shortest path.
223
+ */
224
+ export function lerp(out: Quat, a: ReadonlyQuat, b: ReadonlyQuat, t: number): Quat {
225
+ const ax = a[0];
226
+ const ay = a[1];
227
+ const az = a[2];
228
+ const aw = a[3];
229
+ let bx = b[0];
230
+ let by = b[1];
231
+ let bz = b[2];
232
+ let bw = b[3];
233
+
234
+ if (ax * bx + ay * by + az * bz + aw * bw < 0) {
235
+ bx = -bx;
236
+ by = -by;
237
+ bz = -bz;
238
+ bw = -bw;
239
+ }
240
+
241
+ const x = ax + t * (bx - ax);
242
+ const y = ay + t * (by - ay);
243
+ const z = az + t * (bz - az);
244
+ const w = aw + t * (bw - aw);
245
+
246
+ let len = Math.hypot(x, y, z, w);
247
+ if (len === 0) {
248
+ out[0] = 0;
249
+ out[1] = 0;
250
+ out[2] = 0;
251
+ out[3] = 1;
252
+ } else {
253
+ len = 1 / len;
254
+ out[0] = x * len;
255
+ out[1] = y * len;
256
+ out[2] = z * len;
257
+ out[3] = w * len;
258
+ }
259
+ return out;
260
+ }
261
+
262
+ export function clamp(
263
+ out: Quat,
264
+ a: ReadonlyQuat,
265
+ min: ReadonlyQuat | number,
266
+ max: ReadonlyQuat | number
267
+ ): Quat {
268
+ const minX = typeof min === 'number' ? min : min[0];
269
+ const minY = typeof min === 'number' ? min : min[1];
270
+ const minZ = typeof min === 'number' ? min : min[2];
271
+ const minW = typeof min === 'number' ? min : min[3];
272
+ const maxX = typeof max === 'number' ? max : max[0];
273
+ const maxY = typeof max === 'number' ? max : max[1];
274
+ const maxZ = typeof max === 'number' ? max : max[2];
275
+ const maxW = typeof max === 'number' ? max : max[3];
276
+ out[0] = Math.min(Math.max(a[0], minX), maxX);
277
+ out[1] = Math.min(Math.max(a[1], minY), maxY);
278
+ out[2] = Math.min(Math.max(a[2], minZ), maxZ);
279
+ out[3] = Math.min(Math.max(a[3], minW), maxW);
280
+ return out;
281
+ }
282
+
283
+ export function fromEuler(out: Quat, x: number, y: number, z: number): Quat {
284
+ const halfToRad = 0.5 * (Math.PI / 180.0);
285
+ const xRad = x * halfToRad;
286
+ const yRad = y * halfToRad;
287
+ const zRad = z * halfToRad;
288
+
289
+ const sx = Math.sin(xRad);
290
+ const cx = Math.cos(xRad);
291
+ const sy = Math.sin(yRad);
292
+ const cy = Math.cos(yRad);
293
+ const sz = Math.sin(zRad);
294
+ const cz = Math.cos(zRad);
295
+
296
+ out[0] = sx * cy * cz - cx * sy * sz;
297
+ out[1] = cx * sy * cz + sx * cy * sz;
298
+ out[2] = cx * cy * sz - sx * sy * cz;
299
+ out[3] = cx * cy * cz + sx * sy * sz;
300
+ return out;
301
+ }
302
+
303
+ /**
304
+ * Computes a rotation quaternion that orients an entity at `eye` towards `target`.
305
+ *
306
+ * @param out Output quaternion receiving the computed rotation.
307
+ * @param eye Observer / entity origin position.
308
+ * @param target Destination target point to look at.
309
+ * @param up Upward reference direction (defaults to [0, 1, 0]).
310
+ * @returns `out`
311
+ */
312
+ export function fromLookAt(
313
+ out: Quat,
314
+ eye: ReadonlyVec3,
315
+ target: ReadonlyVec3,
316
+ up: ReadonlyVec3 = [0.0, 1.0, 0.0]
317
+ ): Quat {
318
+ let fx = target[0] - eye[0];
319
+ let fy = target[1] - eye[1];
320
+ let fz = target[2] - eye[2];
321
+ const rlen = 1.0 / (Math.hypot(fx, fy, fz) || 1.0);
322
+ fx *= rlen;
323
+ fy *= rlen;
324
+ fz *= rlen;
325
+
326
+ let rx = up[1] * fz - up[2] * fy;
327
+ let ry = up[2] * fx - up[0] * fz;
328
+ let rz = up[0] * fy - up[1] * fx;
329
+ const rlenR = 1.0 / (Math.hypot(rx, ry, rz) || 1.0);
330
+ rx *= rlenR;
331
+ ry *= rlenR;
332
+ rz *= rlenR;
333
+
334
+ const ux = fy * rz - fz * ry;
335
+ const uy = fz * rx - fx * rz;
336
+ const uz = fx * ry - fy * rx;
337
+
338
+ const m00 = rx;
339
+ const m01 = ry;
340
+ const m02 = rz;
341
+ const m10 = ux;
342
+ const m11 = uy;
343
+ const m12 = uz;
344
+ const m20 = -fx;
345
+ const m21 = -fy;
346
+ const m22 = -fz;
347
+
348
+ const trace = m00 + m11 + m22;
349
+ if (trace > 0) {
350
+ const s = 0.5 / Math.sqrt(trace + 1.0);
351
+ out[0] = (m12 - m21) * s;
352
+ out[1] = (m20 - m02) * s;
353
+ out[2] = (m01 - m10) * s;
354
+ out[3] = 0.25 / s;
355
+ } else if (m00 > m11 && m00 > m22) {
356
+ const s = 2.0 * Math.sqrt(1.0 + m00 - m11 - m22);
357
+ out[0] = 0.25 * s;
358
+ out[1] = (m10 + m01) / s;
359
+ out[2] = (m20 + m02) / s;
360
+ out[3] = (m12 - m21) / s;
361
+ } else if (m11 > m22) {
362
+ const s = 2.0 * Math.sqrt(1.0 + m11 - m00 - m22);
363
+ out[0] = (m10 + m01) / s;
364
+ out[1] = 0.25 * s;
365
+ out[2] = (m21 + m12) / s;
366
+ out[3] = (m20 - m02) / s;
367
+ } else {
368
+ const s = 2.0 * Math.sqrt(1.0 + m22 - m00 - m11);
369
+ out[0] = (m20 + m02) / s;
370
+ out[1] = (m21 + m12) / s;
371
+ out[2] = 0.25 * s;
372
+ out[3] = (m01 - m10) / s;
373
+ }
374
+ return out;
375
+ }
376
+
377
+ export function fromMat4(out: Quat, m: ReadonlyMat4): Quat {
378
+ let m00 = m[0]!;
379
+ let m01 = m[1]!;
380
+ let m02 = m[2]!;
381
+ let m10 = m[4]!;
382
+ let m11 = m[5]!;
383
+ let m12 = m[6]!;
384
+ let m20 = m[8]!;
385
+ let m21 = m[9]!;
386
+ let m22 = m[10]!;
387
+
388
+ const sx = 1.0 / (Math.hypot(m00, m01, m02) || 1.0);
389
+ const sy = 1.0 / (Math.hypot(m10, m11, m12) || 1.0);
390
+ const sz = 1.0 / (Math.hypot(m20, m21, m22) || 1.0);
391
+
392
+ m00 *= sx;
393
+ m01 *= sx;
394
+ m02 *= sx;
395
+ m10 *= sy;
396
+ m11 *= sy;
397
+ m12 *= sy;
398
+ m20 *= sz;
399
+ m21 *= sz;
400
+ m22 *= sz;
401
+
402
+ const trace = m00 + m11 + m22;
403
+ if (trace > 0) {
404
+ const s = 0.5 / Math.sqrt(trace + 1.0);
405
+ out[0] = (m12 - m21) * s;
406
+ out[1] = (m20 - m02) * s;
407
+ out[2] = (m01 - m10) * s;
408
+ out[3] = 0.25 / s;
409
+ } else if (m00 > m11 && m00 > m22) {
410
+ const s = 2.0 * Math.sqrt(1.0 + m00 - m11 - m22);
411
+ out[0] = 0.25 * s;
412
+ out[1] = (m10 + m01) / s;
413
+ out[2] = (m20 + m02) / s;
414
+ out[3] = (m12 - m21) / s;
415
+ } else if (m11 > m22) {
416
+ const s = 2.0 * Math.sqrt(1.0 + m11 - m00 - m22);
417
+ out[0] = (m10 + m01) / s;
418
+ out[1] = 0.25 * s;
419
+ out[2] = (m21 + m12) / s;
420
+ out[3] = (m20 - m02) / s;
421
+ } else {
422
+ const s = 2.0 * Math.sqrt(1.0 + m22 - m00 - m11);
423
+ out[0] = (m20 + m02) / s;
424
+ out[1] = (m21 + m12) / s;
425
+ out[2] = 0.25 * s;
426
+ out[3] = (m01 - m10) / s;
427
+ }
428
+ return out;
429
+ }
430
+
431
+ export function equals(a: ReadonlyQuat, b: ReadonlyQuat, epsilon: number = EPSILON): boolean {
432
+ return (
433
+ equalsApprox(a[0], b[0], epsilon) &&
434
+ equalsApprox(a[1], b[1], epsilon) &&
435
+ equalsApprox(a[2], b[2], epsilon) &&
436
+ equalsApprox(a[3], b[3], epsilon)
437
+ );
438
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * 2D vector operations.
3
+ */
4
+
5
+ import { EPSILON, equalsApprox } from './common';
6
+ import type { ReadonlyMat2d } from './mat2d';
7
+
8
+ export type Vec2 = Float32Array & {
9
+ 0: number;
10
+ 1: number;
11
+ };
12
+
13
+ export type ReadonlyVec2 = ArrayLike<number> & {
14
+ 0: number;
15
+ 1: number;
16
+ };
17
+
18
+ export type WritableVec2 = Vec2 | [number, number];
19
+
20
+ export function create(): Vec2 {
21
+ return new Float32Array(2) as unknown as Vec2;
22
+ }
23
+
24
+ export function fromValues(x: number, y: number): Vec2 {
25
+ const out = new Float32Array(2) as unknown as Vec2;
26
+ out[0] = x;
27
+ out[1] = y;
28
+ return out;
29
+ }
30
+
31
+ export function copy(out: Vec2, a: ReadonlyVec2): Vec2 {
32
+ out[0] = a[0];
33
+ out[1] = a[1];
34
+ return out;
35
+ }
36
+
37
+ export function set<T extends WritableVec2>(out: T, x: number, y: number): T {
38
+ out[0] = x;
39
+ out[1] = y;
40
+ return out;
41
+ }
42
+
43
+ export function add<T extends WritableVec2>(out: T, a: ReadonlyVec2, b: ReadonlyVec2): T {
44
+ out[0] = a[0] + b[0];
45
+ out[1] = a[1] + b[1];
46
+ return out;
47
+ }
48
+
49
+ export function subtract<T extends WritableVec2>(out: T, a: ReadonlyVec2, b: ReadonlyVec2): T {
50
+ out[0] = a[0] - b[0];
51
+ out[1] = a[1] - b[1];
52
+ return out;
53
+ }
54
+
55
+ export function multiply(out: Vec2, a: ReadonlyVec2, b: ReadonlyVec2): Vec2 {
56
+ out[0] = a[0] * b[0];
57
+ out[1] = a[1] * b[1];
58
+ return out;
59
+ }
60
+
61
+ export function scale<T extends WritableVec2>(out: T, a: ReadonlyVec2, b: number): T {
62
+ out[0] = a[0] * b;
63
+ out[1] = a[1] * b;
64
+ return out;
65
+ }
66
+
67
+ export function distance(a: ReadonlyVec2, b: ReadonlyVec2): number {
68
+ const x = b[0] - a[0];
69
+ const y = b[1] - a[1];
70
+ return Math.hypot(x, y);
71
+ }
72
+
73
+ export function length(a: ReadonlyVec2): number {
74
+ return Math.hypot(a[0], a[1]);
75
+ }
76
+
77
+ export function normalize<T extends WritableVec2>(out: T, a: ReadonlyVec2): T {
78
+ const x = a[0];
79
+ const y = a[1];
80
+ const len = Math.hypot(x, y);
81
+ out[0] = len === 0 ? 0 : x / len;
82
+ out[1] = len === 0 ? 0 : y / len;
83
+ return out;
84
+ }
85
+
86
+ export function dot(a: ReadonlyVec2, b: ReadonlyVec2): number {
87
+ return a[0] * b[0] + a[1] * b[1];
88
+ }
89
+
90
+ export function cross(a: ReadonlyVec2, b: ReadonlyVec2): number {
91
+ return a[0] * b[1] - a[1] * b[0];
92
+ }
93
+
94
+ export function lerp(out: Vec2, a: ReadonlyVec2, b: ReadonlyVec2, t: number): Vec2 {
95
+ const ax = a[0];
96
+ const ay = a[1];
97
+ out[0] = ax + t * (b[0] - ax);
98
+ out[1] = ay + t * (b[1] - ay);
99
+ return out;
100
+ }
101
+
102
+ export function clamp(
103
+ out: Vec2,
104
+ a: ReadonlyVec2,
105
+ min: ReadonlyVec2 | number,
106
+ max: ReadonlyVec2 | number
107
+ ): Vec2 {
108
+ const minX = typeof min === 'number' ? min : min[0];
109
+ const minY = typeof min === 'number' ? min : min[1];
110
+ const maxX = typeof max === 'number' ? max : max[0];
111
+ const maxY = typeof max === 'number' ? max : max[1];
112
+ out[0] = Math.min(Math.max(a[0], minX), maxX);
113
+ out[1] = Math.min(Math.max(a[1], minY), maxY);
114
+ return out;
115
+ }
116
+
117
+ export function transformMat2d<T extends WritableVec2>(
118
+ out: T,
119
+ a: ReadonlyVec2,
120
+ m: ReadonlyMat2d
121
+ ): T {
122
+ const x = a[0];
123
+ const y = a[1];
124
+ out[0] = m[0] * x + m[2] * y + m[4];
125
+ out[1] = m[1] * x + m[3] * y + m[5];
126
+ return out;
127
+ }
128
+
129
+ /** Transforms a direction by the linear part of an affine matrix. */
130
+ export function transformDirectionMat2d<T extends WritableVec2>(
131
+ out: T,
132
+ a: ReadonlyVec2,
133
+ m: ReadonlyMat2d
134
+ ): T {
135
+ const x = a[0],
136
+ y = a[1];
137
+ out[0] = m[0] * x + m[2] * y;
138
+ out[1] = m[1] * x + m[3] * y;
139
+ return out;
140
+ }
141
+
142
+ /** Transforms a normal using the transpose of an already inverted affine matrix. */
143
+ export function transformNormalMat2d<T extends WritableVec2>(
144
+ out: T,
145
+ a: ReadonlyVec2,
146
+ inverse: ReadonlyMat2d
147
+ ): T {
148
+ const x = a[0],
149
+ y = a[1];
150
+ out[0] = inverse[0] * x + inverse[1] * y;
151
+ out[1] = inverse[2] * x + inverse[3] * y;
152
+ return out;
153
+ }
154
+
155
+ export function exactEquals(a: ReadonlyVec2, b: ReadonlyVec2): boolean {
156
+ return a[0] === b[0] && a[1] === b[1];
157
+ }
158
+
159
+ export function equals(a: ReadonlyVec2, b: ReadonlyVec2, epsilon: number = EPSILON): boolean {
160
+ return equalsApprox(a[0], b[0], epsilon) && equalsApprox(a[1], b[1], epsilon);
161
+ }