@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
package/README.md CHANGED
@@ -25,12 +25,43 @@ npm install @inglorious/utils
25
25
 
26
26
  Here are a few examples of how you can use the utilities in this package.
27
27
 
28
+ ### `compose`
29
+
30
+ Compose functions together from right to left.
31
+
32
+ ```javascript
33
+ import { compose } from "@inglorious/utils/functions"
34
+
35
+ const add = (a) => (b) => a + b
36
+ const multiply = (a) => (b) => a * b
37
+
38
+ const calculate = compose(
39
+ add(10), // 30 + 10 = 40
40
+ multiply(2), // 15 * 2 = 30
41
+ add(5), // 10 + 5 = 15
42
+ )
43
+
44
+ const result = calculate(10) // 40
45
+ ```
46
+
47
+ ### `debounce`
48
+
49
+ Delay a function until calls stop for a given amount of time.
50
+
51
+ ```javascript
52
+ import { debounce } from "@inglorious/utils/functions"
53
+
54
+ const debouncedSave = debounce(() => {
55
+ save()
56
+ }, 300)
57
+ ```
58
+
28
59
  ### `pipe`
29
60
 
30
61
  Compose functions together in a readable, left-to-right sequence.
31
62
 
32
63
  ```javascript
33
- import { pipe } from "@inglorious/utils/functions/functions"
64
+ import { pipe } from "@inglorious/utils/functions"
34
65
 
35
66
  const add = (a) => (b) => a + b
36
67
  const multiply = (a) => (b) => a * b
@@ -44,12 +75,24 @@ const calculate = pipe(
44
75
  const result = calculate(10) // 40
45
76
  ```
46
77
 
78
+ ### `throttle`
79
+
80
+ Run a function immediately, then suppress repeated calls until the wait time has elapsed.
81
+
82
+ ```javascript
83
+ import { throttle } from "@inglorious/utils/functions"
84
+
85
+ const throttledSave = throttle(() => {
86
+ save()
87
+ }, 300)
88
+ ```
89
+
47
90
  ### Vector Math
48
91
 
49
92
  Perform vector operations on plain objects.
50
93
 
51
94
  ```javascript
52
- import { add } from "@inglorious/utils/math/vector.js"
95
+ import { add } from "@inglorious/utils/vectors"
53
96
 
54
97
  const position = [10, 20]
55
98
  const velocity = [2, -1]
@@ -64,11 +107,32 @@ const newPosition = add(position, velocity)
64
107
 
65
108
  This package is designed to be used with subpath imports, which helps with tree-shaking.
66
109
 
110
+ Most modules are grouped by category:
111
+
67
112
  - **`@inglorious/utils/algorithms`**: Utilities for AI algorithms, like decision trees and A\*.
68
- - **`@inglorious/utils/functions`**: Utilities for function composition.
113
+ - **`@inglorious/utils/data-structures`**: Utilities for arrays, boards, heaps, objects, sets, strings, and trees.
114
+ - **`@inglorious/utils/functions`**: Utilities for function composition and timing.
69
115
  - **`@inglorious/utils/math`**: Utilities for math operations.
70
116
  - **`@inglorious/utils/physics`**: Utilities for calculations on friction, acceleration, and gravity.
71
117
 
118
+ The four largest modules are also available as top-level subpaths, so you don't need to
119
+ know whether a helper lives under `data-structures` or `math`:
120
+
121
+ - **`@inglorious/utils/functions`**: `compose`, `debounce`, `isFunction`, `pipe`, `throttle`.
122
+ - **`@inglorious/utils/objects`**: Single-object helpers (`clone`, `get`, `set`, `produce`, ...) alongside multi-object helpers (`merge`, `extend`, `defaults`, ...).
123
+ - **`@inglorious/utils/quaternions`**: `quaternion` and `combine`.
124
+ - **`@inglorious/utils/vectors`**: Single-vector helpers (`magnitude`, `normalize`, `rotate`, ...) alongside multi-vector helpers (`cross`, `dot`, `sum`, ...).
125
+
126
+ `divide`, `mod`, `multiply` and `power` accept either a scalar or one or more vectors:
127
+ a scalar is applied to every component, while vectors are combined component-wise.
128
+
129
+ ```javascript
130
+ import { multiply } from "@inglorious/utils/vectors"
131
+
132
+ multiply([1, 2, 3], 2) // => [2, 4, 6]
133
+ multiply([1, 2, 3], [4, 5, 6]) // => [4, 10, 18]
134
+ ```
135
+
72
136
  ---
73
137
 
74
138
  ## License
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@inglorious/utils",
3
- "version": "3.8.0",
3
+ "version": "4.0.0",
4
4
  "description": "A set of general-purpose utility functions designed with functional programming principles in mind.",
5
- "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
5
+ "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -26,13 +26,89 @@
26
26
  "types": "./types/index.d.ts",
27
27
  "import": "./src/index.js"
28
28
  },
29
- "./data-structures/string.js": {
30
- "types": "./types/data-structures/string.d.ts",
31
- "import": "./src/data-structures/string.js"
29
+ "./algorithms": {
30
+ "types": "./types/algorithms/index.d.ts",
31
+ "import": "./src/algorithms/index.js"
32
32
  },
33
- "./*": {
34
- "types": "./types/*.d.ts",
35
- "import": "./src/*"
33
+ "./algorithms/*.js": {
34
+ "types": "./types/algorithms/*.d.ts",
35
+ "import": "./src/algorithms/*.js"
36
+ },
37
+ "./algorithms/*": {
38
+ "types": "./types/algorithms/*.d.ts",
39
+ "import": "./src/algorithms/*.js"
40
+ },
41
+ "./data-structures": {
42
+ "types": "./types/data-structures/index.d.ts",
43
+ "import": "./src/data-structures/index.js"
44
+ },
45
+ "./data-structures/*.js": {
46
+ "types": "./types/data-structures/*.d.ts",
47
+ "import": "./src/data-structures/*.js"
48
+ },
49
+ "./data-structures/*": {
50
+ "types": "./types/data-structures/*.d.ts",
51
+ "import": "./src/data-structures/*.js"
52
+ },
53
+ "./functions": {
54
+ "types": "./types/functions/index.d.ts",
55
+ "import": "./src/functions/index.js"
56
+ },
57
+ "./math": {
58
+ "types": "./types/math/index.d.ts",
59
+ "import": "./src/math/index.js"
60
+ },
61
+ "./math/*.js": {
62
+ "types": "./types/math/*.d.ts",
63
+ "import": "./src/math/*.js"
64
+ },
65
+ "./math/*": {
66
+ "types": "./types/math/*.d.ts",
67
+ "import": "./src/math/*.js"
68
+ },
69
+ "./physics": {
70
+ "types": "./types/physics/index.d.ts",
71
+ "import": "./src/physics/index.js"
72
+ },
73
+ "./physics/*.js": {
74
+ "types": "./types/physics/*.d.ts",
75
+ "import": "./src/physics/*.js"
76
+ },
77
+ "./physics/*": {
78
+ "types": "./types/physics/*.d.ts",
79
+ "import": "./src/physics/*.js"
80
+ },
81
+ "./objects.js": {
82
+ "types": "./types/data-structures/objects.d.ts",
83
+ "import": "./src/data-structures/objects.js"
84
+ },
85
+ "./objects": {
86
+ "types": "./types/data-structures/objects.d.ts",
87
+ "import": "./src/data-structures/objects.js"
88
+ },
89
+ "./quaternions.js": {
90
+ "types": "./types/math/quaternions.d.ts",
91
+ "import": "./src/math/quaternions.js"
92
+ },
93
+ "./quaternions": {
94
+ "types": "./types/math/quaternions.d.ts",
95
+ "import": "./src/math/quaternions.js"
96
+ },
97
+ "./vectors.js": {
98
+ "types": "./types/math/vectors.d.ts",
99
+ "import": "./src/math/vectors.js"
100
+ },
101
+ "./vectors": {
102
+ "types": "./types/math/vectors.d.ts",
103
+ "import": "./src/math/vectors.js"
104
+ },
105
+ "./v.js": {
106
+ "types": "./types/v.d.ts",
107
+ "import": "./src/v.js"
108
+ },
109
+ "./v": {
110
+ "types": "./types/v.d.ts",
111
+ "import": "./src/v.js"
36
112
  }
37
113
  },
38
114
  "files": [
@@ -45,10 +121,10 @@
45
121
  },
46
122
  "sideEffects": false,
47
123
  "devDependencies": {
48
- "prettier": "^3.6.2",
49
- "vite": "^7.1.3",
50
- "vitest": "^1.6.1",
51
- "@inglorious/eslint-config": "1.1.2"
124
+ "@inglorious/eslint-config": "1.1.2",
125
+ "prettier": "^3.9.6",
126
+ "vite": "^8.2.2",
127
+ "vitest": "^5.0.0"
52
128
  },
53
129
  "engines": {
54
130
  "node": ">= 22"
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import("../../types/algorithms").Conditions} Conditions
3
- * @typedef {import("../../types/algorithms").Outcome} Outcome
4
- * @typedef {import("../../types/algorithms").DecisionTree} DecisionTree
2
+ * @typedef {import("../../types/algorithms/decision-tree").Conditions} Conditions
3
+ * @typedef {import("../../types/algorithms/decision-tree").Outcome} Outcome
4
+ * @typedef {import("../../types/algorithms/decision-tree").DecisionTree} DecisionTree
5
5
  */
6
6
 
7
7
  /**
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @typedef {import("../../types/data-structures").Node} Node
3
- * @typedef {import("../../types/data-structures").Graph} Graph
2
+ * @typedef {import("../../types/algorithms/path-finding").Node} Node
3
+ * @typedef {import("../../types/algorithms/path-finding").Graph} Graph
4
+ * @typedef {import("../../types/math/vectors").Vector2} Vector2
4
5
  */
5
6
 
6
7
  import { contains, push, remove, root } from "../data-structures/heap.js"
7
- import { abs, magnitude } from "../math/vector.js"
8
- import { subtract } from "../math/vectors.js"
8
+ import { abs, magnitude, subtract } from "../math/vectors.js"
9
9
 
10
10
  const NO_COST = 0
11
11
 
@@ -32,7 +32,7 @@ export const eucledianDistance = (a, b) =>
32
32
  *
33
33
  * @param {Node} a - The first node.
34
34
  * @param {Node} b - The second node.
35
- * @returns {number} - The Manhattan distance between the two nodes.
35
+ * @returns {Vector2} The component-wise Manhattan distance between the two nodes.
36
36
  */
37
37
  export const manhattanDistance = (a, b) => abs(subtract(a.position, b.position))
38
38
 
@@ -61,7 +61,7 @@ const compareTotalCost = (a, b) => b.totalCost - a.totalCost
61
61
  * @param {string} start - The ID of the start node.
62
62
  * @param {string} end - The ID of the end node.
63
63
  * @param {(a: Node, b: Node) => number} [heuristic=eucledianDistance] - The heuristic function to estimate the cost.
64
- * @returns {string[]} - An array of node IDs representing the shortest path.
64
+ * @returns {string[]|undefined} - The node IDs representing the shortest path, or `undefined` if no path exists.
65
65
  */
66
66
  export function findPath(graph, start, end, heuristic = eucledianDistance) {
67
67
  const { nodes, arcs } = adaptGraph(graph)
@@ -11,24 +11,55 @@ const DEFAULT_FILLER = () => null
11
11
  */
12
12
  const DEFAULT_CELL_TO_STRING = (cell) => `${cell}`
13
13
 
14
+ /**
15
+ * Default predicate that matches every neighboring cell.
16
+ * @returns {boolean} Always true.
17
+ */
18
+ const DEFAULT_NEIGHBOR_PREDICATE = () => true
19
+
14
20
  const FIRST_ROW = 0
15
21
  const FIRST_COLUMN = 0
16
22
  const LAST_ROW_OFFSET = 1
17
23
  const LAST_COLUMN_OFFSET = 1
18
- const MOVE_DOWN = 1
19
- const MOVE_LEFT = -1
20
- const MOVE_RIGHT = 1
21
- const MOVE_UP = -1
24
+ const DOWN = 1
25
+ const LEFT = -1
26
+ const RIGHT = 1
27
+ const UP = -1
28
+ const CENTER = 0
29
+
30
+ const BOTH = "both"
31
+ const HORIZONTAL = "horizontal"
32
+ const NO_WRAP = undefined
33
+ const VERTICAL = "vertical"
34
+
35
+ const NEIGHBOR_OFFSETS = [
36
+ [UP, LEFT],
37
+ [UP, CENTER],
38
+ [UP, RIGHT],
39
+ [CENTER, LEFT],
40
+ [CENTER, RIGHT],
41
+ [DOWN, LEFT],
42
+ [DOWN, CENTER],
43
+ [DOWN, RIGHT],
44
+ ]
22
45
 
23
46
  /**
24
- * Calculates the index in a one-dimensional array for given row and column.
25
- * @param {number} row - The row index.
26
- * @param {number} column - The column index.
27
- * @param {number} columns - The total number of columns.
28
- * @returns {number} The calculated index.
47
+ * Counts the neighboring cells that satisfy a predicate.
48
+ * @param {Array} cells - The board cells.
49
+ * @param {[number, number]} coords - The coordinates [row, column].
50
+ * @param {[number, number]} size - Array containing the number of rows and columns.
51
+ * @param {Object} [options] - Neighbor options.
52
+ * @param {Function} [options.predicate=DEFAULT_NEIGHBOR_PREDICATE] - The predicate applied to each neighboring cell.
53
+ * @param {"horizontal" | "vertical" | "both"} [options.wrap=undefined] - The axes that wrap around board edges.
54
+ * @returns {number} The number of matching neighboring cells.
29
55
  */
30
- function getIndex(row, column, columns) {
31
- return row * columns + column
56
+ export function countNeighbors(cells, coords, size, options = {}) {
57
+ const { predicate = DEFAULT_NEIGHBOR_PREDICATE, wrap = NO_WRAP } = options
58
+ const [, columns] = size
59
+
60
+ return neighbors(coords, size, { wrap }).filter(([row, column]) =>
61
+ predicate(cells[getIndex(row, column, columns)]),
62
+ ).length
32
63
  }
33
64
 
34
65
  /**
@@ -56,7 +87,7 @@ export function down([i, j], [rows]) {
56
87
  if (i === rows - LAST_ROW_OFFSET) {
57
88
  throw new Error()
58
89
  }
59
- return [i + MOVE_DOWN, j]
90
+ return [i + DOWN, j]
60
91
  }
61
92
 
62
93
  /**
@@ -79,6 +110,17 @@ export function downRight(coords, size) {
79
110
  return down(right(coords, size), size)
80
111
  }
81
112
 
113
+ /**
114
+ * Calculates the index in a one-dimensional array for given row and column.
115
+ * @param {number} row - The row index.
116
+ * @param {number} column - The column index.
117
+ * @param {number} columns - The total number of columns.
118
+ * @returns {number} The calculated index.
119
+ */
120
+ export function getIndex(row, column, columns) {
121
+ return row * columns + column
122
+ }
123
+
82
124
  /**
83
125
  * Moves the given coordinates one step left.
84
126
  * @param {[number, number]} coords - The current coordinates [row, column].
@@ -89,7 +131,36 @@ export function left([i, j]) {
89
131
  if (j === FIRST_COLUMN) {
90
132
  throw new Error()
91
133
  }
92
- return [i, j + MOVE_LEFT]
134
+ return [i, j + LEFT]
135
+ }
136
+
137
+ /**
138
+ * Returns the coordinates neighboring the given coordinates that fall within the board.
139
+ * @param {[number, number]} coords - The coordinates [row, column].
140
+ * @param {[number, number]} size - Array containing the number of rows and columns.
141
+ * @param {Object} [options] - Neighbor options.
142
+ * @param {"horizontal" | "vertical" | "both"} [options.wrap=undefined] - The axes that wrap around board edges. "horizontal" wraps the column axis, "vertical" wraps the row axis, and "both" wraps each axis independently.
143
+ * @returns {[number, number][]} The neighboring coordinates within the board.
144
+ */
145
+ export function neighbors([i, j], [rows, columns], options = {}) {
146
+ const { wrap = NO_WRAP } = options
147
+ const shouldWrapRows = wrap === BOTH || wrap === VERTICAL
148
+ const shouldWrapColumns = wrap === BOTH || wrap === HORIZONTAL
149
+
150
+ return NEIGHBOR_OFFSETS.map(([rowOffset, columnOffset]) => {
151
+ const row = shouldWrapRows ? wrapIndex(i + rowOffset, rows) : i + rowOffset
152
+ const column = shouldWrapColumns
153
+ ? wrapIndex(j + columnOffset, columns)
154
+ : j + columnOffset
155
+
156
+ return [row, column]
157
+ }).filter(
158
+ ([row, column]) =>
159
+ row >= FIRST_ROW &&
160
+ row < rows &&
161
+ column >= FIRST_COLUMN &&
162
+ column < columns,
163
+ )
93
164
  }
94
165
 
95
166
  /**
@@ -103,7 +174,7 @@ export function right([i, j], [, columns]) {
103
174
  if (j === columns - LAST_COLUMN_OFFSET) {
104
175
  throw new Error()
105
176
  }
106
- return [i, j + MOVE_RIGHT]
177
+ return [i, j + RIGHT]
107
178
  }
108
179
 
109
180
  /**
@@ -135,7 +206,7 @@ export function up([i, j]) {
135
206
  if (i === FIRST_ROW) {
136
207
  throw new Error()
137
208
  }
138
- return [i + MOVE_UP, j]
209
+ return [i + UP, j]
139
210
  }
140
211
 
141
212
  /**
@@ -157,3 +228,14 @@ export function upLeft(coords, size) {
157
228
  export function upRight(coords, size) {
158
229
  return up(right(coords, size), size)
159
230
  }
231
+
232
+ /**
233
+ * Normalizes an index into a wrapping range.
234
+ * @param {number} index - The index to normalize.
235
+ * @param {number} length - The range length.
236
+ * @returns {number} The normalized index.
237
+ */
238
+ function wrapIndex(index, length) {
239
+ const remainder = index % length
240
+ return remainder < FIRST_ROW ? remainder + length : remainder
241
+ }
@@ -2,10 +2,9 @@ import * as array from "./array.js"
2
2
  import * as board from "./board.js"
3
3
  import * as boolean from "./boolean.js"
4
4
  import * as heap from "./heap.js"
5
- import * as object from "./object.js"
6
5
  import * as objects from "./objects.js"
7
6
  import * as set from "./set.js"
8
7
  import * as string from "./string.js"
9
8
  import * as tree from "./tree.js"
10
9
 
11
- export { array, board, boolean, heap, object, objects, set, string, tree }
10
+ export { array, board, boolean, heap, objects, set, string, tree }