@flare-engine/math 0.2.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -1,1199 +1,1330 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/circle.ts
3
+ const Circle = {
4
+ /** Returns true if point is inside or on the circle. */
5
+ contains(c, p) {
6
+ const dx = p.x - c.x;
7
+ const dy = p.y - c.y;
8
+ return dx * dx + dy * dy <= c.radius * c.radius;
9
+ },
10
+ /** Returns true if two circles overlap (share area). */
11
+ intersects(a, b) {
12
+ const dx = b.x - a.x;
13
+ const dy = b.y - a.y;
14
+ const distSq = dx * dx + dy * dy;
15
+ const radSum = a.radius + b.radius;
16
+ return distSq < radSum * radSum;
17
+ },
18
+ /** Returns true if a circle and rect overlap. */
19
+ intersectsRect(c, r) {
20
+ const closestX = c.x < r.x ? r.x : c.x > r.x + r.width ? r.x + r.width : c.x;
21
+ const closestY = c.y < r.y ? r.y : c.y > r.y + r.height ? r.y + r.height : c.y;
22
+ const dx = c.x - closestX;
23
+ const dy = c.y - closestY;
24
+ return dx * dx + dy * dy <= c.radius * c.radius;
25
+ }
9
26
  };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- Circle: () => Circle,
24
- Color: () => Color,
25
- Mat3: () => Mat3,
26
- Rect: () => Rect,
27
- SeededRNG: () => SeededRNG,
28
- Transform2D: () => Transform2D,
29
- Vec2: () => Vec2,
30
- advanceWindowedStreak: () => advanceWindowedStreak,
31
- angleDelta: () => angleDelta,
32
- approxEquals: () => approxEquals,
33
- cappedBonus: () => cappedBonus,
34
- choose: () => choose,
35
- circleVsCircle: () => circleVsCircle,
36
- clamp: () => clamp,
37
- degToRad: () => degToRad,
38
- easeInBack: () => easeInBack,
39
- easeInBounce: () => easeInBounce,
40
- easeInCirc: () => easeInCirc,
41
- easeInCubic: () => easeInCubic,
42
- easeInElastic: () => easeInElastic,
43
- easeInExpo: () => easeInExpo,
44
- easeInOutBack: () => easeInOutBack,
45
- easeInOutBounce: () => easeInOutBounce,
46
- easeInOutCirc: () => easeInOutCirc,
47
- easeInOutCubic: () => easeInOutCubic,
48
- easeInOutElastic: () => easeInOutElastic,
49
- easeInOutExpo: () => easeInOutExpo,
50
- easeInOutQuad: () => easeInOutQuad,
51
- easeInOutQuart: () => easeInOutQuart,
52
- easeInOutQuint: () => easeInOutQuint,
53
- easeInOutSine: () => easeInOutSine,
54
- easeInQuad: () => easeInQuad,
55
- easeInQuart: () => easeInQuart,
56
- easeInQuint: () => easeInQuint,
57
- easeInSine: () => easeInSine,
58
- easeOutBack: () => easeOutBack,
59
- easeOutBounce: () => easeOutBounce,
60
- easeOutCirc: () => easeOutCirc,
61
- easeOutCubic: () => easeOutCubic,
62
- easeOutElastic: () => easeOutElastic,
63
- easeOutExpo: () => easeOutExpo,
64
- easeOutQuad: () => easeOutQuad,
65
- easeOutQuart: () => easeOutQuart,
66
- easeOutQuint: () => easeOutQuint,
67
- easeOutSine: () => easeOutSine,
68
- hashU32: () => hashU32,
69
- inverseLerp: () => inverseLerp,
70
- lerp: () => lerp,
71
- lineVsCircle: () => lineVsCircle,
72
- lineVsRect: () => lineVsRect,
73
- linear: () => linear,
74
- map: () => map,
75
- pingPong: () => pingPong,
76
- pointInCircle: () => pointInCircle,
77
- pointInRect: () => pointInRect,
78
- radToDeg: () => radToDeg,
79
- randomFloat: () => randomFloat,
80
- randomInt: () => randomInt,
81
- randomPointInCircle: () => randomPointInCircle,
82
- randomVec2InRect: () => randomVec2InRect,
83
- rayVsRect: () => rayVsRect,
84
- rectVsCircle: () => rectVsCircle,
85
- rectVsRect: () => rectVsRect,
86
- remainingInWindow: () => remainingInWindow,
87
- rotateTowards: () => rotateTowards,
88
- seededIndex: () => seededIndex,
89
- shuffle: () => shuffle,
90
- sign: () => sign,
91
- smoothStep: () => smoothStep,
92
- springIn: () => springIn,
93
- springInOut: () => springInOut,
94
- springOut: () => springOut,
95
- step: () => step,
96
- vec2: () => vec2,
97
- weightedIndex: () => weightedIndex,
98
- weightedIndexBy: () => weightedIndexBy,
99
- weightedPick: () => weightedPick,
100
- windowExpired: () => windowExpired,
101
- wrap: () => wrap
102
- });
103
- module.exports = __toCommonJS(index_exports);
104
-
105
- // src/scalar.ts
27
+ //#endregion
28
+ //#region src/scalar.ts
29
+ /** Clamps value between min and max. */
106
30
  function clamp(value, min, max) {
107
- return value < min ? min : value > max ? max : value;
31
+ return value < min ? min : value > max ? max : value;
108
32
  }
33
+ /** Linearly interpolates between a and b by t (unclamped). */
109
34
  function lerp(a, b, t) {
110
- return a + (b - a) * t;
35
+ return a + (b - a) * t;
111
36
  }
37
+ /** Returns the interpolation factor of value between a and b. */
112
38
  function inverseLerp(a, b, value) {
113
- const range = b - a;
114
- if (range === 0) return 0;
115
- return (value - a) / range;
39
+ const range = b - a;
40
+ if (range === 0) return 0;
41
+ return (value - a) / range;
116
42
  }
43
+ /** Maps value from [inMin, inMax] to [outMin, outMax]. */
117
44
  function map(value, inMin, inMax, outMin, outMax) {
118
- return lerp(outMin, outMax, inverseLerp(inMin, inMax, value));
45
+ return lerp(outMin, outMax, inverseLerp(inMin, inMax, value));
119
46
  }
120
- var DEG_TO_RAD = Math.PI / 180;
121
- var RAD_TO_DEG = 180 / Math.PI;
47
+ const DEG_TO_RAD = Math.PI / 180;
48
+ const RAD_TO_DEG = 180 / Math.PI;
49
+ /** Converts degrees to radians. */
122
50
  function degToRad(degrees) {
123
- return degrees * DEG_TO_RAD;
51
+ return degrees * DEG_TO_RAD;
124
52
  }
53
+ /** Converts radians to degrees. */
125
54
  function radToDeg(radians) {
126
- return radians * RAD_TO_DEG;
55
+ return radians * RAD_TO_DEG;
127
56
  }
57
+ /** Returns true if a and b are approximately equal within epsilon. */
128
58
  function approxEquals(a, b, epsilon = 1e-6) {
129
- return Math.abs(a - b) <= epsilon;
59
+ return Math.abs(a - b) <= epsilon;
130
60
  }
61
+ /** Wraps value into [min, max) range. */
131
62
  function wrap(value, min, max) {
132
- const range = max - min;
133
- if (range === 0) return min;
134
- return min + ((value - min) % range + range) % range;
63
+ const range = max - min;
64
+ if (range === 0) return min;
65
+ return min + ((value - min) % range + range) % range;
135
66
  }
67
+ /** PingPong — bounces value back and forth in [0, length]. */
136
68
  function pingPong(t, length) {
137
- if (length === 0) return 0;
138
- const tt = wrap(t, 0, length * 2);
139
- return length - Math.abs(tt - length);
69
+ if (length === 0) return 0;
70
+ const tt = wrap(t, 0, length * 2);
71
+ return length - Math.abs(tt - length);
140
72
  }
73
+ /** Hermite smooth interpolation (0→1) with zero first-derivative at edges. */
141
74
  function smoothStep(edge0, edge1, x) {
142
- const range = edge1 - edge0;
143
- if (range === 0) return x >= edge1 ? 1 : 0;
144
- const t = clamp((x - edge0) / range, 0, 1);
145
- return t * t * (3 - 2 * t);
75
+ const range = edge1 - edge0;
76
+ if (range === 0) return x >= edge1 ? 1 : 0;
77
+ const t = clamp((x - edge0) / range, 0, 1);
78
+ return t * t * (3 - 2 * t);
146
79
  }
80
+ /** Returns -1, 0, or 1. Unlike Math.sign, returns 0 for NaN. */
147
81
  function sign(value) {
148
- if (Number.isNaN(value)) return 0;
149
- return value > 0 ? 1 : value < 0 ? -1 : 0;
82
+ if (Number.isNaN(value)) return 0;
83
+ return value > 0 ? 1 : value < 0 ? -1 : 0;
150
84
  }
85
+ /** Shortest signed angle delta a - b, wrapped to [-pi, pi] (closed both ends — strict comparisons
86
+ * leave exact +/-pi unwrapped). Branch form (single +/-2pi correction)
87
+ * — byte-identical to hand-rolled aim/homing turn math; do NOT swap for a modulo form (it shifts
88
+ * results by ULPs at the +/-pi boundary). */
151
89
  function angleDelta(a, b) {
152
- let diff = a - b;
153
- if (diff > Math.PI) diff -= 2 * Math.PI;
154
- else if (diff < -Math.PI) diff += 2 * Math.PI;
155
- return diff;
90
+ let diff = a - b;
91
+ if (diff > Math.PI) diff -= 2 * Math.PI;
92
+ else if (diff < -Math.PI) diff += 2 * Math.PI;
93
+ return diff;
156
94
  }
95
+ /** Rotates angle cur toward target by at most maxStep radians, shortest direction. Returns the new
96
+ * angle (NOT re-wrapped). Branch-form step clamp — byte-identical to hand-rolled homing steering. */
157
97
  function rotateTowards(cur, target, maxStep) {
158
- const diff = angleDelta(target, cur);
159
- const turn = diff > maxStep ? maxStep : diff < -maxStep ? -maxStep : diff;
160
- return cur + turn;
161
- }
162
-
163
- // src/vec2.ts
164
- function vec2(x = 0, y = 0) {
165
- return { x, y };
166
- }
167
- var Vec2 = {
168
- /** Returns { 0, 0 }. */
169
- zero() {
170
- return { x: 0, y: 0 };
171
- },
172
- /** Returns { 1, 1 }. */
173
- one() {
174
- return { x: 1, y: 1 };
175
- },
176
- /** Returns { 0, -1 }. */
177
- up() {
178
- return { x: 0, y: -1 };
179
- },
180
- /** Returns { 0, 1 }. */
181
- down() {
182
- return { x: 0, y: 1 };
183
- },
184
- /** Returns { -1, 0 }. */
185
- left() {
186
- return { x: -1, y: 0 };
187
- },
188
- /** Returns { 1, 0 }. */
189
- right() {
190
- return { x: 1, y: 0 };
191
- },
192
- /** Component-wise addition. Writes into out if provided. */
193
- add(a, b, out) {
194
- if (out) {
195
- out.x = a.x + b.x;
196
- out.y = a.y + b.y;
197
- return out;
198
- }
199
- return { x: a.x + b.x, y: a.y + b.y };
200
- },
201
- /** Component-wise subtraction a - b. */
202
- sub(a, b, out) {
203
- if (out) {
204
- out.x = a.x - b.x;
205
- out.y = a.y - b.y;
206
- return out;
207
- }
208
- return { x: a.x - b.x, y: a.y - b.y };
209
- },
210
- /** Scales a vector by a scalar. */
211
- scale(v, s, out) {
212
- if (out) {
213
- out.x = v.x * s;
214
- out.y = v.y * s;
215
- return out;
216
- }
217
- return { x: v.x * s, y: v.y * s };
218
- },
219
- /** Returns a unit vector. Returns zero vector if length is 0. */
220
- normalize(v, out) {
221
- const len = Math.sqrt(v.x * v.x + v.y * v.y);
222
- if (len === 0) {
223
- if (out) {
224
- out.x = 0;
225
- out.y = 0;
226
- return out;
227
- }
228
- return { x: 0, y: 0 };
229
- }
230
- const inv = 1 / len;
231
- if (out) {
232
- out.x = v.x * inv;
233
- out.y = v.y * inv;
234
- return out;
235
- }
236
- return { x: v.x * inv, y: v.y * inv };
237
- },
238
- /** Zero-alloc; writes into `out`. Mandatory `out` so callers avoid temporaries in hot paths. */
239
- normalizeOut(dx, dy, speed, out) {
240
- const len = Math.sqrt(dx * dx + dy * dy);
241
- if (len <= 1e-6) {
242
- out.x = 0;
243
- out.y = 0;
244
- return out;
245
- }
246
- const inv = speed / len;
247
- out.x = dx * inv;
248
- out.y = dy * inv;
249
- return out;
250
- },
251
- /** Dot product. */
252
- dot(a, b) {
253
- return a.x * b.x + a.y * b.y;
254
- },
255
- /** 2D cross product (z-component of the 3D cross). */
256
- cross(a, b) {
257
- return a.x * b.y - a.y * b.x;
258
- },
259
- /** Linearly interpolates between a and b (unclamped). */
260
- lerp(a, b, t, out) {
261
- const x = a.x + (b.x - a.x) * t;
262
- const y = a.y + (b.y - a.y) * t;
263
- if (out) {
264
- out.x = x;
265
- out.y = y;
266
- return out;
267
- }
268
- return { x, y };
269
- },
270
- /** Spherical linear interpolation for unit vectors. */
271
- slerp(a, b, t, out) {
272
- let d = a.x * b.x + a.y * b.y;
273
- d = d < -1 ? -1 : d > 1 ? 1 : d;
274
- const theta = Math.acos(d);
275
- if (theta < 1e-6) {
276
- return Vec2.lerp(a, b, t, out);
277
- }
278
- const sinTheta = Math.sin(theta);
279
- const wa = Math.sin((1 - t) * theta) / sinTheta;
280
- const wb = Math.sin(t * theta) / sinTheta;
281
- const x = a.x * wa + b.x * wb;
282
- const y = a.y * wa + b.y * wb;
283
- if (out) {
284
- out.x = x;
285
- out.y = y;
286
- return out;
287
- }
288
- return { x, y };
289
- },
290
- /** Euclidean distance between two points. */
291
- distance(a, b) {
292
- const dx = b.x - a.x;
293
- const dy = b.y - a.y;
294
- return Math.sqrt(dx * dx + dy * dy);
295
- },
296
- /** Squared distance (avoids sqrt). */
297
- distanceSq(a, b) {
298
- const dx = b.x - a.x;
299
- const dy = b.y - a.y;
300
- return dx * dx + dy * dy;
301
- },
302
- /** Length of a vector. */
303
- length(v) {
304
- return Math.sqrt(v.x * v.x + v.y * v.y);
305
- },
306
- /** Squared length (avoids sqrt). */
307
- lengthSq(v) {
308
- return v.x * v.x + v.y * v.y;
309
- },
310
- /** Angle of vector in radians (atan2). */
311
- angle(v) {
312
- return Math.atan2(v.y, v.x);
313
- },
314
- /** Rotates a vector by angle (radians) around origin. */
315
- rotate(v, angle, out) {
316
- const c = Math.cos(angle);
317
- const s = Math.sin(angle);
318
- const x = v.x * c - v.y * s;
319
- const y = v.x * s + v.y * c;
320
- if (out) {
321
- out.x = x;
322
- out.y = y;
323
- return out;
324
- }
325
- return { x, y };
326
- },
327
- /** Component-wise approximate equality. */
328
- equals(a, b, epsilon = 1e-6) {
329
- return approxEquals(a.x, b.x, epsilon) && approxEquals(a.y, b.y, epsilon);
330
- },
331
- /** Returns a copy of the vector. */
332
- clone(v) {
333
- return { x: v.x, y: v.y };
334
- },
335
- /** Clamps the length of a vector to maxLength. */
336
- clampLength(v, maxLength, out) {
337
- const lenSq = v.x * v.x + v.y * v.y;
338
- if (lenSq <= maxLength * maxLength) {
339
- if (out) {
340
- out.x = v.x;
341
- out.y = v.y;
342
- return out;
343
- }
344
- return { x: v.x, y: v.y };
345
- }
346
- const len = Math.sqrt(lenSq);
347
- const inv = maxLength / len;
348
- if (out) {
349
- out.x = v.x * inv;
350
- out.y = v.y * inv;
351
- return out;
352
- }
353
- return { x: v.x * inv, y: v.y * inv };
354
- },
355
- /** Zero-alloc homing steer: rotate velocity (vx,vy) toward direction (dx,dy) by at most maxStep
356
- * radians, preserving speed; writes into out. Mandatory out. Byte-identical to hand-rolled
357
- * homing-bullet steering (Math.hypot speed + atan2 + branch-form turn + cos/sin * speed). */
358
- turnVelocityToward(vx, vy, dx, dy, maxStep, out) {
359
- const speed = Math.hypot(vx, vy);
360
- const newAng = rotateTowards(Math.atan2(vy, vx), Math.atan2(dy, dx), maxStep);
361
- out.x = Math.cos(newAng) * speed;
362
- out.y = Math.sin(newAng) * speed;
363
- return out;
364
- }
365
- };
366
-
367
- // src/rect.ts
368
- var Rect = {
369
- /** Returns true if point is inside rect (inclusive). */
370
- contains(r, p) {
371
- return p.x >= r.x && p.x <= r.x + r.width && p.y >= r.y && p.y <= r.y + r.height;
372
- },
373
- /** Returns true if two rects overlap (share area). */
374
- intersects(a, b) {
375
- return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
376
- },
377
- /** Returns the overlapping rect, or null if no overlap. */
378
- intersection(a, b) {
379
- const x = Math.max(a.x, b.x);
380
- const y = Math.max(a.y, b.y);
381
- const right = Math.min(a.x + a.width, b.x + b.width);
382
- const bottom = Math.min(a.y + a.height, b.y + b.height);
383
- if (right <= x || bottom <= y) return null;
384
- return { x, y, width: right - x, height: bottom - y };
385
- },
386
- /** Returns the smallest rect containing both rects. */
387
- union(a, b) {
388
- const x = Math.min(a.x, b.x);
389
- const y = Math.min(a.y, b.y);
390
- const right = Math.max(a.x + a.width, b.x + b.width);
391
- const bottom = Math.max(a.y + a.height, b.y + b.height);
392
- return { x, y, width: right - x, height: bottom - y };
393
- },
394
- /** Creates a rect from an array of points (bounding box). */
395
- fromPoints(points) {
396
- if (points.length === 0) return { x: 0, y: 0, width: 0, height: 0 };
397
- let minX = points[0].x;
398
- let minY = points[0].y;
399
- let maxX = minX;
400
- let maxY = minY;
401
- for (let i = 1; i < points.length; i++) {
402
- const p = points[i];
403
- if (p.x < minX) minX = p.x;
404
- if (p.y < minY) minY = p.y;
405
- if (p.x > maxX) maxX = p.x;
406
- if (p.y > maxY) maxY = p.y;
407
- }
408
- return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
409
- },
410
- /** Returns the center point of the rect. */
411
- center(r) {
412
- return { x: r.x + r.width * 0.5, y: r.y + r.height * 0.5 };
413
- },
414
- /** Returns a new rect expanded by amount on each side. */
415
- expand(r, amount) {
416
- return {
417
- x: r.x - amount,
418
- y: r.y - amount,
419
- width: r.width + amount * 2,
420
- height: r.height + amount * 2
421
- };
422
- }
423
- };
424
-
425
- // src/circle.ts
426
- var Circle = {
427
- /** Returns true if point is inside or on the circle. */
428
- contains(c, p) {
429
- const dx = p.x - c.x;
430
- const dy = p.y - c.y;
431
- return dx * dx + dy * dy <= c.radius * c.radius;
432
- },
433
- /** Returns true if two circles overlap (share area). */
434
- intersects(a, b) {
435
- const dx = b.x - a.x;
436
- const dy = b.y - a.y;
437
- const distSq = dx * dx + dy * dy;
438
- const radSum = a.radius + b.radius;
439
- return distSq < radSum * radSum;
440
- },
441
- /** Returns true if a circle and rect overlap. */
442
- intersectsRect(c, r) {
443
- const closestX = c.x < r.x ? r.x : c.x > r.x + r.width ? r.x + r.width : c.x;
444
- const closestY = c.y < r.y ? r.y : c.y > r.y + r.height ? r.y + r.height : c.y;
445
- const dx = c.x - closestX;
446
- const dy = c.y - closestY;
447
- return dx * dx + dy * dy <= c.radius * c.radius;
448
- }
449
- };
450
-
451
- // src/transform2d.ts
452
- var Transform2D = {
453
- /** Creates an identity transform. */
454
- identity() {
455
- return { position: { x: 0, y: 0 }, rotation: 0, scale: { x: 1, y: 1 } };
456
- },
457
- /** Creates a transform from components. */
458
- create(x = 0, y = 0, rotation = 0, scaleX = 1, scaleY = 1) {
459
- return {
460
- position: { x, y },
461
- rotation,
462
- scale: { x: scaleX, y: scaleY }
463
- };
464
- },
465
- /** Transforms a local-space point to world-space. */
466
- localToWorld(t, point) {
467
- const c = Math.cos(t.rotation);
468
- const s = Math.sin(t.rotation);
469
- const sx = point.x * t.scale.x;
470
- const sy = point.y * t.scale.y;
471
- return {
472
- x: sx * c - sy * s + t.position.x,
473
- y: sx * s + sy * c + t.position.y
474
- };
475
- },
476
- /** Transforms a world-space point to local-space. */
477
- worldToLocal(t, point) {
478
- const dx = point.x - t.position.x;
479
- const dy = point.y - t.position.y;
480
- const c = Math.cos(-t.rotation);
481
- const s = Math.sin(-t.rotation);
482
- const rx = dx * c - dy * s;
483
- const ry = dx * s + dy * c;
484
- return {
485
- x: t.scale.x !== 0 ? rx / t.scale.x : 0,
486
- y: t.scale.y !== 0 ? ry / t.scale.y : 0
487
- };
488
- },
489
- /** Returns a copy of the transform. */
490
- clone(t) {
491
- return {
492
- position: { x: t.position.x, y: t.position.y },
493
- rotation: t.rotation,
494
- scale: { x: t.scale.x, y: t.scale.y }
495
- };
496
- }
497
- };
498
-
499
- // src/mat3.ts
500
- var Mat3 = {
501
- /** Creates a new identity matrix. */
502
- create() {
503
- const m = new Float32Array(9);
504
- m[0] = 1;
505
- m[4] = 1;
506
- m[8] = 1;
507
- return m;
508
- },
509
- /** Sets m to identity. */
510
- identity(m) {
511
- m[0] = 1;
512
- m[1] = 0;
513
- m[2] = 0;
514
- m[3] = 0;
515
- m[4] = 1;
516
- m[5] = 0;
517
- m[6] = 0;
518
- m[7] = 0;
519
- m[8] = 1;
520
- return m;
521
- },
522
- /** Multiplies a * b, writes into out (or allocates). */
523
- multiply(a, b, out) {
524
- const a00 = a[0], a01 = a[3], a02 = a[6];
525
- const a10 = a[1], a11 = a[4], a12 = a[7];
526
- const a20 = a[2], a21 = a[5], a22 = a[8];
527
- const b00 = b[0], b01 = b[3], b02 = b[6];
528
- const b10 = b[1], b11 = b[4], b12 = b[7];
529
- const b20 = b[2], b21 = b[5], b22 = b[8];
530
- const o = out ?? new Float32Array(9);
531
- o[0] = a00 * b00 + a01 * b10 + a02 * b20;
532
- o[1] = a10 * b00 + a11 * b10 + a12 * b20;
533
- o[2] = a20 * b00 + a21 * b10 + a22 * b20;
534
- o[3] = a00 * b01 + a01 * b11 + a02 * b21;
535
- o[4] = a10 * b01 + a11 * b11 + a12 * b21;
536
- o[5] = a20 * b01 + a21 * b11 + a22 * b21;
537
- o[6] = a00 * b02 + a01 * b12 + a02 * b22;
538
- o[7] = a10 * b02 + a11 * b12 + a12 * b22;
539
- o[8] = a20 * b02 + a21 * b12 + a22 * b22;
540
- return o;
541
- },
542
- /** Creates a translation matrix. */
543
- translate(tx, ty, out) {
544
- const m = out ?? new Float32Array(9);
545
- m[0] = 1;
546
- m[1] = 0;
547
- m[2] = 0;
548
- m[3] = 0;
549
- m[4] = 1;
550
- m[5] = 0;
551
- m[6] = tx;
552
- m[7] = ty;
553
- m[8] = 1;
554
- return m;
555
- },
556
- /** Creates a rotation matrix (radians). */
557
- rotate(angle, out) {
558
- const c = Math.cos(angle);
559
- const s = Math.sin(angle);
560
- const m = out ?? new Float32Array(9);
561
- m[0] = c;
562
- m[1] = s;
563
- m[2] = 0;
564
- m[3] = -s;
565
- m[4] = c;
566
- m[5] = 0;
567
- m[6] = 0;
568
- m[7] = 0;
569
- m[8] = 1;
570
- return m;
571
- },
572
- /** Creates a scale matrix. */
573
- scale(sx, sy, out) {
574
- const m = out ?? new Float32Array(9);
575
- m[0] = sx;
576
- m[1] = 0;
577
- m[2] = 0;
578
- m[3] = 0;
579
- m[4] = sy;
580
- m[5] = 0;
581
- m[6] = 0;
582
- m[7] = 0;
583
- m[8] = 1;
584
- return m;
585
- },
586
- /** Inverts the matrix. Returns null if singular. */
587
- invert(m, out) {
588
- const a00 = m[0], a01 = m[3], a02 = m[6];
589
- const a10 = m[1], a11 = m[4], a12 = m[7];
590
- const a20 = m[2], a21 = m[5], a22 = m[8];
591
- const det = a00 * (a11 * a22 - a12 * a21) - a01 * (a10 * a22 - a12 * a20) + a02 * (a10 * a21 - a11 * a20);
592
- if (Math.abs(det) < 1e-8) return null;
593
- const invDet = 1 / det;
594
- const o = out ?? new Float32Array(9);
595
- o[0] = (a11 * a22 - a12 * a21) * invDet;
596
- o[1] = (a12 * a20 - a10 * a22) * invDet;
597
- o[2] = (a10 * a21 - a11 * a20) * invDet;
598
- o[3] = (a02 * a21 - a01 * a22) * invDet;
599
- o[4] = (a00 * a22 - a02 * a20) * invDet;
600
- o[5] = (a01 * a20 - a00 * a21) * invDet;
601
- o[6] = (a01 * a12 - a02 * a11) * invDet;
602
- o[7] = (a02 * a10 - a00 * a12) * invDet;
603
- o[8] = (a00 * a11 - a01 * a10) * invDet;
604
- return o;
605
- },
606
- /** Builds a matrix from a Transform2D (TRS order: translate * rotate * scale). */
607
- fromTransform(t, out) {
608
- const c = Math.cos(t.rotation);
609
- const s = Math.sin(t.rotation);
610
- const sx = t.scale.x;
611
- const sy = t.scale.y;
612
- const m = out ?? new Float32Array(9);
613
- m[0] = c * sx;
614
- m[1] = s * sx;
615
- m[2] = 0;
616
- m[3] = -s * sy;
617
- m[4] = c * sy;
618
- m[5] = 0;
619
- m[6] = t.position.x;
620
- m[7] = t.position.y;
621
- m[8] = 1;
622
- return m;
623
- }
624
- };
625
-
626
- // src/color.ts
98
+ const diff = angleDelta(target, cur);
99
+ return cur + (diff > maxStep ? maxStep : diff < -maxStep ? -maxStep : diff);
100
+ }
101
+ //#endregion
102
+ //#region src/color.ts
103
+ /**
104
+ * Converts a single sRGB channel to linear light.
105
+ * The 0.04045 threshold (modern WCAG errata) is numerically identical to the
106
+ * older 0.03928 for 8-bit inputs and is shared by OKLab and WCAG luminance.
107
+ */
627
108
  function srgbToLinear(c) {
628
- return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
109
+ return c <= .04045 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4;
629
110
  }
111
+ /** Converts a single linear-light channel back to sRGB. */
630
112
  function linearToSrgb(c) {
631
- return c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
632
- }
633
- var Color = {
634
- WHITE: { r: 1, g: 1, b: 1, a: 1 },
635
- BLACK: { r: 0, g: 0, b: 0, a: 1 },
636
- TRANSPARENT: { r: 0, g: 0, b: 0, a: 0 },
637
- /** Creates a Color from normalized 0-1 components. */
638
- create(r = 0, g = 0, b = 0, a = 1) {
639
- return { r, g, b, a };
640
- },
641
- /** Parses a hex string (#RGB, #RGBA, #RRGGBB, #RRGGBBAA). */
642
- fromHex(hex) {
643
- let h = hex.startsWith("#") ? hex.slice(1) : hex;
644
- if (h.length === 3) {
645
- h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
646
- } else if (h.length === 4) {
647
- h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2] + h[3] + h[3];
648
- }
649
- const r = Number.parseInt(h.slice(0, 2), 16) / 255;
650
- const g = Number.parseInt(h.slice(2, 4), 16) / 255;
651
- const b = Number.parseInt(h.slice(4, 6), 16) / 255;
652
- const a = h.length >= 8 ? Number.parseInt(h.slice(6, 8), 16) / 255 : 1;
653
- return { r, g, b, a };
654
- },
655
- /** Converts to hex string #RRGGBB or #RRGGBBAA if alpha < 1. */
656
- toHex(c) {
657
- const r = Math.round(clamp(c.r, 0, 1) * 255).toString(16).padStart(2, "0");
658
- const g = Math.round(clamp(c.g, 0, 1) * 255).toString(16).padStart(2, "0");
659
- const b = Math.round(clamp(c.b, 0, 1) * 255).toString(16).padStart(2, "0");
660
- if (c.a < 1) {
661
- const a = Math.round(clamp(c.a, 0, 1) * 255).toString(16).padStart(2, "0");
662
- return `#${r}${g}${b}${a}`;
663
- }
664
- return `#${r}${g}${b}`;
665
- },
666
- /** Linearly interpolates between two colors. */
667
- lerp(a, b, t) {
668
- return {
669
- r: a.r + (b.r - a.r) * t,
670
- g: a.g + (b.g - a.g) * t,
671
- b: a.b + (b.b - a.b) * t,
672
- a: a.a + (b.a - a.a) * t
673
- };
674
- },
675
- /** Component-wise multiplication. */
676
- multiply(a, b) {
677
- return {
678
- r: a.r * b.r,
679
- g: a.g * b.g,
680
- b: a.b * b.b,
681
- a: a.a * b.a
682
- };
683
- },
684
- /**
685
- * Converts to a Skia-compatible color number (0xAARRGGBB).
686
- * Compatible with @shopify/react-native-skia Skia.Color() format.
687
- */
688
- toSkColor(c) {
689
- const r = Math.round(clamp(c.r, 0, 1) * 255) & 255;
690
- const g = Math.round(clamp(c.g, 0, 1) * 255) & 255;
691
- const b = Math.round(clamp(c.b, 0, 1) * 255) & 255;
692
- const a = Math.round(clamp(c.a, 0, 1) * 255) & 255;
693
- return (a << 24 | r << 16 | g << 8 | b) >>> 0;
694
- },
695
- /** Converts sRGB Color to OKLab. Alpha is dropped. */
696
- toOklab(c) {
697
- const rl = srgbToLinear(c.r);
698
- const gl = srgbToLinear(c.g);
699
- const bl = srgbToLinear(c.b);
700
- const l = 0.4122214708 * rl + 0.5363325363 * gl + 0.0514459929 * bl;
701
- const m = 0.2119034982 * rl + 0.6806995451 * gl + 0.1073969566 * bl;
702
- const s = 0.0883024619 * rl + 0.2817188376 * gl + 0.6299787005 * bl;
703
- const l_ = Math.cbrt(l);
704
- const m_ = Math.cbrt(m);
705
- const s_ = Math.cbrt(s);
706
- return {
707
- L: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
708
- a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
709
- b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_
710
- };
711
- },
712
- /**
713
- * Converts OKLab to sRGB Color. Out-of-gamut results are clamped to [0,1];
714
- * round-trips are exact only for in-gamut colors.
715
- */
716
- fromOklab(lab, alpha = 1) {
717
- const l_ = lab.L + 0.3963377774 * lab.a + 0.2158037573 * lab.b;
718
- const m_ = lab.L - 0.1055613458 * lab.a - 0.0638541728 * lab.b;
719
- const s_ = lab.L - 0.0894841775 * lab.a - 1.291485548 * lab.b;
720
- const l = l_ ** 3;
721
- const m = m_ ** 3;
722
- const s = s_ ** 3;
723
- const rl = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
724
- const gl = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
725
- const bl = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
726
- return {
727
- r: clamp(linearToSrgb(rl), 0, 1),
728
- g: clamp(linearToSrgb(gl), 0, 1),
729
- b: clamp(linearToSrgb(bl), 0, 1),
730
- a: alpha
731
- };
732
- },
733
- /** Converts sRGB Color to OKLCH (polar OKLab). Achromatic colors get h = 0. */
734
- toOklch(c) {
735
- const lab = Color.toOklab(c);
736
- const C = Math.hypot(lab.a, lab.b);
737
- const h = C < 1e-7 ? 0 : (Math.atan2(lab.b, lab.a) * 180 / Math.PI + 360) % 360;
738
- return { L: lab.L, C, h };
739
- },
740
- /**
741
- * Converts OKLCH to sRGB Color. Hue is wrapped via ((h%360)+360)%360.
742
- * Out-of-gamut results are clamped; round-trips exact only in-gamut.
743
- */
744
- fromOklch(lch, alpha = 1) {
745
- const hWrapped = (lch.h % 360 + 360) % 360;
746
- const hRad = hWrapped * Math.PI / 180;
747
- return Color.fromOklab(
748
- { L: lch.L, a: lch.C * Math.cos(hRad), b: lch.C * Math.sin(hRad) },
749
- alpha
750
- );
751
- },
752
- /** Euclidean distance in OKLab space between two sRGB colors. */
753
- deltaEOk(a, b) {
754
- return Color.deltaEOkLab(Color.toOklab(a), Color.toOklab(b));
755
- },
756
- /** Euclidean distance between two OKLab values. */
757
- deltaEOkLab(a, b) {
758
- return Math.hypot(a.L - b.L, a.a - b.a, a.b - b.b);
759
- },
760
- /** WCAG 2.x relative luminance. White = 1, black = 0. */
761
- relativeLuminance(c) {
762
- return 0.2126 * srgbToLinear(c.r) + 0.7152 * srgbToLinear(c.g) + 0.0722 * srgbToLinear(c.b);
763
- },
764
- /** WCAG 2.x contrast ratio. Order-independent; range [1, 21]. */
765
- contrastRatio(fg, bg) {
766
- const l1 = Color.relativeLuminance(fg);
767
- const l2 = Color.relativeLuminance(bg);
768
- const lMax = Math.max(l1, l2);
769
- const lMin = Math.min(l1, l2);
770
- return (lMax + 0.05) / (lMin + 0.05);
771
- }
113
+ return c <= .0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - .055;
114
+ }
115
+ const Color = {
116
+ WHITE: {
117
+ r: 1,
118
+ g: 1,
119
+ b: 1,
120
+ a: 1
121
+ },
122
+ BLACK: {
123
+ r: 0,
124
+ g: 0,
125
+ b: 0,
126
+ a: 1
127
+ },
128
+ TRANSPARENT: {
129
+ r: 0,
130
+ g: 0,
131
+ b: 0,
132
+ a: 0
133
+ },
134
+ /** Creates a Color from normalized 0-1 components. */
135
+ create(r = 0, g = 0, b = 0, a = 1) {
136
+ return {
137
+ r,
138
+ g,
139
+ b,
140
+ a
141
+ };
142
+ },
143
+ /** Parses a hex string (#RGB, #RGBA, #RRGGBB, #RRGGBBAA). */
144
+ fromHex(hex) {
145
+ let h = hex.startsWith("#") ? hex.slice(1) : hex;
146
+ if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
147
+ else if (h.length === 4) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2] + h[3] + h[3];
148
+ return {
149
+ r: Number.parseInt(h.slice(0, 2), 16) / 255,
150
+ g: Number.parseInt(h.slice(2, 4), 16) / 255,
151
+ b: Number.parseInt(h.slice(4, 6), 16) / 255,
152
+ a: h.length >= 8 ? Number.parseInt(h.slice(6, 8), 16) / 255 : 1
153
+ };
154
+ },
155
+ /** Converts to hex string #RRGGBB or #RRGGBBAA if alpha < 1. */
156
+ toHex(c) {
157
+ const r = Math.round(clamp(c.r, 0, 1) * 255).toString(16).padStart(2, "0");
158
+ const g = Math.round(clamp(c.g, 0, 1) * 255).toString(16).padStart(2, "0");
159
+ const b = Math.round(clamp(c.b, 0, 1) * 255).toString(16).padStart(2, "0");
160
+ if (c.a < 1) return `#${r}${g}${b}${Math.round(clamp(c.a, 0, 1) * 255).toString(16).padStart(2, "0")}`;
161
+ return `#${r}${g}${b}`;
162
+ },
163
+ /** Linearly interpolates between two colors. */
164
+ lerp(a, b, t) {
165
+ return {
166
+ r: a.r + (b.r - a.r) * t,
167
+ g: a.g + (b.g - a.g) * t,
168
+ b: a.b + (b.b - a.b) * t,
169
+ a: a.a + (b.a - a.a) * t
170
+ };
171
+ },
172
+ /** Component-wise multiplication. */
173
+ multiply(a, b) {
174
+ return {
175
+ r: a.r * b.r,
176
+ g: a.g * b.g,
177
+ b: a.b * b.b,
178
+ a: a.a * b.a
179
+ };
180
+ },
181
+ /**
182
+ * Converts to a Skia-compatible color number (0xAARRGGBB).
183
+ * Compatible with @shopify/react-native-skia Skia.Color() format.
184
+ */
185
+ toSkColor(c) {
186
+ const r = Math.round(clamp(c.r, 0, 1) * 255) & 255;
187
+ const g = Math.round(clamp(c.g, 0, 1) * 255) & 255;
188
+ const b = Math.round(clamp(c.b, 0, 1) * 255) & 255;
189
+ return ((Math.round(clamp(c.a, 0, 1) * 255) & 255) << 24 | r << 16 | g << 8 | b) >>> 0;
190
+ },
191
+ /** Converts sRGB Color to OKLab. Alpha is dropped. */
192
+ toOklab(c) {
193
+ const rl = srgbToLinear(c.r);
194
+ const gl = srgbToLinear(c.g);
195
+ const bl = srgbToLinear(c.b);
196
+ const l = .4122214708 * rl + .5363325363 * gl + .0514459929 * bl;
197
+ const m = .2119034982 * rl + .6806995451 * gl + .1073969566 * bl;
198
+ const s = .0883024619 * rl + .2817188376 * gl + .6299787005 * bl;
199
+ const l_ = Math.cbrt(l);
200
+ const m_ = Math.cbrt(m);
201
+ const s_ = Math.cbrt(s);
202
+ return {
203
+ L: .2104542553 * l_ + .793617785 * m_ - .0040720468 * s_,
204
+ a: 1.9779984951 * l_ - 2.428592205 * m_ + .4505937099 * s_,
205
+ b: .0259040371 * l_ + .7827717662 * m_ - .808675766 * s_
206
+ };
207
+ },
208
+ /**
209
+ * Converts OKLab to sRGB Color. Out-of-gamut results are clamped to [0,1];
210
+ * round-trips are exact only for in-gamut colors.
211
+ */
212
+ fromOklab(lab, alpha = 1) {
213
+ const l_ = lab.L + .3963377774 * lab.a + .2158037573 * lab.b;
214
+ const m_ = lab.L - .1055613458 * lab.a - .0638541728 * lab.b;
215
+ const s_ = lab.L - .0894841775 * lab.a - 1.291485548 * lab.b;
216
+ const l = l_ ** 3;
217
+ const m = m_ ** 3;
218
+ const s = s_ ** 3;
219
+ const rl = 4.0767416621 * l - 3.3077115913 * m + .2309699292 * s;
220
+ const gl = -1.2684380046 * l + 2.6097574011 * m - .3413193965 * s;
221
+ const bl = -.0041960863 * l - .7034186147 * m + 1.707614701 * s;
222
+ return {
223
+ r: clamp(linearToSrgb(rl), 0, 1),
224
+ g: clamp(linearToSrgb(gl), 0, 1),
225
+ b: clamp(linearToSrgb(bl), 0, 1),
226
+ a: alpha
227
+ };
228
+ },
229
+ /** Converts sRGB Color to OKLCH (polar OKLab). Achromatic colors get h = 0. */
230
+ toOklch(c) {
231
+ const lab = Color.toOklab(c);
232
+ const C = Math.hypot(lab.a, lab.b);
233
+ const h = C < 1e-7 ? 0 : (Math.atan2(lab.b, lab.a) * 180 / Math.PI + 360) % 360;
234
+ return {
235
+ L: lab.L,
236
+ C,
237
+ h
238
+ };
239
+ },
240
+ /**
241
+ * Converts OKLCH to sRGB Color. Hue is wrapped via ((h%360)+360)%360.
242
+ * Out-of-gamut results are clamped; round-trips exact only in-gamut.
243
+ */
244
+ fromOklch(lch, alpha = 1) {
245
+ const hRad = (lch.h % 360 + 360) % 360 * Math.PI / 180;
246
+ return Color.fromOklab({
247
+ L: lch.L,
248
+ a: lch.C * Math.cos(hRad),
249
+ b: lch.C * Math.sin(hRad)
250
+ }, alpha);
251
+ },
252
+ /** Euclidean distance in OKLab space between two sRGB colors. */
253
+ deltaEOk(a, b) {
254
+ return Color.deltaEOkLab(Color.toOklab(a), Color.toOklab(b));
255
+ },
256
+ /** Euclidean distance between two OKLab values. */
257
+ deltaEOkLab(a, b) {
258
+ return Math.hypot(a.L - b.L, a.a - b.a, a.b - b.b);
259
+ },
260
+ /** WCAG 2.x relative luminance. White = 1, black = 0. */
261
+ relativeLuminance(c) {
262
+ return .2126 * srgbToLinear(c.r) + .7152 * srgbToLinear(c.g) + .0722 * srgbToLinear(c.b);
263
+ },
264
+ /** WCAG 2.x contrast ratio. Order-independent; range [1, 21]. */
265
+ contrastRatio(fg, bg) {
266
+ const l1 = Color.relativeLuminance(fg);
267
+ const l2 = Color.relativeLuminance(bg);
268
+ const lMax = Math.max(l1, l2);
269
+ const lMin = Math.min(l1, l2);
270
+ return (lMax + .05) / (lMin + .05);
271
+ }
772
272
  };
773
-
774
- // src/easings.ts
775
- var PI = Math.PI;
776
- var HALF_PI = PI / 2;
777
- var TAU = PI * 2;
273
+ //#endregion
274
+ //#region src/easings.ts
275
+ const PI = Math.PI;
276
+ const HALF_PI = PI / 2;
277
+ const TAU = PI * 2;
778
278
  function linear(t) {
779
- return t;
279
+ return t;
780
280
  }
781
281
  function step(t) {
782
- return t < 1 ? 0 : 1;
282
+ return t < 1 ? 0 : 1;
783
283
  }
784
284
  function easeInQuad(t) {
785
- return t * t;
285
+ return t * t;
786
286
  }
787
287
  function easeOutQuad(t) {
788
- return t * (2 - t);
288
+ return t * (2 - t);
789
289
  }
790
290
  function easeInOutQuad(t) {
791
- return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
291
+ return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
792
292
  }
793
293
  function easeInCubic(t) {
794
- return t * t * t;
294
+ return t * t * t;
795
295
  }
796
296
  function easeOutCubic(t) {
797
- const t1 = t - 1;
798
- return t1 * t1 * t1 + 1;
297
+ const t1 = t - 1;
298
+ return t1 * t1 * t1 + 1;
799
299
  }
800
300
  function easeInOutCubic(t) {
801
- return t < 0.5 ? 4 * t * t * t : 1 + (t - 1) * (2 * t - 2) * (2 * t - 2);
301
+ return t < .5 ? 4 * t * t * t : 1 + (t - 1) * (2 * t - 2) * (2 * t - 2);
802
302
  }
803
303
  function easeInQuart(t) {
804
- return t * t * t * t;
304
+ return t * t * t * t;
805
305
  }
806
306
  function easeOutQuart(t) {
807
- const t1 = t - 1;
808
- return 1 - t1 * t1 * t1 * t1;
307
+ const t1 = t - 1;
308
+ return 1 - t1 * t1 * t1 * t1;
809
309
  }
810
310
  function easeInOutQuart(t) {
811
- const t1 = t - 1;
812
- return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * t1 * t1 * t1 * t1;
311
+ const t1 = t - 1;
312
+ return t < .5 ? 8 * t * t * t * t : 1 - 8 * t1 * t1 * t1 * t1;
813
313
  }
814
314
  function easeInQuint(t) {
815
- return t * t * t * t * t;
315
+ return t * t * t * t * t;
816
316
  }
817
317
  function easeOutQuint(t) {
818
- const t1 = t - 1;
819
- return 1 + t1 * t1 * t1 * t1 * t1;
318
+ const t1 = t - 1;
319
+ return 1 + t1 * t1 * t1 * t1 * t1;
820
320
  }
821
321
  function easeInOutQuint(t) {
822
- const t1 = t - 1;
823
- return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * t1 * t1 * t1 * t1 * t1;
322
+ const t1 = t - 1;
323
+ return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * t1 * t1 * t1 * t1 * t1;
824
324
  }
825
325
  function easeInSine(t) {
826
- return 1 - Math.cos(t * HALF_PI);
326
+ return 1 - Math.cos(t * HALF_PI);
827
327
  }
828
328
  function easeOutSine(t) {
829
- return Math.sin(t * HALF_PI);
329
+ return Math.sin(t * HALF_PI);
830
330
  }
831
331
  function easeInOutSine(t) {
832
- return 0.5 * (1 - Math.cos(PI * t));
332
+ return .5 * (1 - Math.cos(PI * t));
833
333
  }
834
334
  function easeInExpo(t) {
835
- return t === 0 ? 0 : 2 ** (10 * (t - 1));
335
+ return t === 0 ? 0 : 2 ** (10 * (t - 1));
836
336
  }
837
337
  function easeOutExpo(t) {
838
- return t === 1 ? 1 : 1 - 2 ** (-10 * t);
338
+ return t === 1 ? 1 : 1 - 2 ** (-10 * t);
839
339
  }
840
340
  function easeInOutExpo(t) {
841
- if (t === 0) return 0;
842
- if (t === 1) return 1;
843
- return t < 0.5 ? 0.5 * 2 ** (20 * t - 10) : 1 - 0.5 * 2 ** (-20 * t + 10);
341
+ if (t === 0) return 0;
342
+ if (t === 1) return 1;
343
+ return t < .5 ? .5 * 2 ** (20 * t - 10) : 1 - .5 * 2 ** (-20 * t + 10);
844
344
  }
845
345
  function easeInCirc(t) {
846
- return 1 - Math.sqrt(1 - t * t);
346
+ return 1 - Math.sqrt(1 - t * t);
847
347
  }
848
348
  function easeOutCirc(t) {
849
- const t1 = t - 1;
850
- return Math.sqrt(1 - t1 * t1);
349
+ const t1 = t - 1;
350
+ return Math.sqrt(1 - t1 * t1);
851
351
  }
852
352
  function easeInOutCirc(t) {
853
- return t < 0.5 ? 0.5 * (1 - Math.sqrt(1 - 4 * t * t)) : 0.5 * (Math.sqrt(1 - (2 * t - 2) * (2 * t - 2)) + 1);
353
+ return t < .5 ? .5 * (1 - Math.sqrt(1 - 4 * t * t)) : .5 * (Math.sqrt(1 - (2 * t - 2) * (2 * t - 2)) + 1);
854
354
  }
855
- var S = 1.70158;
856
- var S2 = S * 1.525;
355
+ const S = 1.70158;
356
+ const S2 = S * 1.525;
857
357
  function easeInBack(t) {
858
- return t * t * ((S + 1) * t - S);
358
+ return t * t * (2.70158 * t - S);
859
359
  }
860
360
  function easeOutBack(t) {
861
- const t1 = t - 1;
862
- return t1 * t1 * ((S + 1) * t1 + S) + 1;
361
+ const t1 = t - 1;
362
+ return t1 * t1 * (2.70158 * t1 + S) + 1;
863
363
  }
864
364
  function easeInOutBack(t) {
865
- if (t < 0.5) {
866
- const t22 = 2 * t;
867
- return 0.5 * (t22 * t22 * ((S2 + 1) * t22 - S2));
868
- }
869
- const t2 = 2 * t - 2;
870
- return 0.5 * (t2 * t2 * ((S2 + 1) * t2 + S2) + 2);
365
+ if (t < .5) {
366
+ const t2 = 2 * t;
367
+ return .5 * (t2 * t2 * (3.5949095 * t2 - S2));
368
+ }
369
+ const t2 = 2 * t - 2;
370
+ return .5 * (t2 * t2 * (3.5949095 * t2 + S2) + 2);
871
371
  }
872
372
  function easeInElastic(t) {
873
- if (t === 0 || t === 1) return t;
874
- return -(2 ** (10 * t - 10)) * Math.sin((t * 10 - 10.75) * (TAU / 3));
373
+ if (t === 0 || t === 1) return t;
374
+ return -(2 ** (10 * t - 10)) * Math.sin((t * 10 - 10.75) * (TAU / 3));
875
375
  }
876
376
  function easeOutElastic(t) {
877
- if (t === 0 || t === 1) return t;
878
- return 2 ** (-10 * t) * Math.sin((t * 10 - 0.75) * (TAU / 3)) + 1;
377
+ if (t === 0 || t === 1) return t;
378
+ return 2 ** (-10 * t) * Math.sin((t * 10 - .75) * (TAU / 3)) + 1;
879
379
  }
880
380
  function easeInOutElastic(t) {
881
- if (t === 0 || t === 1) return t;
882
- const p = TAU / 4.5;
883
- return t < 0.5 ? -0.5 * 2 ** (20 * t - 10) * Math.sin((20 * t - 11.125) * p) : 0.5 * 2 ** (-20 * t + 10) * Math.sin((20 * t - 11.125) * p) + 1;
381
+ if (t === 0 || t === 1) return t;
382
+ const p = TAU / 4.5;
383
+ return t < .5 ? -.5 * 2 ** (20 * t - 10) * Math.sin((20 * t - 11.125) * p) : .5 * 2 ** (-20 * t + 10) * Math.sin((20 * t - 11.125) * p) + 1;
884
384
  }
885
385
  function easeOutBounce(t) {
886
- if (t < 1 / 2.75) {
887
- return 7.5625 * t * t;
888
- }
889
- if (t < 2 / 2.75) {
890
- const t22 = t - 1.5 / 2.75;
891
- return 7.5625 * t22 * t22 + 0.75;
892
- }
893
- if (t < 2.5 / 2.75) {
894
- const t22 = t - 2.25 / 2.75;
895
- return 7.5625 * t22 * t22 + 0.9375;
896
- }
897
- const t2 = t - 2.625 / 2.75;
898
- return 7.5625 * t2 * t2 + 0.984375;
386
+ if (t < 1 / 2.75) return 7.5625 * t * t;
387
+ if (t < 2 / 2.75) {
388
+ const t2 = t - 1.5 / 2.75;
389
+ return 7.5625 * t2 * t2 + .75;
390
+ }
391
+ if (t < 2.5 / 2.75) {
392
+ const t2 = t - 2.25 / 2.75;
393
+ return 7.5625 * t2 * t2 + .9375;
394
+ }
395
+ const t2 = t - 2.625 / 2.75;
396
+ return 7.5625 * t2 * t2 + .984375;
899
397
  }
900
398
  function easeInBounce(t) {
901
- return 1 - easeOutBounce(1 - t);
399
+ return 1 - easeOutBounce(1 - t);
902
400
  }
903
401
  function easeInOutBounce(t) {
904
- return t < 0.5 ? 0.5 * easeInBounce(t * 2) : 0.5 * easeOutBounce(t * 2 - 1) + 0.5;
402
+ return t < .5 ? .5 * easeInBounce(t * 2) : .5 * easeOutBounce(t * 2 - 1) + .5;
905
403
  }
906
404
  function springIn(t) {
907
- return t * t * ((1.70158 + 1) * t - 1.70158);
405
+ return t * t * (2.70158 * t - 1.70158);
908
406
  }
909
407
  function springOut(t) {
910
- const t1 = t - 1;
911
- return t1 * t1 * ((1.70158 + 1) * t1 + 1.70158) + 1;
408
+ const t1 = t - 1;
409
+ return t1 * t1 * (2.70158 * t1 + 1.70158) + 1;
912
410
  }
913
411
  function springInOut(t) {
914
- if (t < 0.5) {
915
- const t22 = 2 * t;
916
- return 0.5 * t22 * t22 * ((S2 + 1) * t22 - S2);
917
- }
918
- const t2 = 2 * t - 2;
919
- return 0.5 * (t2 * t2 * ((S2 + 1) * t2 + S2) + 2);
412
+ if (t < .5) {
413
+ const t2 = 2 * t;
414
+ return .5 * t2 * t2 * (3.5949095 * t2 - S2);
415
+ }
416
+ const t2 = 2 * t - 2;
417
+ return .5 * (t2 * t2 * (3.5949095 * t2 + S2) + 2);
418
+ }
419
+ //#endregion
420
+ //#region src/intersection.ts
421
+ /** Returns true if point is inside rect (inclusive). */
422
+ function pointInRect(p, r) {
423
+ return p.x >= r.x && p.x <= r.x + r.width && p.y >= r.y && p.y <= r.y + r.height;
920
424
  }
921
-
922
- // src/random.ts
425
+ /** Returns true if point is inside circle (inclusive). */
426
+ function pointInCircle(p, c) {
427
+ const dx = p.x - c.x;
428
+ const dy = p.y - c.y;
429
+ return dx * dx + dy * dy <= c.radius * c.radius;
430
+ }
431
+ /** Returns true if two rects overlap. */
432
+ function rectVsRect(a, b) {
433
+ return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
434
+ }
435
+ /** Returns true if rect and circle overlap. */
436
+ function rectVsCircle(r, c) {
437
+ const closestX = c.x < r.x ? r.x : c.x > r.x + r.width ? r.x + r.width : c.x;
438
+ const closestY = c.y < r.y ? r.y : c.y > r.y + r.height ? r.y + r.height : c.y;
439
+ const dx = c.x - closestX;
440
+ const dy = c.y - closestY;
441
+ return dx * dx + dy * dy <= c.radius * c.radius;
442
+ }
443
+ /** Returns true if two circles overlap. */
444
+ function circleVsCircle(a, b) {
445
+ const dx = b.x - a.x;
446
+ const dy = b.y - a.y;
447
+ const radSum = a.radius + b.radius;
448
+ return dx * dx + dy * dy < radSum * radSum;
449
+ }
450
+ /** Line segment intersection with AABB. Uses Liang-Barsky algorithm. */
451
+ function lineVsRect(x1, y1, x2, y2, r) {
452
+ const dx = x2 - x1;
453
+ const dy = y2 - y1;
454
+ let tMin = 0;
455
+ let tMax = 1;
456
+ const edges = [
457
+ {
458
+ p: -dx,
459
+ q: x1 - r.x
460
+ },
461
+ {
462
+ p: dx,
463
+ q: r.x + r.width - x1
464
+ },
465
+ {
466
+ p: -dy,
467
+ q: y1 - r.y
468
+ },
469
+ {
470
+ p: dy,
471
+ q: r.y + r.height - y1
472
+ }
473
+ ];
474
+ for (const { p, q } of edges) if (Math.abs(p) < 1e-12) {
475
+ if (q < 0) return false;
476
+ } else {
477
+ const t = q / p;
478
+ if (p < 0) {
479
+ if (t > tMax) return false;
480
+ if (t > tMin) tMin = t;
481
+ } else {
482
+ if (t < tMin) return false;
483
+ if (t < tMax) tMax = t;
484
+ }
485
+ }
486
+ return tMin <= tMax;
487
+ }
488
+ /** Line segment intersection with circle. */
489
+ function lineVsCircle(x1, y1, x2, y2, c) {
490
+ const dx = x2 - x1;
491
+ const dy = y2 - y1;
492
+ const fx = x1 - c.x;
493
+ const fy = y1 - c.y;
494
+ const a = dx * dx + dy * dy;
495
+ const b = 2 * (fx * dx + fy * dy);
496
+ const cc = fx * fx + fy * fy - c.radius * c.radius;
497
+ if (a < 1e-12) return cc <= 0;
498
+ let discriminant = b * b - 4 * a * cc;
499
+ if (discriminant < 0) return false;
500
+ discriminant = Math.sqrt(discriminant);
501
+ const t1 = (-b - discriminant) / (2 * a);
502
+ const t2 = (-b + discriminant) / (2 * a);
503
+ if (t1 >= 0 && t1 <= 1) return true;
504
+ if (t2 >= 0 && t2 <= 1) return true;
505
+ if (t1 < 0 && t2 > 1) return true;
506
+ return false;
507
+ }
508
+ /** Ray intersection with AABB. Returns t (distance) or null if no hit. */
509
+ function rayVsRect(originX, originY, dirX, dirY, r) {
510
+ const invDx = dirX !== 0 ? 1 / dirX : Number.POSITIVE_INFINITY;
511
+ const invDy = dirY !== 0 ? 1 / dirY : Number.POSITIVE_INFINITY;
512
+ const tx1 = (r.x - originX) * invDx;
513
+ const tx2 = (r.x + r.width - originX) * invDx;
514
+ const ty1 = (r.y - originY) * invDy;
515
+ const ty2 = (r.y + r.height - originY) * invDy;
516
+ const tMin = Math.max(Math.min(tx1, tx2), Math.min(ty1, ty2));
517
+ const tMax = Math.min(Math.max(tx1, tx2), Math.max(ty1, ty2));
518
+ if (tMax < 0 || tMin > tMax) return null;
519
+ return tMin >= 0 ? tMin : tMax;
520
+ }
521
+ //#endregion
522
+ //#region src/mat3.ts
523
+ const Mat3 = {
524
+ /** Creates a new identity matrix. */
525
+ create() {
526
+ const m = /* @__PURE__ */ new Float32Array(9);
527
+ m[0] = 1;
528
+ m[4] = 1;
529
+ m[8] = 1;
530
+ return m;
531
+ },
532
+ /** Sets m to identity. */
533
+ identity(m) {
534
+ m[0] = 1;
535
+ m[1] = 0;
536
+ m[2] = 0;
537
+ m[3] = 0;
538
+ m[4] = 1;
539
+ m[5] = 0;
540
+ m[6] = 0;
541
+ m[7] = 0;
542
+ m[8] = 1;
543
+ return m;
544
+ },
545
+ /** Multiplies a * b, writes into out (or allocates). */
546
+ multiply(a, b, out) {
547
+ const a00 = a[0], a01 = a[3], a02 = a[6];
548
+ const a10 = a[1], a11 = a[4], a12 = a[7];
549
+ const a20 = a[2], a21 = a[5], a22 = a[8];
550
+ const b00 = b[0], b01 = b[3], b02 = b[6];
551
+ const b10 = b[1], b11 = b[4], b12 = b[7];
552
+ const b20 = b[2], b21 = b[5], b22 = b[8];
553
+ const o = out ?? /* @__PURE__ */ new Float32Array(9);
554
+ o[0] = a00 * b00 + a01 * b10 + a02 * b20;
555
+ o[1] = a10 * b00 + a11 * b10 + a12 * b20;
556
+ o[2] = a20 * b00 + a21 * b10 + a22 * b20;
557
+ o[3] = a00 * b01 + a01 * b11 + a02 * b21;
558
+ o[4] = a10 * b01 + a11 * b11 + a12 * b21;
559
+ o[5] = a20 * b01 + a21 * b11 + a22 * b21;
560
+ o[6] = a00 * b02 + a01 * b12 + a02 * b22;
561
+ o[7] = a10 * b02 + a11 * b12 + a12 * b22;
562
+ o[8] = a20 * b02 + a21 * b12 + a22 * b22;
563
+ return o;
564
+ },
565
+ /** Creates a translation matrix. */
566
+ translate(tx, ty, out) {
567
+ const m = out ?? /* @__PURE__ */ new Float32Array(9);
568
+ m[0] = 1;
569
+ m[1] = 0;
570
+ m[2] = 0;
571
+ m[3] = 0;
572
+ m[4] = 1;
573
+ m[5] = 0;
574
+ m[6] = tx;
575
+ m[7] = ty;
576
+ m[8] = 1;
577
+ return m;
578
+ },
579
+ /** Creates a rotation matrix (radians). */
580
+ rotate(angle, out) {
581
+ const c = Math.cos(angle);
582
+ const s = Math.sin(angle);
583
+ const m = out ?? /* @__PURE__ */ new Float32Array(9);
584
+ m[0] = c;
585
+ m[1] = s;
586
+ m[2] = 0;
587
+ m[3] = -s;
588
+ m[4] = c;
589
+ m[5] = 0;
590
+ m[6] = 0;
591
+ m[7] = 0;
592
+ m[8] = 1;
593
+ return m;
594
+ },
595
+ /** Creates a scale matrix. */
596
+ scale(sx, sy, out) {
597
+ const m = out ?? /* @__PURE__ */ new Float32Array(9);
598
+ m[0] = sx;
599
+ m[1] = 0;
600
+ m[2] = 0;
601
+ m[3] = 0;
602
+ m[4] = sy;
603
+ m[5] = 0;
604
+ m[6] = 0;
605
+ m[7] = 0;
606
+ m[8] = 1;
607
+ return m;
608
+ },
609
+ /** Inverts the matrix. Returns null if singular. */
610
+ invert(m, out) {
611
+ const a00 = m[0], a01 = m[3], a02 = m[6];
612
+ const a10 = m[1], a11 = m[4], a12 = m[7];
613
+ const a20 = m[2], a21 = m[5], a22 = m[8];
614
+ const det = a00 * (a11 * a22 - a12 * a21) - a01 * (a10 * a22 - a12 * a20) + a02 * (a10 * a21 - a11 * a20);
615
+ if (Math.abs(det) < 1e-8) return null;
616
+ const invDet = 1 / det;
617
+ const o = out ?? /* @__PURE__ */ new Float32Array(9);
618
+ o[0] = (a11 * a22 - a12 * a21) * invDet;
619
+ o[1] = (a12 * a20 - a10 * a22) * invDet;
620
+ o[2] = (a10 * a21 - a11 * a20) * invDet;
621
+ o[3] = (a02 * a21 - a01 * a22) * invDet;
622
+ o[4] = (a00 * a22 - a02 * a20) * invDet;
623
+ o[5] = (a01 * a20 - a00 * a21) * invDet;
624
+ o[6] = (a01 * a12 - a02 * a11) * invDet;
625
+ o[7] = (a02 * a10 - a00 * a12) * invDet;
626
+ o[8] = (a00 * a11 - a01 * a10) * invDet;
627
+ return o;
628
+ },
629
+ /** Builds a matrix from a Transform2D (TRS order: translate * rotate * scale). */
630
+ fromTransform(t, out) {
631
+ const c = Math.cos(t.rotation);
632
+ const s = Math.sin(t.rotation);
633
+ const sx = t.scale.x;
634
+ const sy = t.scale.y;
635
+ const m = out ?? /* @__PURE__ */ new Float32Array(9);
636
+ m[0] = c * sx;
637
+ m[1] = s * sx;
638
+ m[2] = 0;
639
+ m[3] = -s * sy;
640
+ m[4] = c * sy;
641
+ m[5] = 0;
642
+ m[6] = t.position.x;
643
+ m[7] = t.position.y;
644
+ m[8] = 1;
645
+ return m;
646
+ }
647
+ };
648
+ //#endregion
649
+ //#region src/random.ts
650
+ /** Seeded PRNG using mulberry32 algorithm. */
923
651
  var SeededRNG = class {
924
- state;
925
- constructor(seed) {
926
- this.state = seed | 0;
927
- }
928
- /** Returns a float in [0, 1). */
929
- next() {
930
- this.state = this.state + 1831565813 | 0;
931
- let t = Math.imul(this.state ^ this.state >>> 15, 1 | this.state);
932
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
933
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
934
- }
935
- /** Returns a float in [min, max). */
936
- float(min, max) {
937
- return min + this.next() * (max - min);
938
- }
939
- /** Returns an integer in [min, max] (inclusive). */
940
- int(min, max) {
941
- return Math.floor(this.float(min, max + 1));
942
- }
652
+ state;
653
+ constructor(seed) {
654
+ this.state = seed | 0;
655
+ }
656
+ /** Returns a float in [0, 1). */
657
+ next() {
658
+ this.state = this.state + 1831565813 | 0;
659
+ let t = Math.imul(this.state ^ this.state >>> 15, 1 | this.state);
660
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
661
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
662
+ }
663
+ /** Returns a float in [min, max). */
664
+ float(min, max) {
665
+ return min + this.next() * (max - min);
666
+ }
667
+ /** Returns an integer in [min, max] (inclusive). */
668
+ int(min, max) {
669
+ return Math.floor(this.float(min, max + 1));
670
+ }
943
671
  };
672
+ /** Returns a random float in [min, max). */
944
673
  function randomFloat(min, max) {
945
- return min + Math.random() * (max - min);
674
+ return min + Math.random() * (max - min);
946
675
  }
676
+ /** Returns a random integer in [min, max] (inclusive). */
947
677
  function randomInt(min, max) {
948
- return Math.floor(randomFloat(min, max + 1));
678
+ return Math.floor(randomFloat(min, max + 1));
949
679
  }
680
+ /** Returns a random Vec2 within a rect. */
950
681
  function randomVec2InRect(r) {
951
- return {
952
- x: r.x + Math.random() * r.width,
953
- y: r.y + Math.random() * r.height
954
- };
682
+ return {
683
+ x: r.x + Math.random() * r.width,
684
+ y: r.y + Math.random() * r.height
685
+ };
955
686
  }
687
+ /** Returns a random point inside a circle (uniform distribution). */
956
688
  function randomPointInCircle(cx, cy, radius) {
957
- const r = radius * Math.sqrt(Math.random());
958
- const angle = Math.random() * Math.PI * 2;
959
- return {
960
- x: cx + r * Math.cos(angle),
961
- y: cy + r * Math.sin(angle)
962
- };
963
- }
689
+ const r = radius * Math.sqrt(Math.random());
690
+ const angle = Math.random() * Math.PI * 2;
691
+ return {
692
+ x: cx + r * Math.cos(angle),
693
+ y: cy + r * Math.sin(angle)
694
+ };
695
+ }
696
+ /** Fisher-Yates shuffle (in-place). Returns the array. */
964
697
  function shuffle(array) {
965
- for (let i = array.length - 1; i > 0; i--) {
966
- const j = Math.floor(Math.random() * (i + 1));
967
- const tmp = array[i];
968
- array[i] = array[j];
969
- array[j] = tmp;
970
- }
971
- return array;
972
- }
698
+ for (let i = array.length - 1; i > 0; i--) {
699
+ const j = Math.floor(Math.random() * (i + 1));
700
+ const tmp = array[i];
701
+ array[i] = array[j];
702
+ array[j] = tmp;
703
+ }
704
+ return array;
705
+ }
706
+ /** Returns a random element from the array. */
973
707
  function choose(array) {
974
- if (array.length === 0) return void 0;
975
- return array[Math.floor(Math.random() * array.length)];
708
+ if (array.length === 0) return void 0;
709
+ return array[Math.floor(Math.random() * array.length)];
976
710
  }
711
+ /**
712
+ * Picks an index from a weight array using a cumulative walk.
713
+ * `roll01` must be in [0, 1); returns 0 for an empty or all-zero array.
714
+ */
977
715
  function weightedIndex(weights, roll01) {
978
- let total = 0;
979
- for (let i = 0; i < weights.length; i++) total += weights[i] ?? 0;
980
- if (total <= 0) return 0;
981
- const target = roll01 * total;
982
- let acc = 0;
983
- for (let i = 0; i < weights.length; i++) {
984
- acc += weights[i] ?? 0;
985
- if (target < acc) return i;
986
- }
987
- return weights.length - 1;
988
- }
716
+ let total = 0;
717
+ for (let i = 0; i < weights.length; i++) total += weights[i] ?? 0;
718
+ if (total <= 0) return 0;
719
+ const target = roll01 * total;
720
+ let acc = 0;
721
+ for (let i = 0; i < weights.length; i++) {
722
+ acc += weights[i] ?? 0;
723
+ if (target < acc) return i;
724
+ }
725
+ return weights.length - 1;
726
+ }
727
+ /**
728
+ * Picks an index from `items` using a per-item weight accessor.
729
+ * `roll01` must be in [0, 1); returns 0 for an empty or all-zero-weight array.
730
+ */
989
731
  function weightedIndexBy(items, weight, roll01) {
990
- let total = 0;
991
- for (let i = 0; i < items.length; i++) total += weight(items[i]);
992
- if (total <= 0) return 0;
993
- const target = roll01 * total;
994
- let acc = 0;
995
- for (let i = 0; i < items.length; i++) {
996
- acc += weight(items[i]);
997
- if (target < acc) return i;
998
- }
999
- return items.length - 1;
1000
- }
732
+ let total = 0;
733
+ for (let i = 0; i < items.length; i++) total += weight(items[i]);
734
+ if (total <= 0) return 0;
735
+ const target = roll01 * total;
736
+ let acc = 0;
737
+ for (let i = 0; i < items.length; i++) {
738
+ acc += weight(items[i]);
739
+ if (target < acc) return i;
740
+ }
741
+ return items.length - 1;
742
+ }
743
+ /**
744
+ * Picks an item from `items` using a per-item weight accessor.
745
+ * Returns `undefined` for an empty array; returns the last item on float-edge.
746
+ */
1001
747
  function weightedPick(items, weight, roll01) {
1002
- if (items.length === 0) return void 0;
1003
- return items[weightedIndexBy(items, weight, roll01)];
1004
- }
748
+ if (items.length === 0) return void 0;
749
+ return items[weightedIndexBy(items, weight, roll01)];
750
+ }
751
+ /**
752
+ * 32-bit avalanche finalizer (lowbias32). Mixes `seed` so that adjacent seeds
753
+ * decorrelate into different output values. Returns an unsigned 32-bit integer.
754
+ *
755
+ * Constants from https://nullprogram.com/blog/2018/07/31/ (lowbias32).
756
+ */
1005
757
  function hashU32(seed) {
1006
- let h = (seed | 0) >>> 0;
1007
- h = Math.imul(h ^ h >>> 16, 73244475) >>> 0;
1008
- h = Math.imul(h ^ h >>> 16, 73244475) >>> 0;
1009
- return (h ^ h >>> 16) >>> 0;
1010
- }
758
+ let h = (seed | 0) >>> 0;
759
+ h = Math.imul(h ^ h >>> 16, 73244475) >>> 0;
760
+ h = Math.imul(h ^ h >>> 16, 73244475) >>> 0;
761
+ return (h ^ h >>> 16) >>> 0;
762
+ }
763
+ /**
764
+ * Deterministically maps `seed` to an index in [0, count).
765
+ * Single-entry pools short-circuit to 0. Uses `hashU32` to decorrelate
766
+ * adjacent seeds before the modulo.
767
+ */
1011
768
  function seededIndex(seed, count) {
1012
- if (count <= 1) return 0;
1013
- return hashU32(seed) % count;
1014
- }
1015
-
1016
- // src/reward.ts
769
+ if (count <= 1) return 0;
770
+ return hashU32(seed) % count;
771
+ }
772
+ //#endregion
773
+ //#region src/rect.ts
774
+ const Rect = {
775
+ /** Returns true if point is inside rect (inclusive). */
776
+ contains(r, p) {
777
+ return p.x >= r.x && p.x <= r.x + r.width && p.y >= r.y && p.y <= r.y + r.height;
778
+ },
779
+ /** Returns true if two rects overlap (share area). */
780
+ intersects(a, b) {
781
+ return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
782
+ },
783
+ /** Returns the overlapping rect, or null if no overlap. */
784
+ intersection(a, b) {
785
+ const x = Math.max(a.x, b.x);
786
+ const y = Math.max(a.y, b.y);
787
+ const right = Math.min(a.x + a.width, b.x + b.width);
788
+ const bottom = Math.min(a.y + a.height, b.y + b.height);
789
+ if (right <= x || bottom <= y) return null;
790
+ return {
791
+ x,
792
+ y,
793
+ width: right - x,
794
+ height: bottom - y
795
+ };
796
+ },
797
+ /** Returns the smallest rect containing both rects. */
798
+ union(a, b) {
799
+ const x = Math.min(a.x, b.x);
800
+ const y = Math.min(a.y, b.y);
801
+ const right = Math.max(a.x + a.width, b.x + b.width);
802
+ const bottom = Math.max(a.y + a.height, b.y + b.height);
803
+ return {
804
+ x,
805
+ y,
806
+ width: right - x,
807
+ height: bottom - y
808
+ };
809
+ },
810
+ /** Creates a rect from an array of points (bounding box). */
811
+ fromPoints(points) {
812
+ if (points.length === 0) return {
813
+ x: 0,
814
+ y: 0,
815
+ width: 0,
816
+ height: 0
817
+ };
818
+ let minX = points[0].x;
819
+ let minY = points[0].y;
820
+ let maxX = minX;
821
+ let maxY = minY;
822
+ for (let i = 1; i < points.length; i++) {
823
+ const p = points[i];
824
+ if (p.x < minX) minX = p.x;
825
+ if (p.y < minY) minY = p.y;
826
+ if (p.x > maxX) maxX = p.x;
827
+ if (p.y > maxY) maxY = p.y;
828
+ }
829
+ return {
830
+ x: minX,
831
+ y: minY,
832
+ width: maxX - minX,
833
+ height: maxY - minY
834
+ };
835
+ },
836
+ /** Returns the center point of the rect. */
837
+ center(r) {
838
+ return {
839
+ x: r.x + r.width * .5,
840
+ y: r.y + r.height * .5
841
+ };
842
+ },
843
+ /** Returns a new rect expanded by amount on each side. */
844
+ expand(r, amount) {
845
+ return {
846
+ x: r.x - amount,
847
+ y: r.y - amount,
848
+ width: r.width + amount * 2,
849
+ height: r.height + amount * 2
850
+ };
851
+ }
852
+ };
853
+ //#endregion
854
+ //#region src/reward.ts
855
+ /**
856
+ * Timed-window and capped-bonus math — pure, zero-alloc scoring helpers.
857
+ */
858
+ /**
859
+ * Bonus pay floored to an integer: `pay × min(frac, capFrac)`.
860
+ * `frac` is the current multiplier (e.g. `streak * fracPerStep`);
861
+ * `capFrac` bounds the multiplier so it cannot grow unboundedly.
862
+ * Returns 0 for zero or negative `pay`.
863
+ */
1017
864
  function cappedBonus(pay, frac, capFrac) {
1018
- if (pay <= 0) return 0;
1019
- return Math.floor(pay * Math.min(frac, capFrac));
1020
- }
865
+ if (pay <= 0) return 0;
866
+ return Math.floor(pay * Math.min(frac, capFrac));
867
+ }
868
+ /**
869
+ * Advances a timed streak counter. A new event within `windowSec` of `lastT`
870
+ * increments `prev`; a gap (or `lastT < 0` meaning "no prior event") resets
871
+ * to 1. Pure — the caller stores the returned value and stamps the new time.
872
+ * `elapsed` is the monotonic clock at the new event.
873
+ */
1021
874
  function advanceWindowedStreak(prev, elapsed, lastT, windowSec) {
1022
- if (lastT >= 0 && elapsed - lastT <= windowSec) return prev + 1;
1023
- return 1;
1024
- }
875
+ if (lastT >= 0 && elapsed - lastT <= windowSec) return prev + 1;
876
+ return 1;
877
+ }
878
+ /**
879
+ * Returns true when a streak window has lapsed: a window was open (`lastT >= 0`)
880
+ * and more than `windowSec` seconds have passed since `lastT`.
881
+ * `lastT < 0` means no event has ever occurred — never expired.
882
+ */
1025
883
  function windowExpired(elapsed, lastT, windowSec) {
1026
- return lastT >= 0 && elapsed - lastT > windowSec;
884
+ return lastT >= 0 && elapsed - lastT > windowSec;
1027
885
  }
886
+ /**
887
+ * How many more events may be awarded in the current window given `awarded`
888
+ * already counted. Clamped to 0 — never negative.
889
+ */
1028
890
  function remainingInWindow(awarded, capPerWindow) {
1029
- return Math.max(0, capPerWindow - awarded);
1030
- }
891
+ return Math.max(0, capPerWindow - awarded);
892
+ }
893
+ //#endregion
894
+ //#region src/transform2d.ts
895
+ const Transform2D = {
896
+ /** Creates an identity transform. */
897
+ identity() {
898
+ return {
899
+ position: {
900
+ x: 0,
901
+ y: 0
902
+ },
903
+ rotation: 0,
904
+ scale: {
905
+ x: 1,
906
+ y: 1
907
+ }
908
+ };
909
+ },
910
+ /** Creates a transform from components. */
911
+ create(x = 0, y = 0, rotation = 0, scaleX = 1, scaleY = 1) {
912
+ return {
913
+ position: {
914
+ x,
915
+ y
916
+ },
917
+ rotation,
918
+ scale: {
919
+ x: scaleX,
920
+ y: scaleY
921
+ }
922
+ };
923
+ },
924
+ /** Transforms a local-space point to world-space. */
925
+ localToWorld(t, point) {
926
+ const c = Math.cos(t.rotation);
927
+ const s = Math.sin(t.rotation);
928
+ const sx = point.x * t.scale.x;
929
+ const sy = point.y * t.scale.y;
930
+ return {
931
+ x: sx * c - sy * s + t.position.x,
932
+ y: sx * s + sy * c + t.position.y
933
+ };
934
+ },
935
+ /** Transforms a world-space point to local-space. */
936
+ worldToLocal(t, point) {
937
+ const dx = point.x - t.position.x;
938
+ const dy = point.y - t.position.y;
939
+ const c = Math.cos(-t.rotation);
940
+ const s = Math.sin(-t.rotation);
941
+ const rx = dx * c - dy * s;
942
+ const ry = dx * s + dy * c;
943
+ return {
944
+ x: t.scale.x !== 0 ? rx / t.scale.x : 0,
945
+ y: t.scale.y !== 0 ? ry / t.scale.y : 0
946
+ };
947
+ },
948
+ /** Returns a copy of the transform. */
949
+ clone(t) {
950
+ return {
951
+ position: {
952
+ x: t.position.x,
953
+ y: t.position.y
954
+ },
955
+ rotation: t.rotation,
956
+ scale: {
957
+ x: t.scale.x,
958
+ y: t.scale.y
959
+ }
960
+ };
961
+ }
962
+ };
963
+ //#endregion
964
+ //#region src/vec2.ts
965
+ /** Creates a Vec2 plain object. */
966
+ function vec2(x = 0, y = 0) {
967
+ return {
968
+ x,
969
+ y
970
+ };
971
+ }
972
+ const Vec2 = {
973
+ /** Returns { 0, 0 }. */
974
+ zero() {
975
+ return {
976
+ x: 0,
977
+ y: 0
978
+ };
979
+ },
980
+ /** Returns { 1, 1 }. */
981
+ one() {
982
+ return {
983
+ x: 1,
984
+ y: 1
985
+ };
986
+ },
987
+ /** Returns { 0, -1 }. */
988
+ up() {
989
+ return {
990
+ x: 0,
991
+ y: -1
992
+ };
993
+ },
994
+ /** Returns { 0, 1 }. */
995
+ down() {
996
+ return {
997
+ x: 0,
998
+ y: 1
999
+ };
1000
+ },
1001
+ /** Returns { -1, 0 }. */
1002
+ left() {
1003
+ return {
1004
+ x: -1,
1005
+ y: 0
1006
+ };
1007
+ },
1008
+ /** Returns { 1, 0 }. */
1009
+ right() {
1010
+ return {
1011
+ x: 1,
1012
+ y: 0
1013
+ };
1014
+ },
1015
+ /** Component-wise addition. Writes into out if provided. */
1016
+ add(a, b, out) {
1017
+ if (out) {
1018
+ out.x = a.x + b.x;
1019
+ out.y = a.y + b.y;
1020
+ return out;
1021
+ }
1022
+ return {
1023
+ x: a.x + b.x,
1024
+ y: a.y + b.y
1025
+ };
1026
+ },
1027
+ /** Component-wise subtraction a - b. */
1028
+ sub(a, b, out) {
1029
+ if (out) {
1030
+ out.x = a.x - b.x;
1031
+ out.y = a.y - b.y;
1032
+ return out;
1033
+ }
1034
+ return {
1035
+ x: a.x - b.x,
1036
+ y: a.y - b.y
1037
+ };
1038
+ },
1039
+ /** Scales a vector by a scalar. */
1040
+ scale(v, s, out) {
1041
+ if (out) {
1042
+ out.x = v.x * s;
1043
+ out.y = v.y * s;
1044
+ return out;
1045
+ }
1046
+ return {
1047
+ x: v.x * s,
1048
+ y: v.y * s
1049
+ };
1050
+ },
1051
+ /** Returns a unit vector. Returns zero vector if length is 0. */
1052
+ normalize(v, out) {
1053
+ const len = Math.sqrt(v.x * v.x + v.y * v.y);
1054
+ if (len === 0) {
1055
+ if (out) {
1056
+ out.x = 0;
1057
+ out.y = 0;
1058
+ return out;
1059
+ }
1060
+ return {
1061
+ x: 0,
1062
+ y: 0
1063
+ };
1064
+ }
1065
+ const inv = 1 / len;
1066
+ if (out) {
1067
+ out.x = v.x * inv;
1068
+ out.y = v.y * inv;
1069
+ return out;
1070
+ }
1071
+ return {
1072
+ x: v.x * inv,
1073
+ y: v.y * inv
1074
+ };
1075
+ },
1076
+ /** Zero-alloc; writes into `out`. Mandatory `out` so callers avoid temporaries in hot paths. */
1077
+ normalizeOut(dx, dy, speed, out) {
1078
+ const len = Math.sqrt(dx * dx + dy * dy);
1079
+ if (len <= 1e-6) {
1080
+ out.x = 0;
1081
+ out.y = 0;
1082
+ return out;
1083
+ }
1084
+ const inv = speed / len;
1085
+ out.x = dx * inv;
1086
+ out.y = dy * inv;
1087
+ return out;
1088
+ },
1089
+ /** Dot product. */
1090
+ dot(a, b) {
1091
+ return a.x * b.x + a.y * b.y;
1092
+ },
1093
+ /** 2D cross product (z-component of the 3D cross). */
1094
+ cross(a, b) {
1095
+ return a.x * b.y - a.y * b.x;
1096
+ },
1097
+ /** Perpendicular: (-y, x). In this engine's y-down screen space this is a 90° clockwise turn,
1098
+ * matching the Skia rotation convention used by `@flare-engine/render`. Writes into out if
1099
+ * provided. */
1100
+ perp(v, out) {
1101
+ const x = -v.y;
1102
+ const y = v.x;
1103
+ if (out) {
1104
+ out.x = x;
1105
+ out.y = y;
1106
+ return out;
1107
+ }
1108
+ return {
1109
+ x,
1110
+ y
1111
+ };
1112
+ },
1113
+ /** Scalar × vector cross product: s × v = (-s·y, s·x). Writes into out if provided. */
1114
+ crossSV(s, v, out) {
1115
+ const x = -s * v.y;
1116
+ const y = s * v.x;
1117
+ if (out) {
1118
+ out.x = x;
1119
+ out.y = y;
1120
+ return out;
1121
+ }
1122
+ return {
1123
+ x,
1124
+ y
1125
+ };
1126
+ },
1127
+ /** Linearly interpolates between a and b (unclamped). */
1128
+ lerp(a, b, t, out) {
1129
+ const x = a.x + (b.x - a.x) * t;
1130
+ const y = a.y + (b.y - a.y) * t;
1131
+ if (out) {
1132
+ out.x = x;
1133
+ out.y = y;
1134
+ return out;
1135
+ }
1136
+ return {
1137
+ x,
1138
+ y
1139
+ };
1140
+ },
1141
+ /** Spherical linear interpolation for unit vectors. */
1142
+ slerp(a, b, t, out) {
1143
+ let d = a.x * b.x + a.y * b.y;
1144
+ d = d < -1 ? -1 : d > 1 ? 1 : d;
1145
+ const theta = Math.acos(d);
1146
+ if (theta < 1e-6) return Vec2.lerp(a, b, t, out);
1147
+ const sinTheta = Math.sin(theta);
1148
+ const wa = Math.sin((1 - t) * theta) / sinTheta;
1149
+ const wb = Math.sin(t * theta) / sinTheta;
1150
+ const x = a.x * wa + b.x * wb;
1151
+ const y = a.y * wa + b.y * wb;
1152
+ if (out) {
1153
+ out.x = x;
1154
+ out.y = y;
1155
+ return out;
1156
+ }
1157
+ return {
1158
+ x,
1159
+ y
1160
+ };
1161
+ },
1162
+ /** Euclidean distance between two points. */
1163
+ distance(a, b) {
1164
+ const dx = b.x - a.x;
1165
+ const dy = b.y - a.y;
1166
+ return Math.sqrt(dx * dx + dy * dy);
1167
+ },
1168
+ /** Squared distance (avoids sqrt). */
1169
+ distanceSq(a, b) {
1170
+ const dx = b.x - a.x;
1171
+ const dy = b.y - a.y;
1172
+ return dx * dx + dy * dy;
1173
+ },
1174
+ /** Length of a vector. */
1175
+ length(v) {
1176
+ return Math.sqrt(v.x * v.x + v.y * v.y);
1177
+ },
1178
+ /** Squared length (avoids sqrt). */
1179
+ lengthSq(v) {
1180
+ return v.x * v.x + v.y * v.y;
1181
+ },
1182
+ /** Angle of vector in radians (atan2). */
1183
+ angle(v) {
1184
+ return Math.atan2(v.y, v.x);
1185
+ },
1186
+ /** Rotates a vector by angle (radians) around origin. */
1187
+ rotate(v, angle, out) {
1188
+ const c = Math.cos(angle);
1189
+ const s = Math.sin(angle);
1190
+ const x = v.x * c - v.y * s;
1191
+ const y = v.x * s + v.y * c;
1192
+ if (out) {
1193
+ out.x = x;
1194
+ out.y = y;
1195
+ return out;
1196
+ }
1197
+ return {
1198
+ x,
1199
+ y
1200
+ };
1201
+ },
1202
+ /** Component-wise approximate equality. */
1203
+ equals(a, b, epsilon = 1e-6) {
1204
+ return approxEquals(a.x, b.x, epsilon) && approxEquals(a.y, b.y, epsilon);
1205
+ },
1206
+ /** Returns a copy of the vector. */
1207
+ clone(v) {
1208
+ return {
1209
+ x: v.x,
1210
+ y: v.y
1211
+ };
1212
+ },
1213
+ /** Clamps the length of a vector to maxLength. */
1214
+ clampLength(v, maxLength, out) {
1215
+ const lenSq = v.x * v.x + v.y * v.y;
1216
+ if (lenSq <= maxLength * maxLength) {
1217
+ if (out) {
1218
+ out.x = v.x;
1219
+ out.y = v.y;
1220
+ return out;
1221
+ }
1222
+ return {
1223
+ x: v.x,
1224
+ y: v.y
1225
+ };
1226
+ }
1227
+ const inv = maxLength / Math.sqrt(lenSq);
1228
+ if (out) {
1229
+ out.x = v.x * inv;
1230
+ out.y = v.y * inv;
1231
+ return out;
1232
+ }
1233
+ return {
1234
+ x: v.x * inv,
1235
+ y: v.y * inv
1236
+ };
1237
+ },
1238
+ /** Zero-alloc homing steer: rotate velocity (vx,vy) toward direction (dx,dy) by at most maxStep
1239
+ * radians, preserving speed; writes into out. Mandatory out. Byte-identical to hand-rolled
1240
+ * homing-bullet steering (Math.hypot speed + atan2 + branch-form turn + cos/sin * speed). */
1241
+ turnVelocityToward(vx, vy, dx, dy, maxStep, out) {
1242
+ const speed = Math.hypot(vx, vy);
1243
+ const newAng = rotateTowards(Math.atan2(vy, vx), Math.atan2(dy, dx), maxStep);
1244
+ out.x = Math.cos(newAng) * speed;
1245
+ out.y = Math.sin(newAng) * speed;
1246
+ return out;
1247
+ }
1248
+ };
1249
+ //#endregion
1250
+ exports.Circle = Circle;
1251
+ exports.Color = Color;
1252
+ exports.Mat3 = Mat3;
1253
+ exports.Rect = Rect;
1254
+ exports.SeededRNG = SeededRNG;
1255
+ exports.Transform2D = Transform2D;
1256
+ exports.Vec2 = Vec2;
1257
+ exports.advanceWindowedStreak = advanceWindowedStreak;
1258
+ exports.angleDelta = angleDelta;
1259
+ exports.approxEquals = approxEquals;
1260
+ exports.cappedBonus = cappedBonus;
1261
+ exports.choose = choose;
1262
+ exports.circleVsCircle = circleVsCircle;
1263
+ exports.clamp = clamp;
1264
+ exports.degToRad = degToRad;
1265
+ exports.easeInBack = easeInBack;
1266
+ exports.easeInBounce = easeInBounce;
1267
+ exports.easeInCirc = easeInCirc;
1268
+ exports.easeInCubic = easeInCubic;
1269
+ exports.easeInElastic = easeInElastic;
1270
+ exports.easeInExpo = easeInExpo;
1271
+ exports.easeInOutBack = easeInOutBack;
1272
+ exports.easeInOutBounce = easeInOutBounce;
1273
+ exports.easeInOutCirc = easeInOutCirc;
1274
+ exports.easeInOutCubic = easeInOutCubic;
1275
+ exports.easeInOutElastic = easeInOutElastic;
1276
+ exports.easeInOutExpo = easeInOutExpo;
1277
+ exports.easeInOutQuad = easeInOutQuad;
1278
+ exports.easeInOutQuart = easeInOutQuart;
1279
+ exports.easeInOutQuint = easeInOutQuint;
1280
+ exports.easeInOutSine = easeInOutSine;
1281
+ exports.easeInQuad = easeInQuad;
1282
+ exports.easeInQuart = easeInQuart;
1283
+ exports.easeInQuint = easeInQuint;
1284
+ exports.easeInSine = easeInSine;
1285
+ exports.easeOutBack = easeOutBack;
1286
+ exports.easeOutBounce = easeOutBounce;
1287
+ exports.easeOutCirc = easeOutCirc;
1288
+ exports.easeOutCubic = easeOutCubic;
1289
+ exports.easeOutElastic = easeOutElastic;
1290
+ exports.easeOutExpo = easeOutExpo;
1291
+ exports.easeOutQuad = easeOutQuad;
1292
+ exports.easeOutQuart = easeOutQuart;
1293
+ exports.easeOutQuint = easeOutQuint;
1294
+ exports.easeOutSine = easeOutSine;
1295
+ exports.hashU32 = hashU32;
1296
+ exports.inverseLerp = inverseLerp;
1297
+ exports.lerp = lerp;
1298
+ exports.lineVsCircle = lineVsCircle;
1299
+ exports.lineVsRect = lineVsRect;
1300
+ exports.linear = linear;
1301
+ exports.map = map;
1302
+ exports.pingPong = pingPong;
1303
+ exports.pointInCircle = pointInCircle;
1304
+ exports.pointInRect = pointInRect;
1305
+ exports.radToDeg = radToDeg;
1306
+ exports.randomFloat = randomFloat;
1307
+ exports.randomInt = randomInt;
1308
+ exports.randomPointInCircle = randomPointInCircle;
1309
+ exports.randomVec2InRect = randomVec2InRect;
1310
+ exports.rayVsRect = rayVsRect;
1311
+ exports.rectVsCircle = rectVsCircle;
1312
+ exports.rectVsRect = rectVsRect;
1313
+ exports.remainingInWindow = remainingInWindow;
1314
+ exports.rotateTowards = rotateTowards;
1315
+ exports.seededIndex = seededIndex;
1316
+ exports.shuffle = shuffle;
1317
+ exports.sign = sign;
1318
+ exports.smoothStep = smoothStep;
1319
+ exports.springIn = springIn;
1320
+ exports.springInOut = springInOut;
1321
+ exports.springOut = springOut;
1322
+ exports.step = step;
1323
+ exports.vec2 = vec2;
1324
+ exports.weightedIndex = weightedIndex;
1325
+ exports.weightedIndexBy = weightedIndexBy;
1326
+ exports.weightedPick = weightedPick;
1327
+ exports.windowExpired = windowExpired;
1328
+ exports.wrap = wrap;
1031
1329
 
1032
- // src/intersection.ts
1033
- function pointInRect(p, r) {
1034
- return p.x >= r.x && p.x <= r.x + r.width && p.y >= r.y && p.y <= r.y + r.height;
1035
- }
1036
- function pointInCircle(p, c) {
1037
- const dx = p.x - c.x;
1038
- const dy = p.y - c.y;
1039
- return dx * dx + dy * dy <= c.radius * c.radius;
1040
- }
1041
- function rectVsRect(a, b) {
1042
- return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
1043
- }
1044
- function rectVsCircle(r, c) {
1045
- const closestX = c.x < r.x ? r.x : c.x > r.x + r.width ? r.x + r.width : c.x;
1046
- const closestY = c.y < r.y ? r.y : c.y > r.y + r.height ? r.y + r.height : c.y;
1047
- const dx = c.x - closestX;
1048
- const dy = c.y - closestY;
1049
- return dx * dx + dy * dy <= c.radius * c.radius;
1050
- }
1051
- function circleVsCircle(a, b) {
1052
- const dx = b.x - a.x;
1053
- const dy = b.y - a.y;
1054
- const radSum = a.radius + b.radius;
1055
- return dx * dx + dy * dy < radSum * radSum;
1056
- }
1057
- function lineVsRect(x1, y1, x2, y2, r) {
1058
- const dx = x2 - x1;
1059
- const dy = y2 - y1;
1060
- let tMin = 0;
1061
- let tMax = 1;
1062
- const edges = [
1063
- { p: -dx, q: x1 - r.x },
1064
- { p: dx, q: r.x + r.width - x1 },
1065
- { p: -dy, q: y1 - r.y },
1066
- { p: dy, q: r.y + r.height - y1 }
1067
- ];
1068
- for (const { p, q } of edges) {
1069
- if (Math.abs(p) < 1e-12) {
1070
- if (q < 0) return false;
1071
- } else {
1072
- const t = q / p;
1073
- if (p < 0) {
1074
- if (t > tMax) return false;
1075
- if (t > tMin) tMin = t;
1076
- } else {
1077
- if (t < tMin) return false;
1078
- if (t < tMax) tMax = t;
1079
- }
1080
- }
1081
- }
1082
- return tMin <= tMax;
1083
- }
1084
- function lineVsCircle(x1, y1, x2, y2, c) {
1085
- const dx = x2 - x1;
1086
- const dy = y2 - y1;
1087
- const fx = x1 - c.x;
1088
- const fy = y1 - c.y;
1089
- const a = dx * dx + dy * dy;
1090
- const b = 2 * (fx * dx + fy * dy);
1091
- const cc = fx * fx + fy * fy - c.radius * c.radius;
1092
- if (a < 1e-12) {
1093
- return cc <= 0;
1094
- }
1095
- let discriminant = b * b - 4 * a * cc;
1096
- if (discriminant < 0) return false;
1097
- discriminant = Math.sqrt(discriminant);
1098
- const t1 = (-b - discriminant) / (2 * a);
1099
- const t2 = (-b + discriminant) / (2 * a);
1100
- if (t1 >= 0 && t1 <= 1) return true;
1101
- if (t2 >= 0 && t2 <= 1) return true;
1102
- if (t1 < 0 && t2 > 1) return true;
1103
- return false;
1104
- }
1105
- function rayVsRect(originX, originY, dirX, dirY, r) {
1106
- const invDx = dirX !== 0 ? 1 / dirX : Number.POSITIVE_INFINITY;
1107
- const invDy = dirY !== 0 ? 1 / dirY : Number.POSITIVE_INFINITY;
1108
- const tx1 = (r.x - originX) * invDx;
1109
- const tx2 = (r.x + r.width - originX) * invDx;
1110
- const ty1 = (r.y - originY) * invDy;
1111
- const ty2 = (r.y + r.height - originY) * invDy;
1112
- const tMin = Math.max(Math.min(tx1, tx2), Math.min(ty1, ty2));
1113
- const tMax = Math.min(Math.max(tx1, tx2), Math.max(ty1, ty2));
1114
- if (tMax < 0 || tMin > tMax) return null;
1115
- return tMin >= 0 ? tMin : tMax;
1116
- }
1117
- // Annotate the CommonJS export names for ESM import in node:
1118
- 0 && (module.exports = {
1119
- Circle,
1120
- Color,
1121
- Mat3,
1122
- Rect,
1123
- SeededRNG,
1124
- Transform2D,
1125
- Vec2,
1126
- advanceWindowedStreak,
1127
- angleDelta,
1128
- approxEquals,
1129
- cappedBonus,
1130
- choose,
1131
- circleVsCircle,
1132
- clamp,
1133
- degToRad,
1134
- easeInBack,
1135
- easeInBounce,
1136
- easeInCirc,
1137
- easeInCubic,
1138
- easeInElastic,
1139
- easeInExpo,
1140
- easeInOutBack,
1141
- easeInOutBounce,
1142
- easeInOutCirc,
1143
- easeInOutCubic,
1144
- easeInOutElastic,
1145
- easeInOutExpo,
1146
- easeInOutQuad,
1147
- easeInOutQuart,
1148
- easeInOutQuint,
1149
- easeInOutSine,
1150
- easeInQuad,
1151
- easeInQuart,
1152
- easeInQuint,
1153
- easeInSine,
1154
- easeOutBack,
1155
- easeOutBounce,
1156
- easeOutCirc,
1157
- easeOutCubic,
1158
- easeOutElastic,
1159
- easeOutExpo,
1160
- easeOutQuad,
1161
- easeOutQuart,
1162
- easeOutQuint,
1163
- easeOutSine,
1164
- hashU32,
1165
- inverseLerp,
1166
- lerp,
1167
- lineVsCircle,
1168
- lineVsRect,
1169
- linear,
1170
- map,
1171
- pingPong,
1172
- pointInCircle,
1173
- pointInRect,
1174
- radToDeg,
1175
- randomFloat,
1176
- randomInt,
1177
- randomPointInCircle,
1178
- randomVec2InRect,
1179
- rayVsRect,
1180
- rectVsCircle,
1181
- rectVsRect,
1182
- remainingInWindow,
1183
- rotateTowards,
1184
- seededIndex,
1185
- shuffle,
1186
- sign,
1187
- smoothStep,
1188
- springIn,
1189
- springInOut,
1190
- springOut,
1191
- step,
1192
- vec2,
1193
- weightedIndex,
1194
- weightedIndexBy,
1195
- weightedPick,
1196
- windowExpired,
1197
- wrap
1198
- });
1199
1330
  //# sourceMappingURL=index.js.map