@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/LICENSE +201 -21
- package/NOTICE +2 -0
- package/README.md +4 -4
- package/dist/index.d.mts +263 -245
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +263 -245
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1206 -1075
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1128 -971
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -8
package/dist/index.js
CHANGED
|
@@ -1,1199 +1,1330 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
45
|
+
return lerp(outMin, outMax, inverseLerp(inMin, inMax, value));
|
|
119
46
|
}
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
51
|
+
return degrees * DEG_TO_RAD;
|
|
124
52
|
}
|
|
53
|
+
/** Converts radians to degrees. */
|
|
125
54
|
function radToDeg(radians) {
|
|
126
|
-
|
|
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
|
-
|
|
59
|
+
return Math.abs(a - b) <= epsilon;
|
|
130
60
|
}
|
|
61
|
+
/** Wraps value into [min, max) range. */
|
|
131
62
|
function wrap(value, min, max) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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
|
-
|
|
279
|
+
return t;
|
|
780
280
|
}
|
|
781
281
|
function step(t) {
|
|
782
|
-
|
|
282
|
+
return t < 1 ? 0 : 1;
|
|
783
283
|
}
|
|
784
284
|
function easeInQuad(t) {
|
|
785
|
-
|
|
285
|
+
return t * t;
|
|
786
286
|
}
|
|
787
287
|
function easeOutQuad(t) {
|
|
788
|
-
|
|
288
|
+
return t * (2 - t);
|
|
789
289
|
}
|
|
790
290
|
function easeInOutQuad(t) {
|
|
791
|
-
|
|
291
|
+
return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
792
292
|
}
|
|
793
293
|
function easeInCubic(t) {
|
|
794
|
-
|
|
294
|
+
return t * t * t;
|
|
795
295
|
}
|
|
796
296
|
function easeOutCubic(t) {
|
|
797
|
-
|
|
798
|
-
|
|
297
|
+
const t1 = t - 1;
|
|
298
|
+
return t1 * t1 * t1 + 1;
|
|
799
299
|
}
|
|
800
300
|
function easeInOutCubic(t) {
|
|
801
|
-
|
|
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
|
-
|
|
304
|
+
return t * t * t * t;
|
|
805
305
|
}
|
|
806
306
|
function easeOutQuart(t) {
|
|
807
|
-
|
|
808
|
-
|
|
307
|
+
const t1 = t - 1;
|
|
308
|
+
return 1 - t1 * t1 * t1 * t1;
|
|
809
309
|
}
|
|
810
310
|
function easeInOutQuart(t) {
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
315
|
+
return t * t * t * t * t;
|
|
816
316
|
}
|
|
817
317
|
function easeOutQuint(t) {
|
|
818
|
-
|
|
819
|
-
|
|
318
|
+
const t1 = t - 1;
|
|
319
|
+
return 1 + t1 * t1 * t1 * t1 * t1;
|
|
820
320
|
}
|
|
821
321
|
function easeInOutQuint(t) {
|
|
822
|
-
|
|
823
|
-
|
|
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
|
-
|
|
326
|
+
return 1 - Math.cos(t * HALF_PI);
|
|
827
327
|
}
|
|
828
328
|
function easeOutSine(t) {
|
|
829
|
-
|
|
329
|
+
return Math.sin(t * HALF_PI);
|
|
830
330
|
}
|
|
831
331
|
function easeInOutSine(t) {
|
|
832
|
-
|
|
332
|
+
return .5 * (1 - Math.cos(PI * t));
|
|
833
333
|
}
|
|
834
334
|
function easeInExpo(t) {
|
|
835
|
-
|
|
335
|
+
return t === 0 ? 0 : 2 ** (10 * (t - 1));
|
|
836
336
|
}
|
|
837
337
|
function easeOutExpo(t) {
|
|
838
|
-
|
|
338
|
+
return t === 1 ? 1 : 1 - 2 ** (-10 * t);
|
|
839
339
|
}
|
|
840
340
|
function easeInOutExpo(t) {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
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
|
-
|
|
346
|
+
return 1 - Math.sqrt(1 - t * t);
|
|
847
347
|
}
|
|
848
348
|
function easeOutCirc(t) {
|
|
849
|
-
|
|
850
|
-
|
|
349
|
+
const t1 = t - 1;
|
|
350
|
+
return Math.sqrt(1 - t1 * t1);
|
|
851
351
|
}
|
|
852
352
|
function easeInOutCirc(t) {
|
|
853
|
-
|
|
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
|
-
|
|
856
|
-
|
|
355
|
+
const S = 1.70158;
|
|
356
|
+
const S2 = S * 1.525;
|
|
857
357
|
function easeInBack(t) {
|
|
858
|
-
|
|
358
|
+
return t * t * (2.70158 * t - S);
|
|
859
359
|
}
|
|
860
360
|
function easeOutBack(t) {
|
|
861
|
-
|
|
862
|
-
|
|
361
|
+
const t1 = t - 1;
|
|
362
|
+
return t1 * t1 * (2.70158 * t1 + S) + 1;
|
|
863
363
|
}
|
|
864
364
|
function easeInOutBack(t) {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
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
|
-
|
|
874
|
-
|
|
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
|
-
|
|
878
|
-
|
|
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
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
|
|
399
|
+
return 1 - easeOutBounce(1 - t);
|
|
902
400
|
}
|
|
903
401
|
function easeInOutBounce(t) {
|
|
904
|
-
|
|
402
|
+
return t < .5 ? .5 * easeInBounce(t * 2) : .5 * easeOutBounce(t * 2 - 1) + .5;
|
|
905
403
|
}
|
|
906
404
|
function springIn(t) {
|
|
907
|
-
|
|
405
|
+
return t * t * (2.70158 * t - 1.70158);
|
|
908
406
|
}
|
|
909
407
|
function springOut(t) {
|
|
910
|
-
|
|
911
|
-
|
|
408
|
+
const t1 = t - 1;
|
|
409
|
+
return t1 * t1 * (2.70158 * t1 + 1.70158) + 1;
|
|
912
410
|
}
|
|
913
411
|
function springInOut(t) {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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
|
-
|
|
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
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
-
|
|
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
|
-
|
|
678
|
+
return Math.floor(randomFloat(min, max + 1));
|
|
949
679
|
}
|
|
680
|
+
/** Returns a random Vec2 within a rect. */
|
|
950
681
|
function randomVec2InRect(r) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
-
|
|
975
|
-
|
|
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
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
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
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
-
|
|
1019
|
-
|
|
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
|
-
|
|
1023
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|