@inglorious/utils 3.8.0 → 4.0.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.
Files changed (67) hide show
  1. package/README.md +67 -3
  2. package/package.json +88 -12
  3. package/src/algorithms/decision-tree.js +3 -3
  4. package/src/algorithms/path-finding.js +6 -6
  5. package/src/data-structures/board.js +97 -15
  6. package/src/data-structures/index.js +1 -2
  7. package/src/data-structures/objects.js +352 -34
  8. package/src/data-structures/tree.js +1 -1
  9. package/src/functions/functions.js +168 -0
  10. package/src/functions/index.js +1 -4
  11. package/src/math/circle.js +5 -7
  12. package/src/math/index.js +0 -4
  13. package/src/math/line.js +3 -3
  14. package/src/math/point.js +10 -9
  15. package/src/math/quaternions.js +20 -4
  16. package/src/math/rectangle.js +4 -4
  17. package/src/math/segment.js +11 -5
  18. package/src/math/vectors.js +471 -65
  19. package/src/physics/acceleration.js +2 -3
  20. package/src/physics/friction.js +2 -2
  21. package/src/physics/gravity.js +2 -2
  22. package/src/physics/jump.js +0 -4
  23. package/src/physics/velocity.js +2 -3
  24. package/src/v.js +1 -1
  25. package/types/algorithms/decision-tree.d.ts +27 -0
  26. package/types/algorithms/index.d.ts +2 -0
  27. package/types/algorithms/path-finding.d.ts +69 -0
  28. package/types/data-structures/array.d.ts +56 -0
  29. package/types/data-structures/board.d.ts +121 -0
  30. package/types/data-structures/boolean.d.ts +4 -0
  31. package/types/data-structures/heap.d.ts +69 -0
  32. package/types/data-structures/index.d.ts +8 -0
  33. package/types/data-structures/objects.d.ts +237 -0
  34. package/types/data-structures/set.d.ts +4 -0
  35. package/types/data-structures/tree.d.ts +21 -0
  36. package/types/functions/functions.d.ts +57 -0
  37. package/types/functions/index.d.ts +1 -0
  38. package/types/index.d.ts +6 -4
  39. package/types/math/circle.d.ts +22 -0
  40. package/types/math/hitmask.d.ts +20 -0
  41. package/types/math/index.d.ts +14 -0
  42. package/types/math/line.d.ts +11 -0
  43. package/types/math/linear-interpolation.d.ts +2 -0
  44. package/types/math/numbers.d.ts +26 -0
  45. package/types/math/point.d.ts +25 -0
  46. package/types/math/quaternions.d.ts +10 -0
  47. package/types/math/rectangle.d.ts +36 -0
  48. package/types/math/rng.d.ts +14 -0
  49. package/types/math/segment.d.ts +23 -0
  50. package/types/math/statistics.d.ts +11 -0
  51. package/types/math/triangle.d.ts +5 -0
  52. package/types/math/trigonometry.d.ts +26 -0
  53. package/types/math/vectors.d.ts +190 -0
  54. package/types/physics/acceleration.d.ts +24 -0
  55. package/types/physics/friction.d.ts +11 -0
  56. package/types/physics/gravity.d.ts +20 -0
  57. package/types/physics/index.d.ts +5 -0
  58. package/types/physics/jump.d.ts +10 -0
  59. package/types/physics/velocity.d.ts +20 -0
  60. package/types/v.d.ts +7 -3
  61. package/src/data-structures/object.js +0 -320
  62. package/src/functions/function.js +0 -9
  63. package/src/math/quaternion.js +0 -22
  64. package/src/math/vector.js +0 -426
  65. package/types/algorithms.d.ts +0 -26
  66. package/types/data-structures.d.ts +0 -53
  67. package/types/math.d.ts +0 -90
@@ -1,6 +1,89 @@
1
- import { isFunction } from "../functions/function.js"
1
+ import { isFunction } from "../functions/functions.js"
2
+ import { v } from "../v.js"
2
3
  import { isArray } from "./array.js"
3
- import { isObject } from "./object.js"
4
+
5
+ const INITIAL_LEVEL = 0
6
+ const LAST_KEY_OFFSET = 1
7
+ const NEXT_KEY_OFFSET = 1
8
+ const NEXT_LEVEL = 2
9
+
10
+ /**
11
+ * Creates a deep clone of a given value using the structured clone algorithm.
12
+ * This is a modern and efficient way to deep-clone objects.
13
+ *
14
+ * Note: `structuredClone` has some limitations. It cannot clone functions,
15
+ * DOM nodes, property descriptors, getters, setters, or certain other
16
+ * non-serializable objects. It will throw a `DataCloneError` in such cases.
17
+ *
18
+ * For more details, see:
19
+ * https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
20
+ *
21
+ * This is similar to Lodash's `_.cloneDeep`.
22
+ *
23
+ * @template T
24
+ * @param {T} obj - The value to clone.
25
+ * @returns {T} A deep clone of the input value.
26
+ */
27
+ export function clone(obj) {
28
+ return deserialize(serialize(obj))
29
+ }
30
+
31
+ /**
32
+ * Assigns default properties to a target object from a source object.
33
+ * For each key in `defaultProps`, if `target[key]` is `null` or `undefined`,
34
+ * it is set to `defaultProps[key]`.
35
+ *
36
+ * This function modifies the target object in place.
37
+ *
38
+ * @param {Object} target The object to apply defaults to.
39
+ * @param {Object} defaultProps The object containing the default properties.
40
+ * @returns {Object} The modified target object.
41
+ */
42
+ export function defaults(target, defaultProps) {
43
+ for (const key in defaultProps) {
44
+ target[key] ??= defaultProps[key]
45
+ }
46
+ return target
47
+ }
48
+
49
+ /**
50
+ * Deserializes an object, converting plain object representations back into
51
+ * their original types, such as vector-like objects. This is the inverse
52
+ * operation of `serialize`.
53
+ *
54
+ * - Recursively deserializes nested objects.
55
+ * - Converts objects with a `_type: "vector"` property and `coords` array
56
+ * back into a vector-like object using the `v` factory function.
57
+ * - Copies all other property values as-is.
58
+ *
59
+ * @param {string} str - The JSON string to deserialize.
60
+ * @returns {Object} The deserialized object.
61
+ */
62
+ export function deserialize(str) {
63
+ const data = JSON.parse(str)
64
+
65
+ return revive(data)
66
+
67
+ function revive(value) {
68
+ if (isArray(value)) {
69
+ return value.map(revive)
70
+ }
71
+ if (isObject(value)) {
72
+ if (value._type === "vector" && value.coords) {
73
+ return v(...value.coords)
74
+ }
75
+
76
+ const deserialized = {}
77
+ for (const key in value) {
78
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
79
+ deserialized[key] = revive(value[key])
80
+ }
81
+ }
82
+ return deserialized
83
+ }
84
+ return value
85
+ }
86
+ }
4
87
 
5
88
  /**
6
89
  * Creates a new object by deeply merging a target object with one or more source objects.
@@ -17,6 +100,111 @@ export function extend(target, ...sources) {
17
100
  return extendWith(undefined, target, ...sources)
18
101
  }
19
102
 
103
+ /**
104
+ * Creates a new object by deeply merging a target object with one or more source objects,
105
+ * using a custom merger function to handle specific properties.
106
+ * This function is immutable.
107
+ *
108
+ * @param {Function} [merger] - A function to customize merging behavior. It receives `(targetValue, sourceValue)`. If it returns `undefined`, the default merge logic is used.
109
+ * @param {Object} target - The base object.
110
+ * @param {...Object} sources - The source objects to merge.
111
+ * @returns {Object} A new object with the merged properties.
112
+ */
113
+ export function extendWith(merger, target, ...sources) {
114
+ return mergeWith(merger, {}, target, ...sources)
115
+ }
116
+
117
+ /**
118
+ * Filters the properties of an object based on a callback function.
119
+ *
120
+ * @param {Object} obj - The object to filter.
121
+ * @param {Function} callback - A function that determines whether a property should be included.
122
+ * Receives (key, value, obj) as arguments.
123
+ * @returns {Object} A new object with the filtered properties.
124
+ */
125
+ export function filter(obj, callback) {
126
+ return Object.fromEntries(
127
+ Object.entries(obj).filter(([key, value], obj) =>
128
+ callback(key, value, obj),
129
+ ),
130
+ )
131
+ }
132
+
133
+ /**
134
+ * Finds the first property in an object that satisfies the callback function.
135
+ *
136
+ * @param {Object} obj - The object to search.
137
+ * @param {Function} callback - A function that determines whether a property matches.
138
+ * Receives (key, value, obj) as arguments.
139
+ * @returns {Object} An object containing the first matching property, or an empty object if none match.
140
+ */
141
+ export function find(obj, callback) {
142
+ return Object.fromEntries([
143
+ Object.entries(obj).find(([key, value], obj) => callback(key, value, obj)),
144
+ ])
145
+ }
146
+
147
+ /**
148
+ * Gets the value at a specified path of an object. If the resolved value is
149
+ * `undefined`, the `defaultValue` is returned in its place. This function
150
+ * supports dot-notation for nested properties and numbers for array indices.
151
+ *
152
+ * This is similar to Lodash's `_.get`.
153
+ *
154
+ * @example
155
+ * const object = { 'a': [{ 'b': { 'c': 3 } }] };
156
+ * get(object, 'a.0.b.c');
157
+ * // => 3
158
+ *
159
+ * get(object, 'a.0.d', 'default');
160
+ * // => 'default'
161
+ *
162
+ * @param {Object} obj - The object to query.
163
+ * @param {string} path - The path of the property to retrieve.
164
+ * @param {*} [defaultValue=undefined] - The value returned for `undefined` resolved values.
165
+ * @returns {*} Returns the resolved value, else the `defaultValue`.
166
+ */
167
+ export function get(obj, path, defaultValue = undefined) {
168
+ if (!path) return defaultValue
169
+
170
+ const keys = path.split(".")
171
+ let result = obj
172
+
173
+ for (const key of keys) {
174
+ if (result == null || typeof result !== "object") {
175
+ return defaultValue
176
+ }
177
+ result = result[key]
178
+ }
179
+
180
+ return result !== undefined ? result : defaultValue
181
+ }
182
+
183
+ /**
184
+ * Checks if a value is a plain object.
185
+ *
186
+ * @param {*} value - The value to check.
187
+ * @returns {boolean} True if the value is a plain object, false otherwise.
188
+ */
189
+ export function isObject(value) {
190
+ return value != null && value.constructor === Object
191
+ }
192
+
193
+ /**
194
+ * Maps the properties of an object using a callback function.
195
+ *
196
+ * @param {Object} obj - The object to map.
197
+ * @param {Function} callback - A function that transforms each property.
198
+ * Receives (key, value, obj) as arguments.
199
+ * @returns {Object} A new object with the mapped properties.
200
+ */
201
+ export function map(obj, callback) {
202
+ return Object.entries(obj).reduce((acc, [key, value]) => {
203
+ acc[key] = callback(key, value, obj)
204
+ return acc
205
+ }, {})
206
+ }
207
+
20
208
  /**
21
209
  * Merges multiple source objects into a target object.
22
210
  * This function is mutable; it modifies the `target` object in place.
@@ -32,20 +220,6 @@ export function merge(target, ...sources) {
32
220
  return mergeWith(undefined, target, ...sources)
33
221
  }
34
222
 
35
- /**
36
- * Creates a new object by deeply merging a target object with one or more source objects,
37
- * using a custom merger function to handle specific properties.
38
- * This function is immutable.
39
- *
40
- * @param {Function} [merger] - A function to customize merging behavior. It receives `(targetValue, sourceValue)`. If it returns `undefined`, the default merge logic is used.
41
- * @param {Object} target - The base object.
42
- * @param {...Object} sources - The source objects to merge.
43
- * @returns {Object} A new object with the merged properties.
44
- */
45
- export function extendWith(merger, target, ...sources) {
46
- return mergeWith(merger, {}, target, ...sources)
47
- }
48
-
49
223
  /**
50
224
  * Merges multiple source objects into a target object, using a custom merger function.
51
225
  * This function is mutable; it modifies the `target` object in place.
@@ -61,6 +235,168 @@ export function mergeWith(merger, target, ...sources) {
61
235
  .reduce((acc, source) => deepMerge(acc, source, merger), target)
62
236
  }
63
237
 
238
+ /**
239
+ * A utility function inspired by Immer's `produce` API. It provides a convenient
240
+ * way to work with immutable data structures by allowing "mutations" on a
241
+ * temporary draft.
242
+ *
243
+ * **Important:** Unlike Immer, which uses structural sharing via proxies for
244
+ * high performance, this implementation performs a full deep clone of the base
245
+ * state on every call using `deserialize(serialize())`. This can be
246
+ * inefficient for large or complex states. It is intended for simple use cases
247
+ * where the convenience of the API outweighs the performance cost.
248
+ *
249
+ * The recipe function receives a draft copy of the state. It can either
250
+ * mutate the draft and return nothing (`undefined`), or it can return a
251
+ * completely new value, which will become the next state.
252
+ *
253
+ * Can be called in two ways:
254
+ * - **Standard:** `produce(baseState, recipe, ...args)`
255
+ * - **Curried:** `produce(recipe)` returns a new function `(baseState, ...args) => newState`
256
+ *
257
+ * @template T
258
+ * @param {T|function(T, ...*): (T|void)} baseState The initial state, or a recipe for currying.
259
+ * @param {function(T, ...*): (T|void)} [recipe] The recipe function.
260
+ * @param {...*} args Additional arguments to pass to the recipe.
261
+ * @returns {T | function(T, ...*): T} A new state, or a producer function if curried.
262
+ */
263
+ export function produce(baseState, recipe, ...args) {
264
+ if (typeof baseState === "function" && recipe === undefined) {
265
+ const recipeFn = baseState
266
+ return (state, ...recipeArgs) => produce(state, recipeFn, ...recipeArgs)
267
+ }
268
+
269
+ const draft = clone(baseState)
270
+ const result = recipe(draft, ...args)
271
+ return result === undefined ? draft : result
272
+ }
273
+
274
+ /**
275
+ * Serializes an object, converting special types like vectors into a plain
276
+ * object representation. This is useful for processes like saving state to a
277
+ * file or sending it over a network.
278
+ *
279
+ * - Recursively serializes nested objects.
280
+ * - Converts objects with a `__isVector__` property into a serializable
281
+ * format: `{ _type: "vector", coords: [...] }`.
282
+ * - Copies all other property values as-is.
283
+ *
284
+ * @param {Object} obj - The object to serialize.
285
+ * @returns {string} The serialized JSON string.
286
+ */
287
+ export function serialize(obj) {
288
+ function replacer(key, value) {
289
+ // Handle top-level vector
290
+ if (value?.__isVector__) {
291
+ return {
292
+ _type: "vector",
293
+ coords: Array.from(value),
294
+ }
295
+ }
296
+
297
+ if (isObject(value)) {
298
+ const serialized = {}
299
+
300
+ for (const k in value) {
301
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
302
+ serialized[k] = replacer(undefined, value[k])
303
+ }
304
+ }
305
+
306
+ return serialized
307
+ }
308
+
309
+ return value
310
+ }
311
+
312
+ return JSON.stringify(obj, replacer)
313
+ }
314
+
315
+ /**
316
+ * Sets the value at a specified path of an object. If a portion of the path
317
+ * doesn't exist, it's created. Arrays are created for missing index properties
318
+ * while objects are created for all other missing properties. This function
319
+ * mutates the object.
320
+ *
321
+ * This is similar to Lodash's `_.set`.
322
+ *
323
+ * @example
324
+ * const object = { 'a': [{ 'b': { 'c': 3 } }] };
325
+ * set(object, 'a.0.b.d', 4);
326
+ * // object is now { 'a': [{ 'b': { 'c': 3, 'd': 4 } }] }
327
+ *
328
+ * set(object, 'x.0.y', 5);
329
+ * // object is now { 'a': [...], 'x': [{ 'y': 5 }] }
330
+ *
331
+ * @param {Object} obj - The object to modify.
332
+ * @param {string} path - The path of the property to set.
333
+ * @param {*} value - The value to set.
334
+ * @returns {Object} Returns the modified object.
335
+ */
336
+ export function set(obj, path, value) {
337
+ if (!path) return obj
338
+
339
+ const keys = path.split(".")
340
+ let current = obj
341
+
342
+ for (let i = 0; i < keys.length - LAST_KEY_OFFSET; i++) {
343
+ const key = keys[i]
344
+ const nextKey = keys[i + NEXT_KEY_OFFSET]
345
+
346
+ // Create missing intermediate objects
347
+ if (!(key in current) || typeof current[key] !== "object") {
348
+ // Create array if next key is a number, object otherwise
349
+ current[key] = /^\d+$/.test(nextKey) ? [] : {}
350
+ }
351
+
352
+ current = current[key]
353
+ }
354
+
355
+ current[keys[keys.length - LAST_KEY_OFFSET]] = value
356
+ return obj
357
+ }
358
+
359
+ /**
360
+ * Converts an object or array to a formatted string representation.
361
+ *
362
+ * @param {*} obj - The object or array to convert.
363
+ * @param {number} [indentationLevel=INITIAL_LEVEL] - The current indentation level (used for nested structures).
364
+ * @returns {string} A string representation of the input object or array.
365
+ */
366
+ export function toString(obj, indentationLevel = INITIAL_LEVEL) {
367
+ if (Array.isArray(obj)) {
368
+ return `[
369
+ ${obj
370
+ .map(
371
+ (item) =>
372
+ " ".repeat(indentationLevel + NEXT_LEVEL) +
373
+ toString(item, indentationLevel + NEXT_LEVEL),
374
+ )
375
+ .join(",\n")}
376
+ ${" ".repeat(indentationLevel)}]`
377
+ }
378
+
379
+ if (typeof obj === "object" && obj != null) {
380
+ return `{
381
+ ${Object.entries(obj)
382
+ .map(
383
+ ([key, value]) =>
384
+ `${" ".repeat(indentationLevel + NEXT_LEVEL)}${key}: ${toString(
385
+ value,
386
+ indentationLevel + NEXT_LEVEL,
387
+ )}`,
388
+ )
389
+ .join(",\n")}
390
+ ${" ".repeat(indentationLevel)}}`
391
+ }
392
+
393
+ if (typeof obj === "string") {
394
+ return `"${obj}"`
395
+ }
396
+
397
+ return obj
398
+ }
399
+
64
400
  /**
65
401
  * Recursively merges properties from a source object into a target object.
66
402
  * This is a helper function for `merge` and `extend`.
@@ -93,21 +429,3 @@ function deepMerge(target, source, merger) {
93
429
  }
94
430
  return target
95
431
  }
96
-
97
- /**
98
- * Assigns default properties to a target object from a source object.
99
- * For each key in `defaultProps`, if `target[key]` is `null` or `undefined`,
100
- * it is set to `defaultProps[key]`.
101
- *
102
- * This function modifies the target object in place.
103
- *
104
- * @param {Object} target The object to apply defaults to.
105
- * @param {Object} defaultProps The object containing the default properties.
106
- * @returns {Object} The modified target object.
107
- */
108
- export function defaults(target, defaultProps) {
109
- for (const key in defaultProps) {
110
- target[key] ??= defaultProps[key]
111
- }
112
- return target
113
- }
@@ -1,4 +1,4 @@
1
- /** @typedef {import('../../types/data-structures').Tree} Tree */
1
+ /** @typedef {import('../../types/data-structures/tree').Tree} Tree */
2
2
 
3
3
  /**
4
4
  * Performs a breadth-first search (BFS) traversal on a tree structure.
@@ -1,3 +1,5 @@
1
+ const DEFAULT_DELAY = 0
2
+
1
3
  /**
2
4
  * Composes multiple functions from right to left, as if every function wraps the next one.
3
5
  * The first function (rightmost) can take multiple arguments; the remaining functions must be unary.
@@ -14,6 +16,84 @@ export function compose(...fns) {
14
16
  return (...args) => rest.reduce((acc, fn) => fn(acc), last(...args))
15
17
  }
16
18
 
19
+ /**
20
+ * Creates a debounced function that delays invoking `fn` until after `delay`
21
+ * milliseconds have elapsed since the last call.
22
+ *
23
+ * @param {Function} fn - The function to debounce.
24
+ * @param {number} [delay=0] - The number of milliseconds to delay.
25
+ * @returns {Function & { cancel: Function, flush: Function, pending: Function }} The debounced function.
26
+ */
27
+ export function debounce(fn, delay = DEFAULT_DELAY) {
28
+ let timeoutId
29
+ let lastArgs
30
+ let lastThis
31
+ let result
32
+
33
+ function clearPending() {
34
+ lastArgs = undefined
35
+ lastThis = undefined
36
+ }
37
+
38
+ function invoke() {
39
+ const args = lastArgs
40
+ const context = lastThis
41
+
42
+ clearPending()
43
+ result = fn.apply(context, args)
44
+ return result
45
+ }
46
+
47
+ function debounced(...args) {
48
+ lastArgs = args
49
+ lastThis = this
50
+
51
+ if (timeoutId !== undefined) {
52
+ clearTimeout(timeoutId)
53
+ }
54
+
55
+ timeoutId = setTimeout(() => {
56
+ timeoutId = undefined
57
+ invoke()
58
+ }, delay)
59
+
60
+ return result
61
+ }
62
+
63
+ debounced.cancel = () => {
64
+ if (timeoutId !== undefined) {
65
+ clearTimeout(timeoutId)
66
+ }
67
+
68
+ timeoutId = undefined
69
+ clearPending()
70
+ }
71
+
72
+ debounced.flush = () => {
73
+ if (timeoutId === undefined) {
74
+ return result
75
+ }
76
+
77
+ clearTimeout(timeoutId)
78
+ timeoutId = undefined
79
+ return invoke()
80
+ }
81
+
82
+ debounced.pending = () => timeoutId !== undefined
83
+
84
+ return debounced
85
+ }
86
+
87
+ /**
88
+ * Checks if a value is a function.
89
+ *
90
+ * @param {*} func - The value to check.
91
+ * @returns {boolean} True if the value is a function, false otherwise.
92
+ */
93
+ export function isFunction(func) {
94
+ return typeof func === "function"
95
+ }
96
+
17
97
  /**
18
98
  * Pipes multiple functions from left to right, as if the functions are applied one by one.
19
99
  * The first function (leftmost) can take multiple arguments; the remaining functions must be unary.
@@ -29,3 +109,91 @@ export function pipe(...fns) {
29
109
  const [first, ...rest] = fns
30
110
  return (...args) => rest.reduce((acc, fn) => fn(acc), first(...args))
31
111
  }
112
+
113
+ /**
114
+ * Creates a throttled function that invokes `fn` immediately on the leading
115
+ * edge, then suppresses further calls until `delay` milliseconds have
116
+ * elapsed.
117
+ *
118
+ * By default (`hasTrailing: false`), any calls made while suppressed are
119
+ * dropped entirely — only the leading call's arguments are ever used, and
120
+ * the very last call of a burst may be lost. Pass `{ hasTrailing: true }` to
121
+ * additionally invoke `fn` once more, after `delay` has elapsed, with the
122
+ * arguments from the most recent suppressed call — guaranteeing the final
123
+ * state of a burst is never silently dropped, at the cost of `fn` running
124
+ * again asynchronously after the burst settles.
125
+ *
126
+ * @param {Function} fn - The function to throttle.
127
+ * @param {number} [delay=0] - The number of milliseconds to suppress repeated calls.
128
+ * @param {Object} [options]
129
+ * @param {boolean} [options.hasTrailing=false] - Whether to invoke `fn` once
130
+ * more after `delay`, using the arguments of the last suppressed call.
131
+ * @returns {Function & { cancel: Function, pending: Function }} The throttled function.
132
+ */
133
+ export function throttle(
134
+ fn,
135
+ delay = DEFAULT_DELAY,
136
+ { hasTrailing = false } = {},
137
+ ) {
138
+ let timeoutId
139
+ let result
140
+ let lastArgs
141
+ let lastThis
142
+ let trailingCallPending = false
143
+
144
+ function clearPending() {
145
+ lastArgs = undefined
146
+ lastThis = undefined
147
+ trailingCallPending = false
148
+ }
149
+
150
+ function startWindow() {
151
+ timeoutId = setTimeout(() => {
152
+ timeoutId = undefined
153
+
154
+ // NOTE: this runs asynchronously, after `delay` has elapsed — so this
155
+ // is the one path where `fn` is not invoked synchronously with the
156
+ // triggering call. Callers that pass draft/proxy objects through `fn`
157
+ // (e.g. a mutative entity) must treat this like a debounced call: only
158
+ // hold onto primitives across the throttle boundary, and re-fetch any
159
+ // live object when `fn` actually runs here.
160
+ if (trailingCallPending) {
161
+ const args = lastArgs
162
+ const context = lastThis
163
+ clearPending()
164
+ result = fn.apply(context, args)
165
+ }
166
+ }, delay)
167
+ }
168
+
169
+ function throttled(...args) {
170
+ lastArgs = args
171
+ lastThis = this
172
+
173
+ if (timeoutId !== undefined) {
174
+ if (hasTrailing) {
175
+ trailingCallPending = true
176
+ }
177
+ return result
178
+ }
179
+
180
+ result = fn.apply(this, args)
181
+ lastArgs = undefined
182
+ lastThis = undefined
183
+ startWindow()
184
+ return result
185
+ }
186
+
187
+ throttled.cancel = () => {
188
+ if (timeoutId !== undefined) {
189
+ clearTimeout(timeoutId)
190
+ }
191
+
192
+ timeoutId = undefined
193
+ clearPending()
194
+ }
195
+
196
+ throttled.pending = () => timeoutId !== undefined || trailingCallPending
197
+
198
+ return throttled
199
+ }
@@ -1,4 +1 @@
1
- import * as func from "./function.js"
2
- import * as funcs from "./functions.js"
3
-
4
- export { func, funcs }
1
+ export * from "./functions.js"
@@ -1,14 +1,12 @@
1
1
  /**
2
- * @typedef {import("../../types/math").Point} Point
3
- * @typedef {import("../../types/math").Circle} Circle
4
- * @typedef {import("../../types/math").Rectangle} Rectangle
5
- * @typedef {import("../../types/math").Platform} Platform
2
+ * @typedef {import("../../types/math/point").PointInput} PointInput
3
+ * @typedef {import("../../types/math/circle").Circle} Circle
4
+ * @typedef {import("../../types/math/rectangle").Rectangle} Rectangle
6
5
  */
7
6
 
8
7
  import { intersectsCircle as pointIntersectsCircle } from "./point.js"
9
8
  import { hypothenuse } from "./triangle.js"
10
- import { clamp } from "./vector.js"
11
- import { subtract } from "./vectors.js"
9
+ import { clamp, subtract } from "./vectors.js"
12
10
 
13
11
  const HALF = 2
14
12
  const SQUARED = 2
@@ -17,7 +15,7 @@ const INITIAL_SUM = 0
17
15
  /**
18
16
  * Checks if a circle intersects with a point.
19
17
  * @param {Circle} circle - The circle to check.
20
- * @param {Point} point - The point to check.
18
+ * @param {PointInput} point - The point to check.
21
19
  * @returns {boolean} True if the point intersects the circle, false otherwise.
22
20
  */
23
21
  export function intersectsPoint(circle, point) {
package/src/math/index.js CHANGED
@@ -4,7 +4,6 @@ import * as line from "./line.js"
4
4
  import * as linearInterpolation from "./linear-interpolation.js"
5
5
  import * as numbers from "./numbers.js"
6
6
  import * as point from "./point.js"
7
- import * as quaternion from "./quaternion.js"
8
7
  import * as quaternions from "./quaternions.js"
9
8
  import * as rectangle from "./rectangle.js"
10
9
  import * as rng from "./rng.js"
@@ -12,7 +11,6 @@ import * as segment from "./segment.js"
12
11
  import * as statistics from "./statistics.js"
13
12
  import * as triangle from "./triangle.js"
14
13
  import * as trigonometry from "./trigonometry.js"
15
- import * as vector from "./vector.js"
16
14
  import * as vectors from "./vectors.js"
17
15
 
18
16
  export {
@@ -22,7 +20,6 @@ export {
22
20
  linearInterpolation,
23
21
  numbers,
24
22
  point,
25
- quaternion,
26
23
  quaternions,
27
24
  rectangle,
28
25
  rng,
@@ -30,6 +27,5 @@ export {
30
27
  statistics,
31
28
  triangle,
32
29
  trigonometry,
33
- vector,
34
30
  vectors,
35
31
  }
package/src/math/line.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import("../../types/math").Line} Line
3
- * @typedef {import("../../types/math").Point} Point
4
- * @typedef {import("../../types/math").Circle} Circle
2
+ * @typedef {import("../../types/math/line").Line} Line
3
+ * @typedef {import("../../types/math/point").Point} Point
4
+ * @typedef {import("../../types/math/circle").Circle} Circle
5
5
  */
6
6
 
7
7
  import { abs } from "./numbers.js"