@inglorious/utils 3.0.0 → 3.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/package.json +1 -1
- package/src/math/line.js +0 -2
- package/src/math/linear-interpolation.js +0 -1
- package/src/math/quaternion.js +1 -2
- package/src/math/quaternions.js +7 -4
- package/src/math/rectangle.js +0 -2
- package/src/math/rng.js +0 -2
- package/src/math/segment.js +0 -4
- package/src/math/triangle.js +1 -1
- package/src/math/vector.js +55 -53
- package/src/math/vector.test.js +43 -40
- package/src/math/vectors.js +18 -13
- package/src/physics/acceleration.js +2 -2
- package/src/physics/friction.js +0 -1
- package/src/physics/jump.js +0 -1
- package/src/physics/velocity.js +0 -1
- package/src/v.js +0 -1
- package/src/v.test.js +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "A set of general-purpose utility functions designed with functional programming principles in mind.",
|
|
5
5
|
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
package/src/math/line.js
CHANGED
|
@@ -9,7 +9,6 @@ import { hypothenuse } from "./triangle.js"
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Calculates the shortest distance from a point to a line in 2D space.
|
|
12
|
-
*
|
|
13
12
|
* @param {Line} line - The line represented by the equation ax + bz + c = 0, where `line` is [a, b, c].
|
|
14
13
|
* @param {Point} point - The point in 3D space represented as [x, y, z].
|
|
15
14
|
* @returns {number} The shortest distance from the point to the line.
|
|
@@ -24,7 +23,6 @@ export function distanceFromPoint(line, point) {
|
|
|
24
23
|
// @see https://math.stackexchange.com/questions/275529/check-if-line-intersects-with-circles-perimeter
|
|
25
24
|
/**
|
|
26
25
|
* Determines whether a line intersects with the perimeter of a circle.
|
|
27
|
-
*
|
|
28
26
|
* @param {Line} line - The line represented by the equation ax + bz + c = 0, where `line` is [a, b, c].
|
|
29
27
|
* @param {Circle} circle - The circle defined by its position (center) and radius.
|
|
30
28
|
* @returns {boolean} `true` if the line intersects the circle's perimeter, otherwise `false`.
|
package/src/math/quaternion.js
CHANGED
|
@@ -13,11 +13,10 @@ const Y_AXIS = v(0, 1, 0) // Default axis of rotation (Y-axis).
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Computes a quaternion representing a rotation around a given axis.
|
|
16
|
-
*
|
|
17
16
|
* @param {number} [angle=0] - The angle of rotation in radians. Defaults to 0.
|
|
18
17
|
* @param {Vector3} [axis=Y_AXIS] - The axis of rotation as a 3D vector Defaults to the Y-axis.
|
|
19
18
|
* @returns {Quaternion} The quaternion as an array [w, x, y, z].
|
|
20
19
|
*/
|
|
21
20
|
export function quaternion(angle = DEFAULT_ANGLE, axis = Y_AXIS) {
|
|
22
|
-
return
|
|
21
|
+
return v(cos(angle / HALF), ...axis.map((coord) => coord * sin(angle / HALF)))
|
|
23
22
|
}
|
package/src/math/quaternions.js
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
* @typedef {import('./types').Quaternion} Quaternion
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { v } from "../v.js"
|
|
5
6
|
import { times } from "./vector.js"
|
|
6
7
|
import { cross, dot, sum } from "./vectors.js"
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Combines two quaternions using the Hamilton product.
|
|
10
|
-
*
|
|
11
11
|
* @param {Quaternion} q1 - The first quaternion.
|
|
12
12
|
* @param {Quaternion} q2 - The second quaternion.
|
|
13
13
|
* @returns {Quaternion} - The resulting quaternion after combining q1 and q2.
|
|
14
14
|
*/
|
|
15
15
|
export function combine(q1, q2) {
|
|
16
|
-
const [
|
|
17
|
-
const [
|
|
16
|
+
const [s1, ...v1] = q1
|
|
17
|
+
const [s2, ...v2] = q2
|
|
18
18
|
|
|
19
|
-
return
|
|
19
|
+
return v(
|
|
20
|
+
s1 * s2 - dot(v1, v2),
|
|
21
|
+
...sum(times(v2, s1), times(v1, s2), cross(v1, v2)),
|
|
22
|
+
)
|
|
20
23
|
}
|
package/src/math/rectangle.js
CHANGED
|
@@ -21,7 +21,6 @@ export function intersectsPoint(rectangle, point) {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Checks if a rectangle intersects with a circle.
|
|
24
|
-
*
|
|
25
24
|
* @param {Rectangle} rectangle - The rectangle to check.
|
|
26
25
|
* @param {Circle} circle - The circle to check.
|
|
27
26
|
* @returns {boolean} True if the rectangle intersects the circle, false otherwise.
|
|
@@ -32,7 +31,6 @@ export function intersectsCircle(rectangle, circle) {
|
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
33
|
* Determines whether a rectangle intersects another in 3D space.
|
|
35
|
-
*
|
|
36
34
|
* @param {Rectangle} rectangle1 - The first rectangle, defined by its position (x, y, z) and size (width, height, depth).
|
|
37
35
|
* @param {Rectangle} rectangle2 - The second rectangle, defined by its position (x, y, z) and size (width, height, depth).
|
|
38
36
|
* @returns {boolean} True if the two rectangles intersect, false otherwise.
|
package/src/math/rng.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* - If no arguments are provided, returns a random float between 0 (inclusive) and 1 (exclusive).
|
|
6
6
|
* - If one argument is provided, returns a random integer between 0 and the given number (inclusive).
|
|
7
7
|
* - If two or more arguments are provided, returns a random integer within the specified range and step.
|
|
8
|
-
*
|
|
9
8
|
* @param {number} [to] - The upper bound (inclusive) if one argument is provided.
|
|
10
9
|
* @param {number} [from] - The lower bound (inclusive) if two arguments are provided.
|
|
11
10
|
* @param {number} [step=1] - The step size if two or more arguments are provided.
|
|
@@ -36,7 +35,6 @@ export function random(...args) {
|
|
|
36
35
|
/**
|
|
37
36
|
* Generates a random binomial value.
|
|
38
37
|
* The result is the difference between two random numbers.
|
|
39
|
-
*
|
|
40
38
|
* @returns {number} A random binomial value.
|
|
41
39
|
*/
|
|
42
40
|
export function randomBinomial() {
|
package/src/math/segment.js
CHANGED
|
@@ -12,7 +12,6 @@ const BEFORE_SEGMENT = 0
|
|
|
12
12
|
/**
|
|
13
13
|
* Calculates the coefficients [a, b, c] of the line equation ax + bz + c = 0
|
|
14
14
|
* for a given segment in 2D space.
|
|
15
|
-
*
|
|
16
15
|
* @param {Segment} segment - The segment defined by its start (`from`) and end (`to`) points.
|
|
17
16
|
* @returns {[number, number, number]} An array [a, b, c] representing the line equation.
|
|
18
17
|
*/
|
|
@@ -24,7 +23,6 @@ export function coefficients(segment) {
|
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* Finds the closest point on a segment to a given point in 3D space.
|
|
27
|
-
*
|
|
28
26
|
* @param {Segment} segment - The segment defined by its start (`from`) and end (`to`) points.
|
|
29
27
|
* @param {Point} point - The point in 3D space represented as [x, y, z].
|
|
30
28
|
* @returns {Point} The closest point on the segment to the given point.
|
|
@@ -50,7 +48,6 @@ export function closestPoint(segment, point) {
|
|
|
50
48
|
|
|
51
49
|
/**
|
|
52
50
|
* Calculates the shortest distance from a point to a segment in 3D space.
|
|
53
|
-
*
|
|
54
51
|
* @param {Segment} segment - The segment defined by its start (`from`) and end (`to`) points.
|
|
55
52
|
* @param {Point} point - The point in 3D space represented as [x, y, z].
|
|
56
53
|
* @returns {number} The shortest distance from the point to the segment.
|
|
@@ -62,7 +59,6 @@ export function distanceFromPoint(segment, point) {
|
|
|
62
59
|
|
|
63
60
|
/**
|
|
64
61
|
* Determines whether a segment intersects with the perimeter of a circle.
|
|
65
|
-
*
|
|
66
62
|
* @param {Segment} segment - The segment defined by its start (`from`) and end (`to`) points.
|
|
67
63
|
* @param {Circle} circle - The circle defined by its position (center) and radius.
|
|
68
64
|
* @returns {boolean} `true` if the segment intersects the circle's perimeter, otherwise `false`.
|
package/src/math/triangle.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Calculates the length of the hypothenuse (or magnitude) for a given set of numbers
|
|
3
3
|
* using the Pythagorean theorem.
|
|
4
|
-
*
|
|
5
4
|
* @param {number[]} nums - A list of numbers representing the sides of a right triangle.
|
|
6
5
|
* @returns {number} The length of the hypothenuse.
|
|
7
6
|
*/
|
|
@@ -11,5 +10,6 @@ export function hypothenuse(...nums) {
|
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Alias for the `hypothenuse` function.
|
|
13
|
+
* @type {typeof hypothenuse}
|
|
14
14
|
*/
|
|
15
15
|
export const pythagoras = hypothenuse
|
package/src/math/vector.js
CHANGED
|
@@ -31,17 +31,15 @@ const DEFAULT_DECIMALS = 0
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Returns the absolute value of each component in the vector.
|
|
34
|
-
*
|
|
35
34
|
* @param {Vector} vector - The input vector.
|
|
36
35
|
* @returns {Vector} The vector with absolute values.
|
|
37
36
|
*/
|
|
38
37
|
export function abs(vector) {
|
|
39
|
-
return vector.map(nAbs)
|
|
38
|
+
return v(...vector.map(nAbs))
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
42
|
* Calculates the angle of the vector in radians.
|
|
44
|
-
*
|
|
45
43
|
* @param {Vector} vector - The input vector.
|
|
46
44
|
* @returns {number} The angle of the vector in the XZ plane (for 3D) or XY plane (for 2D), in radians.
|
|
47
45
|
*/
|
|
@@ -51,7 +49,6 @@ export function angle(vector) {
|
|
|
51
49
|
|
|
52
50
|
/**
|
|
53
51
|
* Clamps the magnitude of the vector between the given min and max values.
|
|
54
|
-
*
|
|
55
52
|
* @param {Vector} vector - The input vector.
|
|
56
53
|
* @param {number|Vector} min - The minimum magnitude or a vector representing the lower bounds for each component.
|
|
57
54
|
* @param {number|Vector} max - The maximum magnitude or a vector representing the upper bounds for each component.
|
|
@@ -69,8 +66,10 @@ export function clamp(vector, min, max) {
|
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
if (typeof min !== "number" && typeof max !== "number") {
|
|
72
|
-
return
|
|
73
|
-
|
|
69
|
+
return v(
|
|
70
|
+
...vector.map((coordinate, index) =>
|
|
71
|
+
nClamp(coordinate, min[index], max[index]),
|
|
72
|
+
),
|
|
74
73
|
)
|
|
75
74
|
}
|
|
76
75
|
|
|
@@ -79,17 +78,17 @@ export function clamp(vector, min, max) {
|
|
|
79
78
|
|
|
80
79
|
/**
|
|
81
80
|
* Returns the conjugate of the vector.
|
|
82
|
-
*
|
|
83
81
|
* @param {Vector} vector - The input vector.
|
|
84
82
|
* @returns {Vector} The conjugated vector.
|
|
85
83
|
*/
|
|
86
84
|
export function conjugate(vector) {
|
|
87
|
-
return
|
|
85
|
+
return v(
|
|
86
|
+
...vector.map((coordinate, index) => (index ? -coordinate : coordinate)),
|
|
87
|
+
)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Creates a 3D vector with the given magnitude and angle.
|
|
92
|
-
*
|
|
93
92
|
* @param {number} magnitude - The magnitude of the vector.
|
|
94
93
|
* @param {number} angle - The angle of the vector in radians.
|
|
95
94
|
* @returns {Vector3} The created 3D vector.
|
|
@@ -100,24 +99,22 @@ export function createVector(magnitude, angle) {
|
|
|
100
99
|
|
|
101
100
|
/**
|
|
102
101
|
* Divides each component of the vector by the given scalar.
|
|
103
|
-
*
|
|
104
102
|
* @param {Vector} vector - The input vector.
|
|
105
103
|
* @param {number} scalar - The scalar value.
|
|
106
104
|
* @returns {Vector} The resulting vector.
|
|
107
105
|
*/
|
|
108
106
|
export function divide(vector, scalar) {
|
|
109
|
-
return vector.map((coordinate) => coordinate / scalar)
|
|
107
|
+
return v(...vector.map((coordinate) => coordinate / scalar))
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
/**
|
|
113
111
|
* Converts a 2D vector [x, z] into a 3D vector [x, 0, z].
|
|
114
|
-
*
|
|
115
112
|
* @param {Vector2} vector - A 2D vector represented as [x, z].
|
|
116
113
|
* @returns {Vector3} A 3D vector represented as [x, 0, z].
|
|
117
114
|
*/
|
|
118
115
|
export function from2D(vector) {
|
|
119
116
|
const [x, z] = vector
|
|
120
|
-
return
|
|
117
|
+
return v(x, NO_Y, z)
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
/**
|
|
@@ -133,7 +130,6 @@ export function fromAngle(angle) {
|
|
|
133
130
|
* Checks if a value is a vector.
|
|
134
131
|
* A vector is an array of numbers. For performance, vectors created with `v()`
|
|
135
132
|
* are tagged and can be identified more quickly.
|
|
136
|
-
*
|
|
137
133
|
* @param {*} value - The value to check.
|
|
138
134
|
* @returns {boolean} True if the value is a vector, false otherwise.
|
|
139
135
|
*/
|
|
@@ -147,8 +143,6 @@ export function isVector(value) {
|
|
|
147
143
|
|
|
148
144
|
/**
|
|
149
145
|
* Calculates the magnitude (length) of the vector.
|
|
150
|
-
* Alias: `length`.
|
|
151
|
-
*
|
|
152
146
|
* @param {Vector} vector - The input vector.
|
|
153
147
|
* @returns {number} The magnitude of the vector.
|
|
154
148
|
*/
|
|
@@ -160,49 +154,71 @@ export const length = magnitude
|
|
|
160
154
|
|
|
161
155
|
/**
|
|
162
156
|
* Calculates the modulus of each component in the vector with the given divisor.
|
|
163
|
-
* Alias: `remainder`.
|
|
164
|
-
*
|
|
165
157
|
* @param {Vector} vector - The input vector.
|
|
166
158
|
* @param {number} divisor - The divisor value.
|
|
167
159
|
* @returns {Vector} The resulting vector.
|
|
168
160
|
*/
|
|
169
161
|
export function mod(vector, divisor) {
|
|
170
|
-
return vector.map((coordinate) => nMod(coordinate, divisor))
|
|
162
|
+
return v(...vector.map((coordinate) => nMod(coordinate, divisor)))
|
|
171
163
|
}
|
|
172
164
|
|
|
173
165
|
/**
|
|
174
166
|
* Multiplies each component of the vector by the given scalar.
|
|
175
|
-
* Aliases: `scale`, `times`.
|
|
176
|
-
*
|
|
177
167
|
* @param {Vector} vector - The input vector.
|
|
178
168
|
* @param {number} scalar - The scalar value.
|
|
179
169
|
* @returns {Vector} The resulting vector.
|
|
180
170
|
*/
|
|
181
171
|
export function multiply(vector, scalar) {
|
|
182
|
-
return vector.map((coordinate) => coordinate * scalar)
|
|
172
|
+
return v(...vector.map((coordinate) => coordinate * scalar))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Alias for the power function.
|
|
177
|
+
* @type {typeof power}
|
|
178
|
+
*/
|
|
179
|
+
export const pow = power
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Raises each component of the vector to the given exponent.
|
|
183
|
+
* @param {Vector} vector - The input vector.
|
|
184
|
+
* @param {number} exponent - The exponent value.
|
|
185
|
+
* @returns {Vector} The resulting vector.
|
|
186
|
+
*/
|
|
187
|
+
export function power(vector, exponent) {
|
|
188
|
+
return v(...vector.map((coordinate) => coordinate ** exponent))
|
|
183
189
|
}
|
|
184
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Alias for the multiply function.
|
|
193
|
+
* @type {typeof multiply}
|
|
194
|
+
*/
|
|
185
195
|
export const scale = multiply
|
|
186
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Alias for the multiply function.
|
|
199
|
+
* @type {typeof multiply}
|
|
200
|
+
*/
|
|
187
201
|
export const times = multiply
|
|
188
202
|
|
|
189
203
|
/**
|
|
190
204
|
* Normalizes the vector to have a magnitude of 1.
|
|
191
|
-
*
|
|
192
205
|
* @param {Vector} vector - The input vector.
|
|
193
206
|
* @returns {Vector} The normalized vector.
|
|
194
207
|
*/
|
|
195
208
|
export function normalize(vector) {
|
|
196
209
|
const length = magnitude(vector)
|
|
197
|
-
return vector.map((coordinate) => coordinate / length)
|
|
210
|
+
return v(...vector.map((coordinate) => coordinate / length))
|
|
198
211
|
}
|
|
199
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Alias for the mod function.
|
|
215
|
+
* @type {typeof mod}
|
|
216
|
+
*/
|
|
200
217
|
export const remainder = mod
|
|
201
218
|
|
|
202
219
|
/**
|
|
203
220
|
* Rotates the vector by the given angle. Handles 2D and 3D vectors.
|
|
204
221
|
* For 3D vectors, rotation is around the Y-axis.
|
|
205
|
-
*
|
|
206
222
|
* @param {Vector} vector - The input vector (2D or 3D).
|
|
207
223
|
* @param {number} angle - The angle in radians.
|
|
208
224
|
* @returns {Vector} The rotated vector.
|
|
@@ -219,7 +235,6 @@ export function rotate(vector, angle) {
|
|
|
219
235
|
|
|
220
236
|
/**
|
|
221
237
|
* Sets the angle of the vector in the XZ plane while maintaining its magnitude.
|
|
222
|
-
*
|
|
223
238
|
* @param {Vector} vector - The input vector.
|
|
224
239
|
* @param {number} angle - The new angle in radians.
|
|
225
240
|
* @returns {Vector3} The vector with the updated angle.
|
|
@@ -227,19 +242,17 @@ export function rotate(vector, angle) {
|
|
|
227
242
|
export function setAngle(vector, angle) {
|
|
228
243
|
const length = magnitude(vector)
|
|
229
244
|
const [x, z] = toCartesian([length, angle])
|
|
230
|
-
return
|
|
245
|
+
return v(x, NO_Y, z)
|
|
231
246
|
}
|
|
232
247
|
|
|
233
248
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
249
|
+
* Alias for the setMagnitude function.
|
|
250
|
+
* @type {typeof setMagnitude}
|
|
236
251
|
*/
|
|
237
252
|
export const setLength = setMagnitude
|
|
238
253
|
|
|
239
254
|
/**
|
|
240
255
|
* Sets the magnitude of the vector while maintaining its direction.
|
|
241
|
-
* Alias: `setLength`.
|
|
242
|
-
*
|
|
243
256
|
* @param {Vector} vector - The input vector.
|
|
244
257
|
* @param {number} length - The new magnitude.
|
|
245
258
|
* @returns {Vector} The vector with the updated magnitude.
|
|
@@ -251,72 +264,67 @@ export function setMagnitude(vector, length) {
|
|
|
251
264
|
|
|
252
265
|
/**
|
|
253
266
|
* Shifts the components of the vector by the given index.
|
|
254
|
-
*
|
|
255
267
|
* @param {Vector} vector - The input vector.
|
|
256
268
|
* @param {number} index - The index to shift by.
|
|
257
269
|
* @returns {Vector} The shifted vector.
|
|
258
270
|
*/
|
|
259
271
|
export function shift(vector, index) {
|
|
260
|
-
return
|
|
272
|
+
return v(...vector.slice(index), ...vector.slice(X, index))
|
|
261
273
|
}
|
|
262
274
|
|
|
263
275
|
/**
|
|
264
276
|
* Snaps each component of the vector to the nearest multiple of the given precision.
|
|
265
|
-
*
|
|
266
277
|
* @param {Vector} vector - The input vector.
|
|
267
278
|
* @param {number} [precision=DEFAULT_PRECISION] - The precision value.
|
|
268
279
|
* @returns {Vector} The snapped vector.
|
|
269
280
|
*/
|
|
270
281
|
export function snap(vector, precision = DEFAULT_PRECISION) {
|
|
271
|
-
return
|
|
282
|
+
return v(
|
|
283
|
+
...vector.map((coordinate) => nSnap(coordinate, -precision, precision)),
|
|
284
|
+
)
|
|
272
285
|
}
|
|
273
286
|
|
|
274
287
|
/**
|
|
275
288
|
* Converts a 3D vector [x, y, z] into a 2D vector [x, z].
|
|
276
|
-
*
|
|
277
289
|
* @param {Vector3} vector - A 3D vector represented as [x, y, z].
|
|
278
290
|
* @returns {Vector2} A 2D vector represented as [x, z].
|
|
279
291
|
*/
|
|
280
292
|
export function to2D(vector) {
|
|
281
293
|
const [x, , z] = vector
|
|
282
|
-
return
|
|
294
|
+
return v(x, z)
|
|
283
295
|
}
|
|
284
296
|
|
|
285
297
|
/**
|
|
286
298
|
* Converts 2D polar coordinates to 2D Cartesian coordinates.
|
|
287
|
-
*
|
|
288
299
|
* @param {Vector2} vector - The polar coordinates [magnitude, angle].
|
|
289
300
|
* @returns {Vector2} The Cartesian coordinates [x, y].
|
|
290
301
|
*/
|
|
291
302
|
export function toCartesian([magnitude, angle]) {
|
|
292
|
-
return
|
|
303
|
+
return v(magnitude * cos(angle), magnitude * sin(angle))
|
|
293
304
|
}
|
|
294
305
|
|
|
295
306
|
/**
|
|
296
307
|
* Converts a 3D cartesian vector to cylindrical coordinates.
|
|
297
|
-
*
|
|
298
308
|
* @param {Vector3} vector - The input vector [x, y, z].
|
|
299
309
|
* @returns {Vector3} The cylindrical coordinates [radius, theta, z].
|
|
300
310
|
*/
|
|
301
311
|
export function toCylindrical(vector) {
|
|
302
312
|
const radius = magnitude(vector)
|
|
303
313
|
const theta = angle(vector)
|
|
304
|
-
return
|
|
314
|
+
return v(radius * cos(theta), radius * sin(theta), vector[Z])
|
|
305
315
|
}
|
|
306
316
|
|
|
307
317
|
/**
|
|
308
318
|
* Converts a 2D cartesian vector to 2D polar coordinates.
|
|
309
|
-
*
|
|
310
319
|
* @param {Vector2} vector - The input vector [x, y].
|
|
311
320
|
* @returns {Vector2} The polar coordinates [magnitude, angle].
|
|
312
321
|
*/
|
|
313
322
|
export function toPolar(vector) {
|
|
314
|
-
return
|
|
323
|
+
return v(magnitude(vector), angle(vector))
|
|
315
324
|
}
|
|
316
325
|
|
|
317
326
|
/**
|
|
318
327
|
* Converts a vector to a string representation.
|
|
319
|
-
*
|
|
320
328
|
* @param {Vector} vector - The input vector.
|
|
321
329
|
* @param {number} [decimals=DEFAULT_DECIMALS] - The number of decimal places.
|
|
322
330
|
* @returns {string} The string representation of the vector.
|
|
@@ -327,14 +335,11 @@ export function toString(vector, decimals = DEFAULT_DECIMALS) {
|
|
|
327
335
|
.join(", ")}]`
|
|
328
336
|
}
|
|
329
337
|
|
|
330
|
-
// TODO: add toSpherical(vector),
|
|
331
|
-
|
|
332
338
|
/**
|
|
333
339
|
* Converts a 3D cartesian vector to spherical coordinates [radius, inclination, azimuth].
|
|
334
340
|
* - radius (r): distance from the origin.
|
|
335
341
|
* - inclination (θ): angle from the Y-axis (0 to PI).
|
|
336
342
|
* - azimuth (φ): angle from the X-axis in the XZ-plane (-PI to PI).
|
|
337
|
-
*
|
|
338
343
|
* @param {Vector3} vector The cartesian vector [x, y, z].
|
|
339
344
|
* @returns {Vector3} The spherical coordinates [r, θ, φ].
|
|
340
345
|
*/
|
|
@@ -350,18 +355,17 @@ export function toSpherical(vector) {
|
|
|
350
355
|
// Azimuth is the angle in the XZ plane, which `angle()` calculates.
|
|
351
356
|
const azimuth = angle(vector)
|
|
352
357
|
|
|
353
|
-
return
|
|
358
|
+
return v(r, inclination, azimuth)
|
|
354
359
|
}
|
|
355
360
|
|
|
356
361
|
/**
|
|
357
362
|
* Creates a 3D unit vector in the XZ plane with the given angle.
|
|
358
|
-
*
|
|
359
363
|
* @param {number} [angle=0] - The angle in radians.
|
|
360
364
|
* @returns {Vector3} The unit vector.
|
|
361
365
|
*/
|
|
362
366
|
export function unit(angle) {
|
|
363
367
|
if (!angle) {
|
|
364
|
-
return
|
|
368
|
+
return v(...UNIT_VECTOR)
|
|
365
369
|
}
|
|
366
370
|
|
|
367
371
|
return setAngle(UNIT_VECTOR, angle)
|
|
@@ -369,16 +373,14 @@ export function unit(angle) {
|
|
|
369
373
|
|
|
370
374
|
/**
|
|
371
375
|
* Creates a zero vector of 3 dimensions.
|
|
372
|
-
*
|
|
373
376
|
* @returns {Vector3} The zero vector.
|
|
374
377
|
*/
|
|
375
378
|
export function zero() {
|
|
376
|
-
return
|
|
379
|
+
return v(...ZERO_VECTOR)
|
|
377
380
|
}
|
|
378
381
|
|
|
379
382
|
/**
|
|
380
383
|
* Rotates a 3D vector around the Y-axis using a quaternion.
|
|
381
|
-
*
|
|
382
384
|
* @param {Vector3} vector - The input vector.
|
|
383
385
|
* @param {number} angle - The angle in radians.
|
|
384
386
|
* @returns {Vector3} The rotated vector.
|
package/src/math/vector.test.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
mod,
|
|
18
18
|
multiply,
|
|
19
19
|
normalize,
|
|
20
|
+
power,
|
|
20
21
|
rotate,
|
|
21
22
|
setAngle,
|
|
22
23
|
setMagnitude,
|
|
@@ -40,7 +41,7 @@ test("it should compute the absolute value of a vector's coordinates", () => {
|
|
|
40
41
|
})
|
|
41
42
|
|
|
42
43
|
test("it should compute the angle of a 2D vector", () => {
|
|
43
|
-
const vector =
|
|
44
|
+
const vector = v(1, 1)
|
|
44
45
|
const expectedResult = pi() / 4
|
|
45
46
|
|
|
46
47
|
expect(angle(vector)).toBe(expectedResult)
|
|
@@ -54,28 +55,28 @@ test("it should compute the angle of a 3D vector", () => {
|
|
|
54
55
|
})
|
|
55
56
|
|
|
56
57
|
test("it should clamp the magnitude of a vector to a certain length if too long", () => {
|
|
57
|
-
const vector =
|
|
58
|
+
const vector = v(6, 8)
|
|
58
59
|
const min = 0
|
|
59
60
|
const max = 5
|
|
60
|
-
const expectedResult =
|
|
61
|
+
const expectedResult = v(3, 4)
|
|
61
62
|
|
|
62
63
|
expect(clamp(vector, min, max)).toStrictEqual(expectedResult)
|
|
63
64
|
})
|
|
64
65
|
|
|
65
66
|
test("it should not clamp the magnitude of a vector to a certain length if not too long", () => {
|
|
66
|
-
const vector =
|
|
67
|
+
const vector = v(3, 4)
|
|
67
68
|
const min = 0
|
|
68
69
|
const max = 6
|
|
69
|
-
const expectedResult =
|
|
70
|
+
const expectedResult = v(3, 4)
|
|
70
71
|
|
|
71
72
|
expect(clamp(vector, min, max)).toStrictEqual(expectedResult)
|
|
72
73
|
})
|
|
73
74
|
|
|
74
75
|
test("it should clamp the magnitude of a vector to the values of other vector bounds", () => {
|
|
75
|
-
const vector =
|
|
76
|
+
const vector = v(6, 8)
|
|
76
77
|
const lowerBound = [0, 0]
|
|
77
78
|
const upperBound = [3, 4]
|
|
78
|
-
const expectedResult =
|
|
79
|
+
const expectedResult = v(3, 4)
|
|
79
80
|
|
|
80
81
|
expect(clamp(vector, lowerBound, upperBound)).toStrictEqual(expectedResult)
|
|
81
82
|
})
|
|
@@ -104,7 +105,7 @@ test("it should divide a vector by a scalar", () => {
|
|
|
104
105
|
})
|
|
105
106
|
|
|
106
107
|
test("it should build a 3D vector from a 2D one", () => {
|
|
107
|
-
const vector =
|
|
108
|
+
const vector = v(3, 4)
|
|
108
109
|
const expectedResult = v(3, 0, 4)
|
|
109
110
|
|
|
110
111
|
expect(from2D(vector)).toStrictEqual(expectedResult)
|
|
@@ -118,14 +119,14 @@ test("it should create a 3D unit vector from an angle", () => {
|
|
|
118
119
|
})
|
|
119
120
|
|
|
120
121
|
test("it should compute the magnitude of a vector (aka length)", () => {
|
|
121
|
-
const vector =
|
|
122
|
+
const vector = v(3, 4)
|
|
122
123
|
const expectedResult = 5
|
|
123
124
|
|
|
124
125
|
expect(magnitude(vector)).toBe(expectedResult)
|
|
125
126
|
})
|
|
126
127
|
|
|
127
128
|
test("it should check if a value is a vector", () => {
|
|
128
|
-
expect(isVector(
|
|
129
|
+
expect(isVector([1, 2, 3])).toBe(true)
|
|
129
130
|
expect(isVector([])).toBe(true)
|
|
130
131
|
expect(isVector(v(1, 2, 3))).toBe(true)
|
|
131
132
|
})
|
|
@@ -155,23 +156,31 @@ test("it should multiply a vector with a scalar (aka times)", () => {
|
|
|
155
156
|
})
|
|
156
157
|
|
|
157
158
|
test("it should normalize a vector so it has unit length", () => {
|
|
158
|
-
const vector =
|
|
159
|
-
const expectedResult =
|
|
159
|
+
const vector = v(3, 4)
|
|
160
|
+
const expectedResult = v(0.6, 0.8)
|
|
160
161
|
|
|
161
162
|
expect(normalize(vector)).toStrictEqual(expectedResult)
|
|
162
163
|
})
|
|
163
164
|
|
|
164
165
|
test("it should normalize a negative vector", () => {
|
|
165
|
-
const vector =
|
|
166
|
-
const expectedResult =
|
|
166
|
+
const vector = v(-3, -4)
|
|
167
|
+
const expectedResult = v(-0.6, -0.8)
|
|
167
168
|
|
|
168
169
|
expect(normalize(vector)).toStrictEqual(expectedResult)
|
|
169
170
|
})
|
|
170
171
|
|
|
172
|
+
test("it should raise each component of a vector to the given exponent (aka pow)", () => {
|
|
173
|
+
const vector = v(1, 2, 3)
|
|
174
|
+
const exponent = 2
|
|
175
|
+
const expectedResult = v(1, 4, 9)
|
|
176
|
+
|
|
177
|
+
expect(power(vector, exponent)).toStrictEqual(expectedResult)
|
|
178
|
+
})
|
|
179
|
+
|
|
171
180
|
test("it should rotate a 2D vector by a certain angle", () => {
|
|
172
|
-
const vector =
|
|
181
|
+
const vector = v(1, 0)
|
|
173
182
|
const angle = pi() / 4
|
|
174
|
-
const expectedResult =
|
|
183
|
+
const expectedResult = v(0.7071067811865475, 0.7071067811865476) // close to [cos(pi/4), sin(pi/4)]
|
|
175
184
|
|
|
176
185
|
expect(rotate(vector, angle)).toStrictEqual(expectedResult)
|
|
177
186
|
})
|
|
@@ -201,32 +210,32 @@ test("it should rotate a vector that faces left by a certain angle", () => {
|
|
|
201
210
|
})
|
|
202
211
|
|
|
203
212
|
test("it should change the angle of a vector", () => {
|
|
204
|
-
const vector =
|
|
205
|
-
const expectedResult =
|
|
213
|
+
const vector = v(cos(pi() / 4), 0, sin(pi() / 4))
|
|
214
|
+
const expectedResult = v(6.123233995736766e-17, 0, 1) // close to v(0, 0, 1)
|
|
206
215
|
|
|
207
216
|
expect(setAngle(vector, 1.5707963267948966)).toStrictEqual(expectedResult)
|
|
208
217
|
})
|
|
209
218
|
|
|
210
219
|
test("it should change magnitude of a vector (aka setLength)", () => {
|
|
211
|
-
const vector =
|
|
220
|
+
const vector = v(3, 4)
|
|
212
221
|
const magnitude = 10
|
|
213
|
-
const expectedResult =
|
|
222
|
+
const expectedResult = v(6, 8)
|
|
214
223
|
|
|
215
224
|
expect(setMagnitude(vector, magnitude)).toStrictEqual(expectedResult)
|
|
216
225
|
})
|
|
217
226
|
|
|
218
227
|
test("it should change magnitude of a negative vector", () => {
|
|
219
|
-
const vector =
|
|
228
|
+
const vector = v(-3, -4)
|
|
220
229
|
const magnitude = 10
|
|
221
|
-
const expectedResult =
|
|
230
|
+
const expectedResult = v(-6, -8)
|
|
222
231
|
|
|
223
232
|
expect(setMagnitude(vector, magnitude)).toStrictEqual(expectedResult)
|
|
224
233
|
})
|
|
225
234
|
|
|
226
235
|
test("it should shift a vector at a certain index", () => {
|
|
227
|
-
const vector =
|
|
236
|
+
const vector = v(1, 2, 3, 4, 5)
|
|
228
237
|
const index = 2
|
|
229
|
-
const expectedResult =
|
|
238
|
+
const expectedResult = v(3, 4, 5, 1, 2)
|
|
230
239
|
|
|
231
240
|
expect(shift(vector, index)).toStrictEqual(expectedResult)
|
|
232
241
|
})
|
|
@@ -241,14 +250,14 @@ test("it should snap a floating vector to a certain precision", () => {
|
|
|
241
250
|
|
|
242
251
|
test("it should build a 2D vector from a 3D one", () => {
|
|
243
252
|
const vector = v(3, 0, 4)
|
|
244
|
-
const expectedResult =
|
|
253
|
+
const expectedResult = v(3, 4)
|
|
245
254
|
|
|
246
255
|
expect(to2D(vector)).toStrictEqual(expectedResult)
|
|
247
256
|
})
|
|
248
257
|
|
|
249
258
|
test("it should convert a 2D polar vector to cartesian coordinates", () => {
|
|
250
|
-
const vector =
|
|
251
|
-
const expectedResult =
|
|
259
|
+
const vector = v(sqrt(2), pi() / 4)
|
|
260
|
+
const expectedResult = v(1.0000000000000002, 1)
|
|
252
261
|
|
|
253
262
|
expect(toCartesian(vector)).toStrictEqual(expectedResult)
|
|
254
263
|
})
|
|
@@ -261,28 +270,28 @@ test("it should convert a 3D cartesian vector to cylindrical coordinates", () =>
|
|
|
261
270
|
})
|
|
262
271
|
|
|
263
272
|
test("it should convert a 2D cartesian vector to polar coordinates", () => {
|
|
264
|
-
const vector =
|
|
265
|
-
const expectedResult =
|
|
273
|
+
const vector = v(1, 1)
|
|
274
|
+
const expectedResult = v(sqrt(2), pi() / 4)
|
|
266
275
|
|
|
267
276
|
expect(toPolar(vector)).toStrictEqual(expectedResult)
|
|
268
277
|
})
|
|
269
278
|
|
|
270
279
|
test("it should convert a 3D cartesian vector to spherical coordinates", () => {
|
|
271
280
|
const vector = v(1, 1, 1)
|
|
272
|
-
const expectedResult =
|
|
281
|
+
const expectedResult = v(sqrt(3), Math.acos(1 / sqrt(3)), pi() / 4)
|
|
273
282
|
|
|
274
283
|
expect(toSpherical(vector)).toStrictEqual(expectedResult)
|
|
275
284
|
})
|
|
276
285
|
|
|
277
286
|
test("it should create a string representation of an integer vector", () => {
|
|
278
|
-
const vector =
|
|
287
|
+
const vector = v(3, 4)
|
|
279
288
|
const expectedResult = "[3, 4]"
|
|
280
289
|
|
|
281
290
|
expect(toString(vector)).toBe(expectedResult)
|
|
282
291
|
})
|
|
283
292
|
|
|
284
293
|
test("it should create a string representation of a float vector", () => {
|
|
285
|
-
const vector =
|
|
294
|
+
const vector = v(pi() / 4, pi() / 4)
|
|
286
295
|
const decimals = 2
|
|
287
296
|
const expectedResult = "[0.79, 0.79]"
|
|
288
297
|
|
|
@@ -294,21 +303,15 @@ test("it should create a unit vector oriented on the X-axis", () => {
|
|
|
294
303
|
})
|
|
295
304
|
|
|
296
305
|
test("it should create a unit vector oriented on the Z-axis", () => {
|
|
297
|
-
expect(unit(pi() / 2)).toStrictEqual(
|
|
306
|
+
expect(unit(pi() / 2)).toStrictEqual(v(6.123233995736766e-17, 0, 1)) // close to v(0, 0, 1)
|
|
298
307
|
})
|
|
299
308
|
|
|
300
309
|
test("it should create a vector from a list of coordinates", () => {
|
|
301
|
-
expect(v(1, 2, 3)).toStrictEqual(
|
|
310
|
+
expect(v(1, 2, 3)).toStrictEqual([1, 2, 3])
|
|
302
311
|
expect(v()).toStrictEqual([])
|
|
303
312
|
expect(v(0, -5.5)).toStrictEqual([0, -5.5])
|
|
304
313
|
})
|
|
305
314
|
|
|
306
|
-
test("it should create a vector with a __isVector__ property", () => {
|
|
307
|
-
const vector = v(1, 2, 3)
|
|
308
|
-
expect(vector).toStrictEqual(v(1, 2, 3))
|
|
309
|
-
expect(vector.__isVector__).toBe(true)
|
|
310
|
-
})
|
|
311
|
-
|
|
312
315
|
test("it should create a 3D zero vector", () => {
|
|
313
316
|
const expectedResult = v(0, 0, 0)
|
|
314
317
|
|
package/src/math/vectors.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @typedef {import('./types').Vector7} Vector7
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { v } from "../v.js"
|
|
7
8
|
import { magnitude, shift } from "./vector.js"
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -18,7 +19,7 @@ export const add = sum
|
|
|
18
19
|
* @returns {Vector3 | Vector7} The resulting vector after the cross product.
|
|
19
20
|
*/
|
|
20
21
|
export function cross(...vectors) {
|
|
21
|
-
return vectors.reduce(crossMultiplyCoordinates)
|
|
22
|
+
return v(...vectors.reduce(crossMultiplyCoordinates))
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -53,7 +54,7 @@ export const scalarProduct = dot
|
|
|
53
54
|
* @returns {Vector2 | Vector3} The resulting vector after subtraction.
|
|
54
55
|
*/
|
|
55
56
|
export function subtract(...vectors) {
|
|
56
|
-
return vectors.reduce(subtractCoordinates)
|
|
57
|
+
return v(...vectors.reduce(subtractCoordinates))
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/**
|
|
@@ -62,7 +63,7 @@ export function subtract(...vectors) {
|
|
|
62
63
|
* @returns {Vector2 | Vector3} The resulting vector after summation.
|
|
63
64
|
*/
|
|
64
65
|
export function sum(...vectors) {
|
|
65
|
-
return vectors.reduce(sumCoordinates)
|
|
66
|
+
return v(...vectors.reduce(sumCoordinates))
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
/**
|
|
@@ -78,7 +79,7 @@ export const vectorProduct = cross
|
|
|
78
79
|
* @returns {Vector2 | Vector3} The resulting vector after addition.
|
|
79
80
|
*/
|
|
80
81
|
function sumCoordinates(vector1, vector2) {
|
|
81
|
-
return vector1.map((coordinate, index) => coordinate + vector2[index])
|
|
82
|
+
return v(...vector1.map((coordinate, index) => coordinate + vector2[index]))
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/**
|
|
@@ -88,7 +89,7 @@ function sumCoordinates(vector1, vector2) {
|
|
|
88
89
|
* @returns {Vector2 | Vector3} The resulting vector after subtraction.
|
|
89
90
|
*/
|
|
90
91
|
function subtractCoordinates(vector1, vector2) {
|
|
91
|
-
return vector1.map((coordinate, index) => coordinate - vector2[index])
|
|
92
|
+
return v(...vector1.map((coordinate, index) => coordinate - vector2[index]))
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
/**
|
|
@@ -102,13 +103,17 @@ function crossMultiplyCoordinates(vector1, vector2) {
|
|
|
102
103
|
.fill(null)
|
|
103
104
|
.map((_, index) => index)
|
|
104
105
|
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
return v(
|
|
107
|
+
...indexes.map((_, index) => {
|
|
108
|
+
const [index1, index2] = shift(
|
|
109
|
+
indexes.filter((_, i) => i !== index),
|
|
110
|
+
index,
|
|
111
|
+
)
|
|
112
|
+
return (
|
|
113
|
+
vector1[index1] * vector2[index2] - vector1[index2] * vector2[index1]
|
|
114
|
+
)
|
|
115
|
+
}),
|
|
116
|
+
)
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
/**
|
|
@@ -118,5 +123,5 @@ function crossMultiplyCoordinates(vector1, vector2) {
|
|
|
118
123
|
* @returns {Vector2 | Vector3} The resulting vector containing multiplied coordinates.
|
|
119
124
|
*/
|
|
120
125
|
function dotMultiplyCoordinates(vector1, vector2) {
|
|
121
|
-
return vector1.map((coordinate, index) => coordinate * vector2[index])
|
|
126
|
+
return v(...vector1.map((coordinate, index) => coordinate * vector2[index]))
|
|
122
127
|
}
|
|
@@ -14,8 +14,8 @@ const HALF_ACCELERATION = 0.5 // Half of the acceleration factor used in positio
|
|
|
14
14
|
* Applies acceleration to an object using Euler's integration method.
|
|
15
15
|
*
|
|
16
16
|
* Euler's Integration:
|
|
17
|
-
* v += a * dt
|
|
18
|
-
* p += v * dt + 1/2 * a * dt * dt
|
|
17
|
+
* 1. v += a * dt
|
|
18
|
+
* 2. p += v * dt + 1/2 * a * dt * dt
|
|
19
19
|
*
|
|
20
20
|
* @param {Object} params - The parameters for the function.
|
|
21
21
|
* @param {number} params.maxAcceleration - The maximum allowed acceleration.
|
package/src/physics/friction.js
CHANGED
|
@@ -9,7 +9,6 @@ const NO_FRICTION = 0 // No friction constant.
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Applies friction to a given velocity vector.
|
|
12
|
-
*
|
|
13
12
|
* @param {Object} params - The parameters for the function.
|
|
14
13
|
* @param {Vector3} params.velocity - The velocity vector. Defaults to a zero vector.
|
|
15
14
|
* @param {number} params.friction - The friction coefficient. Defaults to 0.
|
package/src/physics/jump.js
CHANGED
|
@@ -11,7 +11,6 @@ const DEFAULT_MAX_SPEED = 0 // Default maximum speed.
|
|
|
11
11
|
/**
|
|
12
12
|
* Calculates the vertical velocity (vy) of a jump
|
|
13
13
|
* based on the provided parameters.
|
|
14
|
-
*
|
|
15
14
|
* @param {Object} params - Parameters for the jump calculation.
|
|
16
15
|
* @param {number} params.maxJump - Maximum jump height. Defaults to 0.
|
|
17
16
|
* @param {number} params.maxLeap - Maximum leap distance. Defaults to 0.
|
package/src/physics/velocity.js
CHANGED
|
@@ -10,7 +10,6 @@ const DEFAULT_MAX_SPEED = 0 // Default maximum speed for velocity clamping.
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Applies velocity to a position based on the given parameters and options.
|
|
13
|
-
*
|
|
14
13
|
* @param {Object} params - The parameters for velocity application.
|
|
15
14
|
* @param {number} [params.maxSpeed] - The maximum speed for clamping the velocity. Defaults to 0.
|
|
16
15
|
* @param {Vector3} [params.velocity] - The current velocity vector. Defaults to a zero vector.
|
package/src/v.js
CHANGED
package/src/v.test.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { v } from "./v"
|
|
4
|
+
|
|
5
|
+
test("a vector created with the v() function is just a number array", () => {
|
|
6
|
+
const vector = v(1, 2, 3)
|
|
7
|
+
expect(vector).toStrictEqual([1, 2, 3])
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test("a vector created with the v() function has a __isVector__ property", () => {
|
|
11
|
+
const vector = v(1, 2, 3)
|
|
12
|
+
expect(vector.__isVector__).toBe(true)
|
|
13
|
+
})
|