@inglorious/utils 4.0.0 → 5.1.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/README.md +22 -6
- package/package.json +36 -20
- package/src/algorithms/path-finding.js +24 -20
- package/src/data-structures/array.js +12 -0
- package/src/data-structures/index.js +2 -2
- package/src/data-structures/{objects.js → object.js} +100 -14
- package/src/data-structures/string.js +77 -0
- package/src/functions/index.js +1 -1
- package/src/math/circle.js +1 -1
- package/src/math/hitmask.js +1 -1
- package/src/math/index.js +4 -4
- package/src/math/line.js +1 -1
- package/src/math/point.js +23 -2
- package/src/math/{quaternions.js → quaternion.js} +3 -3
- package/src/math/rectangle.js +12 -0
- package/src/math/rng.js +5 -2
- package/src/math/segment.js +1 -1
- package/src/math/trigonometry.js +1 -1
- package/src/math/{vectors.js → vector.js} +67 -8
- package/src/physics/acceleration.js +2 -2
- package/src/physics/friction.js +2 -2
- package/src/physics/gravity.js +2 -2
- package/src/physics/velocity.js +2 -2
- package/src/v.js +1 -1
- package/types/algorithms/path-finding.d.ts +1 -1
- package/types/data-structures/array.d.ts +5 -0
- package/types/data-structures/index.d.ts +1 -1
- package/types/data-structures/{objects.d.ts → object.d.ts} +4 -3
- package/types/math/index.d.ts +3 -3
- package/types/math/point.d.ts +26 -1
- package/types/math/{quaternions.d.ts → quaternion.d.ts} +1 -1
- package/types/math/rectangle.d.ts +23 -2
- package/types/math/{vectors.d.ts → vector.d.ts} +29 -0
- package/types/physics/acceleration.d.ts +1 -1
- package/types/physics/friction.d.ts +1 -1
- package/types/physics/gravity.d.ts +1 -1
- package/types/physics/velocity.d.ts +1 -1
- package/types/v.d.ts +1 -1
- /package/src/functions/{functions.js → function.js} +0 -0
- /package/src/math/{numbers.js → number.js} +0 -0
- /package/types/functions/{functions.d.ts → function.d.ts} +0 -0
- /package/types/math/{numbers.d.ts → number.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ const throttledSave = throttle(() => {
|
|
|
92
92
|
Perform vector operations on plain objects.
|
|
93
93
|
|
|
94
94
|
```javascript
|
|
95
|
-
import { add } from "@inglorious/utils/
|
|
95
|
+
import { add } from "@inglorious/utils/vector.js"
|
|
96
96
|
|
|
97
97
|
const position = [10, 20]
|
|
98
98
|
const velocity = [2, -1]
|
|
@@ -101,6 +101,23 @@ const newPosition = add(position, velocity)
|
|
|
101
101
|
// => [12, 19]
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
Two vectors are worth knowing by name: `ZERO_VECTOR`, with every component at zero, and
|
|
105
|
+
`UNIT_VECTOR`, one unit long along the x axis. The zero one is handy for naming the components
|
|
106
|
+
you care about, or for building a vector up from scratch:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
import { ZERO_VECTOR } from "@inglorious/utils/math/vector.js"
|
|
110
|
+
import { v } from "@inglorious/utils/v.js"
|
|
111
|
+
|
|
112
|
+
const [, NO_RISE, NO_DEPTH] = ZERO_VECTOR
|
|
113
|
+
|
|
114
|
+
const position = v(10, 20, NO_DEPTH)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Vectors are mutable arrays, so copy a shared vector rather than storing it where something writes
|
|
118
|
+
to one, such as an entity position. Copy it with `v(...ZERO_VECTOR)`: a plain spread
|
|
119
|
+
(`[...ZERO_VECTOR]`) gives you a new array but drops the tag that `isVector` looks for.
|
|
120
|
+
|
|
104
121
|
---
|
|
105
122
|
|
|
106
123
|
## API
|
|
@@ -111,7 +128,6 @@ Most modules are grouped by category:
|
|
|
111
128
|
|
|
112
129
|
- **`@inglorious/utils/algorithms`**: Utilities for AI algorithms, like decision trees and A\*.
|
|
113
130
|
- **`@inglorious/utils/data-structures`**: Utilities for arrays, boards, heaps, objects, sets, strings, and trees.
|
|
114
|
-
- **`@inglorious/utils/functions`**: Utilities for function composition and timing.
|
|
115
131
|
- **`@inglorious/utils/math`**: Utilities for math operations.
|
|
116
132
|
- **`@inglorious/utils/physics`**: Utilities for calculations on friction, acceleration, and gravity.
|
|
117
133
|
|
|
@@ -119,15 +135,15 @@ The four largest modules are also available as top-level subpaths, so you don't
|
|
|
119
135
|
know whether a helper lives under `data-structures` or `math`:
|
|
120
136
|
|
|
121
137
|
- **`@inglorious/utils/functions`**: `compose`, `debounce`, `isFunction`, `pipe`, `throttle`.
|
|
122
|
-
- **`@inglorious/utils/
|
|
123
|
-
- **`@inglorious/utils/
|
|
124
|
-
- **`@inglorious/utils/
|
|
138
|
+
- **`@inglorious/utils/object`**: Single-object helpers (`clone`, `get`, `set`, `produce`, ...) alongside multi-object helpers (`merge`, `extend`, `defaults`, ...).
|
|
139
|
+
- **`@inglorious/utils/quaternion`**: `quaternion` and `combine`.
|
|
140
|
+
- **`@inglorious/utils/vector`**: Single-vector helpers (`magnitude`, `normalize`, `rotate`, ...) alongside multi-vector helpers (`cross`, `dot`, `sum`, ...).
|
|
125
141
|
|
|
126
142
|
`divide`, `mod`, `multiply` and `power` accept either a scalar or one or more vectors:
|
|
127
143
|
a scalar is applied to every component, while vectors are combined component-wise.
|
|
128
144
|
|
|
129
145
|
```javascript
|
|
130
|
-
import { multiply } from "@inglorious/utils/
|
|
146
|
+
import { multiply } from "@inglorious/utils/vector.js"
|
|
131
147
|
|
|
132
148
|
multiply([1, 2, 3], 2) // => [2, 4, 6]
|
|
133
149
|
multiply([1, 2, 3], [4, 5, 6]) // => [4, 10, 18]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "A set of general-purpose utility functions designed with functional programming principles in mind.",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
"types": "./types/index.d.ts",
|
|
27
27
|
"import": "./src/index.js"
|
|
28
28
|
},
|
|
29
|
+
"./array.js": {
|
|
30
|
+
"types": "./types/data-structures/array.d.ts",
|
|
31
|
+
"import": "./src/data-structures/array.js"
|
|
32
|
+
},
|
|
33
|
+
"./array": {
|
|
34
|
+
"types": "./types/data-structures/array.d.ts",
|
|
35
|
+
"import": "./src/data-structures/array.js"
|
|
36
|
+
},
|
|
29
37
|
"./algorithms": {
|
|
30
38
|
"types": "./types/algorithms/index.d.ts",
|
|
31
39
|
"import": "./src/algorithms/index.js"
|
|
@@ -50,6 +58,14 @@
|
|
|
50
58
|
"types": "./types/data-structures/*.d.ts",
|
|
51
59
|
"import": "./src/data-structures/*.js"
|
|
52
60
|
},
|
|
61
|
+
"./function.js": {
|
|
62
|
+
"types": "./types/functions/function.d.ts",
|
|
63
|
+
"import": "./src/functions/function.js"
|
|
64
|
+
},
|
|
65
|
+
"./function": {
|
|
66
|
+
"types": "./types/functions/function.d.ts",
|
|
67
|
+
"import": "./src/functions/function.js"
|
|
68
|
+
},
|
|
53
69
|
"./functions": {
|
|
54
70
|
"types": "./types/functions/index.d.ts",
|
|
55
71
|
"import": "./src/functions/index.js"
|
|
@@ -78,29 +94,29 @@
|
|
|
78
94
|
"types": "./types/physics/*.d.ts",
|
|
79
95
|
"import": "./src/physics/*.js"
|
|
80
96
|
},
|
|
81
|
-
"./
|
|
82
|
-
"types": "./types/data-structures/
|
|
83
|
-
"import": "./src/data-structures/
|
|
97
|
+
"./object.js": {
|
|
98
|
+
"types": "./types/data-structures/object.d.ts",
|
|
99
|
+
"import": "./src/data-structures/object.js"
|
|
84
100
|
},
|
|
85
|
-
"./
|
|
86
|
-
"types": "./types/data-structures/
|
|
87
|
-
"import": "./src/data-structures/
|
|
101
|
+
"./object": {
|
|
102
|
+
"types": "./types/data-structures/object.d.ts",
|
|
103
|
+
"import": "./src/data-structures/object.js"
|
|
88
104
|
},
|
|
89
|
-
"./
|
|
90
|
-
"types": "./types/math/
|
|
91
|
-
"import": "./src/math/
|
|
105
|
+
"./quaternion.js": {
|
|
106
|
+
"types": "./types/math/quaternion.d.ts",
|
|
107
|
+
"import": "./src/math/quaternion.js"
|
|
92
108
|
},
|
|
93
|
-
"./
|
|
94
|
-
"types": "./types/math/
|
|
95
|
-
"import": "./src/math/
|
|
109
|
+
"./quaternion": {
|
|
110
|
+
"types": "./types/math/quaternion.d.ts",
|
|
111
|
+
"import": "./src/math/quaternion.js"
|
|
96
112
|
},
|
|
97
|
-
"./
|
|
98
|
-
"types": "./types/math/
|
|
99
|
-
"import": "./src/math/
|
|
113
|
+
"./vector.js": {
|
|
114
|
+
"types": "./types/math/vector.d.ts",
|
|
115
|
+
"import": "./src/math/vector.js"
|
|
100
116
|
},
|
|
101
|
-
"./
|
|
102
|
-
"types": "./types/math/
|
|
103
|
-
"import": "./src/math/
|
|
117
|
+
"./vector": {
|
|
118
|
+
"types": "./types/math/vector.d.ts",
|
|
119
|
+
"import": "./src/math/vector.js"
|
|
104
120
|
},
|
|
105
121
|
"./v.js": {
|
|
106
122
|
"types": "./types/v.d.ts",
|
|
@@ -121,7 +137,7 @@
|
|
|
121
137
|
},
|
|
122
138
|
"sideEffects": false,
|
|
123
139
|
"devDependencies": {
|
|
124
|
-
"@inglorious/eslint-config": "1.1.
|
|
140
|
+
"@inglorious/eslint-config": "1.1.4",
|
|
125
141
|
"prettier": "^3.9.6",
|
|
126
142
|
"vite": "^8.2.2",
|
|
127
143
|
"vitest": "^5.0.0"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import("../../types/algorithms/path-finding").Node} Node
|
|
3
3
|
* @typedef {import("../../types/algorithms/path-finding").Graph} Graph
|
|
4
|
-
* @typedef {import("../../types/math/
|
|
4
|
+
* @typedef {import("../../types/math/vector").Vector2} Vector2
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { contains, push, remove, root } from "../data-structures/heap.js"
|
|
8
|
-
import { abs, magnitude, subtract } from "../math/
|
|
8
|
+
import { abs, magnitude, subtract } from "../math/vector.js"
|
|
9
9
|
|
|
10
10
|
const NO_COST = 0
|
|
11
11
|
|
|
@@ -36,24 +36,6 @@ export const eucledianDistance = (a, b) =>
|
|
|
36
36
|
*/
|
|
37
37
|
export const manhattanDistance = (a, b) => abs(subtract(a.position, b.position))
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
* Compares the cost of two nodes.
|
|
41
|
-
*
|
|
42
|
-
* @param {Node} a - The first node.
|
|
43
|
-
* @param {Node} b - The second node.
|
|
44
|
-
* @returns {number} - The difference in cost between the two nodes.
|
|
45
|
-
*/
|
|
46
|
-
const compareCost = (a, b) => b.cost - a.cost
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Compares the total cost of two nodes.
|
|
50
|
-
*
|
|
51
|
-
* @param {Node} a - The first node.
|
|
52
|
-
* @param {Node} b - The second node.
|
|
53
|
-
* @returns {number} - The difference in total cost between the two nodes.
|
|
54
|
-
*/
|
|
55
|
-
const compareTotalCost = (a, b) => b.totalCost - a.totalCost
|
|
56
|
-
|
|
57
39
|
/**
|
|
58
40
|
* Finds the shortest path in a graph from a start node to an end node using a heuristic.
|
|
59
41
|
*
|
|
@@ -148,3 +130,25 @@ function adaptGraph(graph) {
|
|
|
148
130
|
function createFindNode(nodes) {
|
|
149
131
|
return (id) => nodes.find((node) => node.id === id)
|
|
150
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Compares the cost of two nodes.
|
|
136
|
+
*
|
|
137
|
+
* @param {Node} a - The first node.
|
|
138
|
+
* @param {Node} b - The second node.
|
|
139
|
+
* @returns {number} - The difference in cost between the two nodes.
|
|
140
|
+
*/
|
|
141
|
+
function compareCost(a, b) {
|
|
142
|
+
return b.cost - a.cost
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Compares the total cost of two nodes.
|
|
147
|
+
*
|
|
148
|
+
* @param {Node} a - The first node.
|
|
149
|
+
* @param {Node} b - The second node.
|
|
150
|
+
* @returns {number} - The difference in total cost between the two nodes.
|
|
151
|
+
*/
|
|
152
|
+
function compareTotalCost(a, b) {
|
|
153
|
+
return b.totalCost - a.totalCost
|
|
154
|
+
}
|
|
@@ -22,6 +22,18 @@ export function contains(arr, item) {
|
|
|
22
22
|
return arr.includes(item)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* The index `step` places along a list of `length`, wrapping round at either end.
|
|
27
|
+
*
|
|
28
|
+
* @param {number} index - Where to start from.
|
|
29
|
+
* @param {number} length - How long the list is.
|
|
30
|
+
* @param {number} step - How far to move. Negative steps move backwards.
|
|
31
|
+
* @returns {number} - The index landed on, always within the list.
|
|
32
|
+
*/
|
|
33
|
+
export function cycleIndex(index, length, step) {
|
|
34
|
+
return (((index + step) % length) + length) % length
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
/**
|
|
26
38
|
* Ensures that the given value is an array. If it's not an array, it wraps it in one.
|
|
27
39
|
*
|
|
@@ -2,9 +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
|
|
5
|
+
import * as object from "./object.js"
|
|
6
6
|
import * as set from "./set.js"
|
|
7
7
|
import * as string from "./string.js"
|
|
8
8
|
import * as tree from "./tree.js"
|
|
9
9
|
|
|
10
|
-
export { array, board, boolean, heap,
|
|
10
|
+
export { array, board, boolean, heap, object, set, string, tree }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isFunction } from "../functions/
|
|
1
|
+
import { isFunction } from "../functions/function.js"
|
|
2
2
|
import { v } from "../v.js"
|
|
3
3
|
import { isArray } from "./array.js"
|
|
4
4
|
|
|
@@ -8,24 +8,59 @@ const NEXT_KEY_OFFSET = 1
|
|
|
8
8
|
const NEXT_LEVEL = 2
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Creates a deep clone of a given value
|
|
12
|
-
* This is a modern and efficient way to deep-clone objects.
|
|
11
|
+
* Creates a deep clone of a given value.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Plain objects, arrays and primitives -- what state is nearly always made of -- are
|
|
14
|
+
* copied by hand, because a general algorithm pays for being one: across thousands of
|
|
15
|
+
* small objects, `structuredClone` takes about twice as long and a JSON round trip
|
|
16
|
+
* longer still. Vectors stay vectors, which neither of those keeps: the tag that marks
|
|
17
|
+
* one is not enumerable, so anything that copies only what it can see drops it.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* Anything else is handed to `structuredClone`, so a `Date` or a `Map` is still copied
|
|
20
|
+
* properly, and DOM nodes or class instances with non-cloneable internals still throw a
|
|
21
|
+
* `DataCloneError`. Functions are kept as they are rather than copied, since there is
|
|
22
|
+
* nothing in one to copy.
|
|
20
23
|
*
|
|
21
24
|
* This is similar to Lodash's `_.cloneDeep`.
|
|
22
25
|
*
|
|
23
26
|
* @template T
|
|
24
|
-
* @param {T}
|
|
27
|
+
* @param {T} value - The value to clone.
|
|
25
28
|
* @returns {T} A deep clone of the input value.
|
|
26
29
|
*/
|
|
27
|
-
export function clone(
|
|
28
|
-
|
|
30
|
+
export function clone(value) {
|
|
31
|
+
// A vector holds numbers and nothing else, so it needs no copying below the top -- and
|
|
32
|
+
// building it in one go is noticeably cheaper than copying it and tagging the copy.
|
|
33
|
+
if (value?.__isVector__) {
|
|
34
|
+
return v(...value)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isArray(value)) {
|
|
38
|
+
const copy = new Array(value.length)
|
|
39
|
+
|
|
40
|
+
for (let index = 0; index < value.length; index++) {
|
|
41
|
+
copy[index] = clone(value[index])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return copy
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (value === null || typeof value !== "object") {
|
|
48
|
+
return value
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const prototype = Object.getPrototypeOf(value)
|
|
52
|
+
|
|
53
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
54
|
+
return structuredClone(value)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const copy = {}
|
|
58
|
+
|
|
59
|
+
for (const key in value) {
|
|
60
|
+
copy[key] = clone(value[key])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return copy
|
|
29
64
|
}
|
|
30
65
|
|
|
31
66
|
/**
|
|
@@ -186,6 +221,57 @@ export function get(obj, path, defaultValue = undefined) {
|
|
|
186
221
|
* @param {*} value - The value to check.
|
|
187
222
|
* @returns {boolean} True if the value is a plain object, false otherwise.
|
|
188
223
|
*/
|
|
224
|
+
/**
|
|
225
|
+
* Whether two values are the same, all the way down.
|
|
226
|
+
*
|
|
227
|
+
* Written here rather than with `JSON.stringify` because stringify cannot tell a key
|
|
228
|
+
* that was left out from one that was set to nothing, and those are different: a key
|
|
229
|
+
* mapped to `undefined` is a key with nothing mapped to it, which is how a mapping is
|
|
230
|
+
* taken off.
|
|
231
|
+
*
|
|
232
|
+
* @param {*} left - The first value.
|
|
233
|
+
* @param {*} right - The second value.
|
|
234
|
+
* @param {WeakMap} [seen] - The pairs already compared, so a structure that contains
|
|
235
|
+
* itself is not walked for ever. Passed along rather than asked for.
|
|
236
|
+
*
|
|
237
|
+
* What it does not settle is whether two structures *share* the same parts. One value
|
|
238
|
+
* used twice against two equal ones written out twice are reported as different, because
|
|
239
|
+
* only the pairs already met are remembered. That errs towards calling things changed
|
|
240
|
+
* when they are not, which is the safe way round for the thing this is asked about.
|
|
241
|
+
*
|
|
242
|
+
* @returns {boolean} Whether the two are the same.
|
|
243
|
+
*/
|
|
244
|
+
export function isDeepEqual(left, right, seen = new WeakMap()) {
|
|
245
|
+
if (Object.is(left, right)) return true
|
|
246
|
+
|
|
247
|
+
// `Object.is` has already said no to these two being the same object, and neither can be
|
|
248
|
+
// walked: a function is equal to itself and nothing else, and a key present in one and
|
|
249
|
+
// not the other is a difference even when both read as undefined.
|
|
250
|
+
if (typeof left !== "object" || typeof right !== "object") return false
|
|
251
|
+
if (left === null || right === null) return false
|
|
252
|
+
|
|
253
|
+
if (Array.isArray(left) !== Array.isArray(right)) return false
|
|
254
|
+
|
|
255
|
+
// A thing that contains itself would otherwise be walked for ever. Meeting the same
|
|
256
|
+
// thing twice means it has already been compared, and it was the same then or the walk
|
|
257
|
+
// would have stopped there -- so the only question left is whether it was matched to
|
|
258
|
+
// this one. A structure that loops back to itself differently is a difference, which is
|
|
259
|
+
// what stops this reporting two equal loops that are not.
|
|
260
|
+
if (seen.has(left)) return seen.get(left) === right
|
|
261
|
+
|
|
262
|
+
seen.set(left, right)
|
|
263
|
+
|
|
264
|
+
const leftKeys = Object.keys(left)
|
|
265
|
+
const rightKeys = Object.keys(right)
|
|
266
|
+
|
|
267
|
+
if (leftKeys.length !== rightKeys.length) return false
|
|
268
|
+
|
|
269
|
+
return leftKeys.every(
|
|
270
|
+
(key) =>
|
|
271
|
+
Object.hasOwn(right, key) && isDeepEqual(left[key], right[key], seen),
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
189
275
|
export function isObject(value) {
|
|
190
276
|
return value != null && value.constructor === Object
|
|
191
277
|
}
|
|
@@ -242,7 +328,7 @@ export function mergeWith(merger, target, ...sources) {
|
|
|
242
328
|
*
|
|
243
329
|
* **Important:** Unlike Immer, which uses structural sharing via proxies for
|
|
244
330
|
* high performance, this implementation performs a full deep clone of the base
|
|
245
|
-
* state on every call using `
|
|
331
|
+
* state on every call using `clone`. This can be
|
|
246
332
|
* inefficient for large or complex states. It is intended for simple use cases
|
|
247
333
|
* where the convenience of the API outweighs the performance cost.
|
|
248
334
|
*
|
|
@@ -286,7 +372,7 @@ export function produce(baseState, recipe, ...args) {
|
|
|
286
372
|
*/
|
|
287
373
|
export function serialize(obj) {
|
|
288
374
|
function replacer(key, value) {
|
|
289
|
-
// Handle top-
|
|
375
|
+
// Handle top-stage vector
|
|
290
376
|
if (value?.__isVector__) {
|
|
291
377
|
return {
|
|
292
378
|
_type: "vector",
|
|
@@ -360,7 +446,7 @@ export function set(obj, path, value) {
|
|
|
360
446
|
* Converts an object or array to a formatted string representation.
|
|
361
447
|
*
|
|
362
448
|
* @param {*} obj - The object or array to convert.
|
|
363
|
-
* @param {number} [indentationLevel=INITIAL_LEVEL] - The current indentation
|
|
449
|
+
* @param {number} [indentationLevel=INITIAL_LEVEL] - The current indentation stage (used for nested structures).
|
|
364
450
|
* @returns {string} A string representation of the input object or array.
|
|
365
451
|
*/
|
|
366
452
|
export function toString(obj, indentationLevel = INITIAL_LEVEL) {
|
|
@@ -11,3 +11,80 @@ export function toCamelCase(input) {
|
|
|
11
11
|
|
|
12
12
|
return [firstChar.toLowerCase(), ...rest].join("")
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
// How long a row or column is by one, which is the whole of what the offsets below mean.
|
|
16
|
+
// The grid's own edges are counted by the same one.
|
|
17
|
+
const ONE = 1
|
|
18
|
+
const NONE = 0
|
|
19
|
+
const FIRST = 0
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* How far one string is from another, in single-character edits.
|
|
23
|
+
*
|
|
24
|
+
* The grid is built in full rather than in the one row that would do, because this is
|
|
25
|
+
* short and the short version is where an off-by-one hides.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```js
|
|
29
|
+
* import { editDistance } from "@inglorious/utils/data-structures/string.js"
|
|
30
|
+
*
|
|
31
|
+
* editDistance("audio", "audi") // 1
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @param {string} a - The string that was asked about.
|
|
35
|
+
* @param {string} b - The string to compare it with.
|
|
36
|
+
* @returns {number} How many edits turn one into the other.
|
|
37
|
+
*/
|
|
38
|
+
export function editDistance(a, b) {
|
|
39
|
+
const grid = Array.from({ length: a.length + ONE }, () =>
|
|
40
|
+
Array.from({ length: b.length + ONE }, () => NONE),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
for (let i = FIRST; i <= a.length; i++) grid[i][FIRST] = i
|
|
44
|
+
for (let j = FIRST; j <= b.length; j++) grid[FIRST][j] = j
|
|
45
|
+
|
|
46
|
+
for (let i = ONE; i <= a.length; i++) {
|
|
47
|
+
for (let j = ONE; j <= b.length; j++) {
|
|
48
|
+
grid[i][j] = Math.min(
|
|
49
|
+
grid[i - ONE][j] + ONE,
|
|
50
|
+
grid[i][j - ONE] + ONE,
|
|
51
|
+
grid[i - ONE][j - ONE] + (a[i - ONE] === b[j - ONE] ? NONE : ONE),
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return grid[a.length][b.length]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// How far a string may be from the one asked about and still be worth suggesting. A
|
|
60
|
+
// mistyped name is nearly always one or two edits away from the right one; anything
|
|
61
|
+
// further off is a different string rather than a wrong one.
|
|
62
|
+
const NEAR_ENOUGH = 3
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The strings closest to one that was asked for, nearest first.
|
|
66
|
+
*
|
|
67
|
+
* Case is folded, because a name differing from a declared one only by a capital is the
|
|
68
|
+
* commonest kind of mistake and the one where saying nothing is least helpful.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```js
|
|
72
|
+
* import { namesNear } from "@inglorious/utils/data-structures/string.js"
|
|
73
|
+
*
|
|
74
|
+
* namesNear("audio", ["Game", "Audio"]) // ["Audio"]
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @param {string} wanted - The string that was asked for.
|
|
78
|
+
* @param {string[]} candidates - Every string that could have been meant.
|
|
79
|
+
* @returns {string[]} The ones worth suggesting, which may be none of them.
|
|
80
|
+
*/
|
|
81
|
+
export function namesNear(wanted, candidates) {
|
|
82
|
+
const target = wanted.toLowerCase()
|
|
83
|
+
const allowed = Math.max(ONE, Math.floor(target.length / NEAR_ENOUGH))
|
|
84
|
+
|
|
85
|
+
return candidates
|
|
86
|
+
.map((name) => [name, editDistance(target, name.toLowerCase())])
|
|
87
|
+
.filter(([, d]) => d <= allowed)
|
|
88
|
+
.sort(([, a], [, b]) => a - b)
|
|
89
|
+
.map(([name]) => name)
|
|
90
|
+
}
|
package/src/functions/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./function.js"
|
package/src/math/circle.js
CHANGED
package/src/math/hitmask.js
CHANGED
package/src/math/index.js
CHANGED
|
@@ -2,16 +2,16 @@ import * as circle from "./circle.js"
|
|
|
2
2
|
import * as hitmask from "./hitmask.js"
|
|
3
3
|
import * as line from "./line.js"
|
|
4
4
|
import * as linearInterpolation from "./linear-interpolation.js"
|
|
5
|
-
import * as numbers from "./
|
|
5
|
+
import * as numbers from "./number.js"
|
|
6
6
|
import * as point from "./point.js"
|
|
7
|
-
import * as quaternions from "./
|
|
7
|
+
import * as quaternions from "./quaternion.js"
|
|
8
8
|
import * as rectangle from "./rectangle.js"
|
|
9
9
|
import * as rng from "./rng.js"
|
|
10
10
|
import * as segment from "./segment.js"
|
|
11
11
|
import * as statistics from "./statistics.js"
|
|
12
12
|
import * as triangle from "./triangle.js"
|
|
13
13
|
import * as trigonometry from "./trigonometry.js"
|
|
14
|
-
import * as
|
|
14
|
+
import * as vector from "./vector.js"
|
|
15
15
|
|
|
16
16
|
export {
|
|
17
17
|
circle,
|
|
@@ -27,5 +27,5 @@ export {
|
|
|
27
27
|
statistics,
|
|
28
28
|
triangle,
|
|
29
29
|
trigonometry,
|
|
30
|
-
|
|
30
|
+
vector,
|
|
31
31
|
}
|
package/src/math/line.js
CHANGED
package/src/math/point.js
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* A point in the Inglorious coordinate system: `[x, y, z]`.
|
|
3
|
+
*
|
|
4
|
+
* The world is y-up: the origin sits on the floor, and a bigger `y` is always a
|
|
5
|
+
* higher place in the world. Gravity subtracts from it, a jump adds to it, and an
|
|
6
|
+
* object resting on the ground has `y = 0`.
|
|
7
|
+
*
|
|
8
|
+
* `z` is the depth axis, and it is y-up as well — it is not screen-down, so both
|
|
9
|
+
* vertical axes grow upwards. Renderers turn them into screen coordinates:
|
|
10
|
+
* `@inglorious/renderer-2d` projects a point to `canvasY = height - y - z`,
|
|
11
|
+
* which puts `(0, 0, 0)` at the bottom-left of the canvas and turns both axes
|
|
12
|
+
* upside down.
|
|
13
|
+
*
|
|
14
|
+
* @example Reading a position in a 2D game
|
|
15
|
+
* ```js
|
|
16
|
+
* v(0, 0, 0) // the floor
|
|
17
|
+
* v(0, 100, 0) // a hundred pixels up
|
|
18
|
+
*
|
|
19
|
+
* entity.velocity[1] -= GRAVITY * dt // falling loses height
|
|
20
|
+
* entity.position[1] = 0 // keeping a body on the ground
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
2
23
|
* @typedef {import("../../types/math/point").Point} Point
|
|
3
24
|
* @typedef {import("../../types/math/point").PointInput} PointInput
|
|
4
25
|
* @typedef {import("../../types/math/line").Line} Line
|
|
@@ -10,8 +31,8 @@ const SQUARED = 2
|
|
|
10
31
|
const HALF = 2
|
|
11
32
|
|
|
12
33
|
import { distanceFromPoint } from "./line.js"
|
|
13
|
-
import { isBetween } from "./
|
|
14
|
-
import { isVector } from "./
|
|
34
|
+
import { isBetween } from "./number.js"
|
|
35
|
+
import { isVector } from "./vector.js"
|
|
15
36
|
|
|
16
37
|
/**
|
|
17
38
|
* Calculates the distance from a point to a line.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../../types/math/
|
|
3
|
-
* @typedef {import("../../types/math/
|
|
2
|
+
* @typedef {import("../../types/math/vector").Vector3} Vector3
|
|
3
|
+
* @typedef {import("../../types/math/quaternion").Quaternion} Quaternion
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { v } from "../v.js"
|
|
7
7
|
import { cos, sin } from "./trigonometry.js"
|
|
8
|
-
import { cross, dot, multiply, sum } from "./
|
|
8
|
+
import { cross, dot, multiply, sum } from "./vector.js"
|
|
9
9
|
|
|
10
10
|
const DEFAULT_ANGLE = 0
|
|
11
11
|
const HALF = 2 // Constant representing the divisor for halving an angle.
|
package/src/math/rectangle.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* `height` and `depth` are independent axes, not halves of one screen
|
|
3
|
+
* dimension, so a body on the `xy` plane is sized `v(w, h, 0)` and one on the
|
|
4
|
+
* `xz` plane `v(w, 0, h)`. `@inglorious/renderer-2d` flattens both onto the
|
|
5
|
+
* screen and draws a rectangle `height + depth` tall, while
|
|
6
|
+
* {@link intersectsRectangle} tests the two axes separately.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {import("../../types/math/rectangle").Size} Size
|
|
2
9
|
* @typedef {import("../../types/math/circle").Circle} Circle
|
|
3
10
|
* @typedef {import("../../types/math/point").PointInput} PointInput
|
|
4
11
|
* @typedef {import("../../types/math/rectangle").Rectangle} Rectangle
|
|
@@ -11,6 +18,11 @@ const HALF = 2
|
|
|
11
18
|
|
|
12
19
|
/**
|
|
13
20
|
* Checks if a rectangle intersects with a point.
|
|
21
|
+
*
|
|
22
|
+
* The rectangle's `position` is its center, and `size` extends around it. An
|
|
23
|
+
* anchored shape is the exception: its `anchor` picks which point sits on the
|
|
24
|
+
* position, so the two only line up when the anchor matches, such as
|
|
25
|
+
* `[0.5, 0.5]` for a centred one.
|
|
14
26
|
* @param {Rectangle} rectangle - The rectangle to check.
|
|
15
27
|
* @param {PointInput} point - The point to check.
|
|
16
28
|
* @returns {boolean} True if the point intersects the circle, false otherwise.
|
package/src/math/rng.js
CHANGED
|
@@ -31,10 +31,13 @@ export function random(...args) {
|
|
|
31
31
|
return Math.random()
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// A range with a fractional bound cannot be stepped through as integers, so it
|
|
35
|
+
// is read as a continuous range. Requiring both bounds to be floats used to
|
|
36
|
+
// send `random(0.5, 1)` down the integer path, where it could only ever answer
|
|
37
|
+
// 0 or 1.
|
|
34
38
|
if (
|
|
35
39
|
args.length > 1 &&
|
|
36
|
-
!Number.isInteger(args[0])
|
|
37
|
-
!Number.isInteger(args[1])
|
|
40
|
+
(!Number.isInteger(args[0]) || !Number.isInteger(args[1]))
|
|
38
41
|
) {
|
|
39
42
|
const [from, to] = args
|
|
40
43
|
return Math.random() * (to - from) + from
|
package/src/math/segment.js
CHANGED
package/src/math/trigonometry.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../../types/math/
|
|
3
|
-
* @typedef {import("../../types/math/
|
|
4
|
-
* @typedef {import("../../types/math/
|
|
5
|
-
* @typedef {import("../../types/math/
|
|
2
|
+
* @typedef {import("../../types/math/vector").Vector} Vector
|
|
3
|
+
* @typedef {import("../../types/math/vector").Vector2} Vector2
|
|
4
|
+
* @typedef {import("../../types/math/vector").Vector3} Vector3
|
|
5
|
+
* @typedef {import("../../types/math/vector").Vector7} Vector7
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { v } from "../v.js"
|
|
@@ -11,13 +11,32 @@ import {
|
|
|
11
11
|
clamp as nClamp,
|
|
12
12
|
mod as nMod,
|
|
13
13
|
snap as nSnap,
|
|
14
|
-
} from "./
|
|
15
|
-
import { quaternion } from "./
|
|
14
|
+
} from "./number.js"
|
|
15
|
+
import { quaternion } from "./quaternion.js"
|
|
16
|
+
import { random } from "./rng.js"
|
|
16
17
|
import { hypothenuse } from "./triangle.js"
|
|
17
18
|
import { atan2, cos, sin } from "./trigonometry.js"
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The vector with every component at zero. Useful for naming the components you
|
|
22
|
+
* care about, and for building a vector up from scratch:
|
|
23
|
+
*
|
|
24
|
+
* const [, NO_RISE, NO_DEPTH] = ZERO_VECTOR
|
|
25
|
+
* const position = v(10, 20, NO_DEPTH)
|
|
26
|
+
*
|
|
27
|
+
* Vectors are mutable arrays, so copy it rather than storing it where something
|
|
28
|
+
* writes to one, such as an entity position. Copy it with `v(...ZERO_VECTOR)`,
|
|
29
|
+
* since a plain spread drops the tag `isVector` looks for.
|
|
30
|
+
*/
|
|
31
|
+
export const ZERO_VECTOR = v(0, 0, 0) // eslint-disable-line no-magic-numbers
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The vector one unit long along the x axis. Useful for facing a direction, as
|
|
35
|
+
* in `rotate(UNIT_VECTOR, angle)`.
|
|
36
|
+
*
|
|
37
|
+
* As with `ZERO_VECTOR`, copy it with `v(...UNIT_VECTOR)` before storing it.
|
|
38
|
+
*/
|
|
39
|
+
export const UNIT_VECTOR = v(1, 0, 0) // eslint-disable-line no-magic-numbers
|
|
21
40
|
|
|
22
41
|
const X = 0
|
|
23
42
|
const Y = 1
|
|
@@ -25,6 +44,7 @@ const Z = 2
|
|
|
25
44
|
const LAST_COORDINATE = 1
|
|
26
45
|
const TWO_COORDINATES = 2
|
|
27
46
|
const NO_Y = 0
|
|
47
|
+
const NO_COMPONENT = 0
|
|
28
48
|
const DEFAULT_PRECISION = 1
|
|
29
49
|
const DEFAULT_DECIMALS = 0
|
|
30
50
|
|
|
@@ -228,6 +248,16 @@ export function multiply(...operands) {
|
|
|
228
248
|
return v(...operands.reduce(multiplyCoordinates))
|
|
229
249
|
}
|
|
230
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Flips the direction of every component, so that adding the result to a vector
|
|
253
|
+
* gives back the point mirrored through it.
|
|
254
|
+
* @param {Vector} vector - The input vector.
|
|
255
|
+
* @returns {Vector} The negated vector.
|
|
256
|
+
*/
|
|
257
|
+
export function negate(vector) {
|
|
258
|
+
return v(...vector.map((coordinate) => -coordinate))
|
|
259
|
+
}
|
|
260
|
+
|
|
231
261
|
/**
|
|
232
262
|
* Normalizes the vector to have a magnitude of 1.
|
|
233
263
|
* @param {Vector} vector - The input vector.
|
|
@@ -264,6 +294,35 @@ export function powerOf(scalar, vector) {
|
|
|
264
294
|
return v(...vector.map((coordinate) => scalar ** coordinate))
|
|
265
295
|
}
|
|
266
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Draws a random vector, taking each component from its own range: the low end is
|
|
299
|
+
* where that axis starts, the high end where it can reach. This is `random`
|
|
300
|
+
* applied per axis, so it suits ranges that differ by axis, where a single pair of
|
|
301
|
+
* bounds would have to be square.
|
|
302
|
+
*
|
|
303
|
+
* A component whose bounds are the same always comes out equal to them, and either
|
|
304
|
+
* bound may be left out to fall back to zero on that axis.
|
|
305
|
+
*
|
|
306
|
+
* @param {Vector} [low] - Where each axis starts.
|
|
307
|
+
* @param {Vector} [high] - Where each axis can reach.
|
|
308
|
+
* @returns {Vector} The random vector.
|
|
309
|
+
* @example
|
|
310
|
+
* ```js
|
|
311
|
+
* // Debris thrown to the left or right, never up or down.
|
|
312
|
+
* randomVector(v(-15, 0, 0), v(15, 0, 0))
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
export function randomVector(low, high) {
|
|
316
|
+
const [lowX, lowY, lowZ] = low ?? []
|
|
317
|
+
const [highX, highY, highZ] = high ?? []
|
|
318
|
+
|
|
319
|
+
return v(
|
|
320
|
+
random(lowX ?? NO_COMPONENT, highX ?? NO_COMPONENT),
|
|
321
|
+
random(lowY ?? NO_COMPONENT, highY ?? NO_COMPONENT),
|
|
322
|
+
random(lowZ ?? NO_COMPONENT, highZ ?? NO_COMPONENT),
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
267
326
|
/**
|
|
268
327
|
* Alias for the mod function.
|
|
269
328
|
* @type {typeof mod}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../../types/math/
|
|
2
|
+
* @typedef {import("../../types/math/vector").Vector3} Vector3
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { clamp, multiply, sum, zero } from "../math/
|
|
5
|
+
import { clamp, multiply, sum, zero } from "../math/vector.js"
|
|
6
6
|
import { applyFriction } from "./friction.js"
|
|
7
7
|
|
|
8
8
|
const DEFAULT_DT = 1 // Default time delta for the applyAcceleration function.
|
package/src/physics/friction.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../../types/math/
|
|
2
|
+
* @typedef {import("../../types/math/vector").Vector3} Vector3
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { magnitude, setMagnitude, zero } from "../math/
|
|
5
|
+
import { magnitude, setMagnitude, zero } from "../math/vector.js"
|
|
6
6
|
|
|
7
7
|
const DEFAULT_DT = 1 // Default time delta for the applyFriction function.
|
|
8
8
|
const NO_FRICTION = 0 // No friction constant.
|
package/src/physics/gravity.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../../types/math/
|
|
2
|
+
* @typedef {import("../../types/math/vector").Vector3} Vector3
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/* eslint-disable no-magic-numbers */
|
|
6
6
|
|
|
7
|
-
import { zero } from "../math/
|
|
7
|
+
import { zero } from "../math/vector.js"
|
|
8
8
|
|
|
9
9
|
const DEFAULT_DT = 1 // Default delta time for the applyGravity function.
|
|
10
10
|
const DEFAULT_JUMP = 0 // Default maximum jump height.
|
package/src/physics/velocity.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {import("../../types/math/
|
|
2
|
+
* @typedef {import("../../types/math/vector").Vector3} Vector3
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { clamp, multiply, sum, zero } from "../math/
|
|
5
|
+
import { clamp, multiply, sum, zero } from "../math/vector.js"
|
|
6
6
|
|
|
7
7
|
const DEFAULT_DT = 1 // Default time delta for the applyVelocity function.
|
|
8
8
|
const DEFAULT_MAX_SPEED = 0 // Default maximum speed for velocity clamping.
|
package/src/v.js
CHANGED
|
@@ -5,6 +5,11 @@ export type Comparator<T> = (left: T, right: T) => number
|
|
|
5
5
|
*/
|
|
6
6
|
export function contains<T>(arr: readonly T[], item: T): boolean
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* The index `step` places along a list of `length`, wrapping round at either end.
|
|
10
|
+
*/
|
|
11
|
+
export function cycleIndex(index: number, length: number, step: number): number
|
|
12
|
+
|
|
8
13
|
/**
|
|
9
14
|
* Ensures that a value is an array.
|
|
10
15
|
*/
|
|
@@ -2,7 +2,7 @@ export * as array from "./array"
|
|
|
2
2
|
export * as board from "./board"
|
|
3
3
|
export * as boolean from "./boolean"
|
|
4
4
|
export * as heap from "./heap"
|
|
5
|
-
export * as
|
|
5
|
+
export * as object from "./object"
|
|
6
6
|
export * as set from "./set"
|
|
7
7
|
export * as string from "./string"
|
|
8
8
|
export * as tree from "./tree"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Vector } from "../math/
|
|
1
|
+
import type { Vector } from "../math/vector"
|
|
2
2
|
|
|
3
3
|
export type ObjectValue<T> = T extends object ? T[keyof T] : never
|
|
4
4
|
|
|
@@ -71,9 +71,10 @@ export type DefaultsResult<T extends object, D extends object> = Omit<
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Creates a deep clone of a value.
|
|
74
|
+
* Creates a deep clone of a value. Plain data is copied by hand and vectors stay
|
|
75
|
+
* vectors; anything else goes through `structuredClone`.
|
|
75
76
|
*/
|
|
76
|
-
export function clone<T>(
|
|
77
|
+
export function clone<T>(value: T): T
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* Assigns default properties to a target in place.
|
package/types/math/index.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ export * as circle from "./circle"
|
|
|
2
2
|
export * as hitmask from "./hitmask"
|
|
3
3
|
export * as line from "./line"
|
|
4
4
|
export * as linearInterpolation from "./linear-interpolation"
|
|
5
|
-
export * as
|
|
5
|
+
export * as number from "./number"
|
|
6
6
|
export * as point from "./point"
|
|
7
|
-
export * as
|
|
7
|
+
export * as quaternion from "./quaternion"
|
|
8
8
|
export * as rectangle from "./rectangle"
|
|
9
9
|
export * as rng from "./rng"
|
|
10
10
|
export * as segment from "./segment"
|
|
11
11
|
export * as statistics from "./statistics"
|
|
12
12
|
export * as triangle from "./triangle"
|
|
13
13
|
export * as trigonometry from "./trigonometry"
|
|
14
|
-
export * as
|
|
14
|
+
export * as vector from "./vector"
|
package/types/math/point.d.ts
CHANGED
|
@@ -3,7 +3,32 @@ import type { Circle } from "./circle"
|
|
|
3
3
|
import type { Line } from "./line"
|
|
4
4
|
import type { Rectangle } from "./rectangle"
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* A point in the Inglorious coordinate system: `[x, y, z]`.
|
|
8
|
+
*
|
|
9
|
+
* The world is **y-up**: the origin sits on the floor, and a bigger `y` is
|
|
10
|
+
* always a higher place in the world. Gravity subtracts from it, a jump adds to
|
|
11
|
+
* it, and an object resting on the ground has `y = 0`.
|
|
12
|
+
*
|
|
13
|
+
* `z` is the depth axis, and it is **y-up as well** — it is not screen-down, so
|
|
14
|
+
* both vertical axes grow upwards. Renderers are what turn them into screen
|
|
15
|
+
* coordinates: `@inglorious/renderer-2d` projects a point to
|
|
16
|
+
* `canvasY = viewportHeight - y - z`, which puts `(0, 0, 0)` at the bottom-left
|
|
17
|
+
* corner of the canvas and turns both axes upside down.
|
|
18
|
+
*
|
|
19
|
+
* @example Reading a position in a 2D game
|
|
20
|
+
* ```js
|
|
21
|
+
* // Ground level, and a hundred pixels above it.
|
|
22
|
+
* v(0, 0, 0) // the floor
|
|
23
|
+
* v(0, 100, 0) // a hundred pixels up
|
|
24
|
+
*
|
|
25
|
+
* // Falling means losing height, in 2D as well as in 3D.
|
|
26
|
+
* entity.velocity[1] -= GRAVITY * dt
|
|
27
|
+
*
|
|
28
|
+
* // Keep a body on the ground.
|
|
29
|
+
* entity.position[1] = 0
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
7
32
|
export type Point = readonly [x: number, y: number, z: number]
|
|
8
33
|
|
|
9
34
|
/** Accepts a 3D point, branded vector, or object with a point position. */
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import type { Circle } from "./circle"
|
|
2
2
|
import type { Point, PointInput } from "./point"
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* The dimensions of a 3D object, as `[width, height, depth]`.
|
|
6
|
+
*
|
|
7
|
+
* `width` is the extent along `x`, `height` the extent along `y` and `depth` the
|
|
8
|
+
* extent along `z`. `height` and `depth` are **independent axes**, not halves of
|
|
9
|
+
* one screen dimension, so in a 2D game that plays on the `xy` plane a size reads
|
|
10
|
+
* as `v(w, h, 0)` — and an entity whose body is placed on the `xz` plane instead
|
|
11
|
+
* reads as `v(w, 0, h)`.
|
|
12
|
+
*
|
|
13
|
+
* When a renderer has to flatten both vertical axes onto the screen, it adds
|
|
14
|
+
* them: `@inglorious/renderer-2d` draws a rectangle `height + depth` tall. A
|
|
15
|
+
* collision shape never does — `intersectsRectangle` tests the two axes
|
|
16
|
+
* separately, which is why a body cannot collide with something that is merely
|
|
17
|
+
* "near it" on the other axis.
|
|
18
|
+
*/
|
|
5
19
|
export type Size = readonly [width: number, height: number, depth: number]
|
|
6
20
|
|
|
7
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Represents an axis-aligned rectangle.
|
|
23
|
+
*
|
|
24
|
+
* `position` is the point the box hangs off and `size` is its extent, so with
|
|
25
|
+
* the default centred anchor the position is the middle of the box. Set an
|
|
26
|
+
* `anchor` to describe the box by a different point, such as `[0, 0]` for its
|
|
27
|
+
* bottom-left corner, so that the box stands on its position.
|
|
28
|
+
*/
|
|
8
29
|
export interface Rectangle {
|
|
9
30
|
/** The center of the rectangle. */
|
|
10
31
|
position: Point
|
|
@@ -18,6 +18,22 @@ export type Vector7 = Vector<
|
|
|
18
18
|
readonly [number, number, number, number, number, number, number]
|
|
19
19
|
>
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* The vector with every component at zero.
|
|
23
|
+
*
|
|
24
|
+
* Vectors are mutable, so copy it with `v(...ZERO_VECTOR)` before storing it
|
|
25
|
+
* where something writes to one.
|
|
26
|
+
*/
|
|
27
|
+
export const ZERO_VECTOR: Vector3
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The vector one unit long along the x axis.
|
|
31
|
+
*
|
|
32
|
+
* Vectors are mutable, so copy it with `v(...UNIT_VECTOR)` before storing it
|
|
33
|
+
* where something writes to one.
|
|
34
|
+
*/
|
|
35
|
+
export const UNIT_VECTOR: Vector3
|
|
36
|
+
|
|
21
37
|
/** Returns the absolute value of each vector component. */
|
|
22
38
|
export function abs<T extends Vector>(vector: T): T
|
|
23
39
|
|
|
@@ -107,6 +123,11 @@ export function multiply<T extends Vector2 | Vector3>(
|
|
|
107
123
|
): T
|
|
108
124
|
|
|
109
125
|
/** Normalizes a vector to unit magnitude. */
|
|
126
|
+
/**
|
|
127
|
+
* Flips the direction of every component.
|
|
128
|
+
*/
|
|
129
|
+
export function negate<T extends Vector>(vector: T): T
|
|
130
|
+
|
|
110
131
|
export function normalize<T extends Vector>(vector: T): T
|
|
111
132
|
|
|
112
133
|
/** Alias for `power`. */
|
|
@@ -124,6 +145,14 @@ export function power<T extends Vector2 | Vector3>(
|
|
|
124
145
|
/** Raises a scalar to each vector component. */
|
|
125
146
|
export function powerOf<T extends Vector>(scalar: number, vector: T): T
|
|
126
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Draws a random vector, taking each component from its own range.
|
|
150
|
+
*/
|
|
151
|
+
export function randomVector(
|
|
152
|
+
low?: readonly number[],
|
|
153
|
+
high?: readonly number[],
|
|
154
|
+
): Vector3
|
|
155
|
+
|
|
127
156
|
/** Alias for `mod`. */
|
|
128
157
|
export const remainder: typeof mod
|
|
129
158
|
|
package/types/v.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|