@inglorious/utils 3.2.0 → 3.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/utils",
3
- "version": "3.2.0",
3
+ "version": "3.4.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",
@@ -32,7 +32,8 @@
32
32
  "devDependencies": {
33
33
  "prettier": "^3.6.2",
34
34
  "vite": "^7.1.3",
35
- "vitest": "^1.6.1"
35
+ "vitest": "^1.6.1",
36
+ "@inglorious/eslint-config": "1.0.0"
36
37
  },
37
38
  "engines": {
38
39
  "node": ">= 22"
package/src/math/rng.js CHANGED
@@ -1,5 +1,18 @@
1
1
  /* eslint-disable no-magic-numbers */
2
2
 
3
+ /**
4
+ * Chooses a random value from the given arguments.
5
+ * @param {...*} values - The values to choose from.
6
+ * @returns {*} A randomly selected value.
7
+ */
8
+ export function choose(...values) {
9
+ if (!values.length) {
10
+ return undefined
11
+ }
12
+ const index = random(values.length - 1)
13
+ return values[index]
14
+ }
15
+
3
16
  /**
4
17
  * Generates a random number.
5
18
  * - If no arguments are provided, returns a random float between 0 (inclusive) and 1 (exclusive).
@@ -1,6 +1,13 @@
1
1
  import { expect, test } from "vitest"
2
2
 
3
- import { random, randomBinomial } from "./rng.js"
3
+ import { choose, random, randomBinomial } from "./rng.js"
4
+
5
+ test("it should choose a random value from the given arguments", () => {
6
+ const values = ["a", "b", "c"]
7
+ const chosenValue = choose(...values)
8
+
9
+ expect(values).toContain(chosenValue)
10
+ })
4
11
 
5
12
  test("it should create a random number in the range [0, 1)", () => {
6
13
  const randomNumber = random()
@@ -4,7 +4,6 @@
4
4
  * @typedef {import('./types').Vector3} Vector3
5
5
  */
6
6
 
7
- import { isArray } from "../data-structures/array.js"
8
7
  import { v } from "../v.js"
9
8
  import {
10
9
  abs as nAbs,
@@ -94,7 +93,7 @@ export function conjugate(vector) {
94
93
  * @returns {Vector3} The created 3D vector.
95
94
  */
96
95
  export function createVector(magnitude, angle) {
97
- return multiply(fromAngle(angle), magnitude)
96
+ return scale(fromAngle(angle), magnitude)
98
97
  }
99
98
 
100
99
  /**
@@ -128,17 +127,13 @@ export function fromAngle(angle) {
128
127
 
129
128
  /**
130
129
  * Checks if a value is a vector.
131
- * A vector is an array of numbers. For performance, vectors created with `v()`
132
- * are tagged and can be identified more quickly.
130
+ * This is determined by checking for the `__isVector__` property, which is
131
+ * added by the `v()` factory function for efficient type checking.
133
132
  * @param {*} value - The value to check.
134
133
  * @returns {boolean} True if the value is a vector, false otherwise.
135
134
  */
136
135
  export function isVector(value) {
137
- return (
138
- value?.__isVector__ ||
139
- (isArray(value) &&
140
- value.every((coordinate) => typeof coordinate === "number"))
141
- )
136
+ return !!value?.__isVector__
142
137
  }
143
138
 
144
139
  /**
@@ -226,9 +221,9 @@ export const remainder = mod
226
221
  export function rotate(vector, angle) {
227
222
  const is2D = vector.length === TWO_COORDINATES
228
223
 
229
- let v = is2D ? from2D(vector) : vector
224
+ const vector3 = is2D ? from2D(vector) : vector
230
225
 
231
- let result = rotateWithQuaternion(v, angle)
226
+ const result = rotateWithQuaternion(vector3, angle)
232
227
 
233
228
  return is2D ? to2D(result) : result
234
229
  }
@@ -259,7 +254,7 @@ export const setLength = setMagnitude
259
254
  */
260
255
  export function setMagnitude(vector, length) {
261
256
  const normalized = normalize(vector)
262
- return multiply(normalized, length)
257
+ return scale(normalized, length)
263
258
  }
264
259
 
265
260
  /**
@@ -394,7 +389,7 @@ function rotateWithQuaternion(vector, angle) {
394
389
  const [w, ...r] = quaternion(angle)
395
390
  const result = sum(
396
391
  vector,
397
- cross(multiply(r, 2), sum(cross(r, vector), multiply(vector, w))), // eslint-disable-line no-magic-numbers
392
+ cross(scale(r, 2), sum(cross(r, vector), scale(vector, w))), // eslint-disable-line no-magic-numbers
398
393
  )
399
394
 
400
395
  return conjugate(result) // HACK: not really sure why I should invert the result, it just works this way
@@ -126,8 +126,7 @@ test("it should compute the magnitude of a vector (aka length)", () => {
126
126
  })
127
127
 
128
128
  test("it should check if a value is a vector", () => {
129
- expect(isVector([1, 2, 3])).toBe(true)
130
- expect(isVector([])).toBe(true)
129
+ expect(isVector([1, 2, 3])).toBe(false)
131
130
  expect(isVector(v(1, 2, 3))).toBe(true)
132
131
  })
133
132
 
package/src/v.js CHANGED
@@ -1,3 +1,27 @@
1
+ import { isArray } from "./data-structures/array"
2
+
3
+ /**
4
+ * Ensures that a given value is a vector.
5
+ *
6
+ * If the value is an array of finite numbers but not already a vector (i.e., it lacks the `__isVector__` property),
7
+ * this function converts it into a vector by calling `v()`. If the value is already a vector or not a numeric array,
8
+ * it is returned unchanged. This is particularly useful for ensuring that the results of standard array operations
9
+ * (like `.map()` or `.filter()`) which return a new array are correctly re-tagged as vectors.
10
+ * @param {*} value The value to check and potentially convert.
11
+ * @returns {Vector|*} The value as a vector, or the original value if no conversion was needed.
12
+ */
13
+ export function ensureV(value) {
14
+ if (
15
+ isArray(value) &&
16
+ !value.__isVector__ &&
17
+ value.every((coord) => isFinite(coord))
18
+ ) {
19
+ return v(...value)
20
+ }
21
+
22
+ return value
23
+ }
24
+
1
25
  /**
2
26
  * A utility function to create a vector from a list of coordinates.
3
27
  * This is a shorthand for `[x, y, z, ...]`.
package/src/v.test.js CHANGED
@@ -1,6 +1,41 @@
1
1
  import { expect, test } from "vitest"
2
2
 
3
- import { v } from "./v"
3
+ import { ensureV, v } from "./v"
4
+
5
+ test("ensureV() should convert a numeric array into a vector", () => {
6
+ const array = [1, 2, 3]
7
+ const result = ensureV(array)
8
+ expect(result).toStrictEqual([1, 2, 3])
9
+ expect(result.__isVector__).toBe(true)
10
+ })
11
+
12
+ test("ensureV() should return an existing vector unchanged", () => {
13
+ const vector = v(4, 5, 6)
14
+ const result = ensureV(vector)
15
+ expect(result).toBe(vector)
16
+ expect(result.__isVector__).toBe(true)
17
+ })
18
+
19
+ test("ensureV() should not convert an array with non-finite numbers", () => {
20
+ const array = [1, Infinity, 3]
21
+ const result = ensureV(array)
22
+ expect(result).toBe(array)
23
+ expect(result.__isVector__).toBeUndefined()
24
+ })
25
+
26
+ test("ensureV() should not convert an array with non-numeric values", () => {
27
+ const array = [1, "a", 3]
28
+ const result = ensureV(array)
29
+ expect(result).toBe(array)
30
+ expect(result.__isVector__).toBeUndefined()
31
+ })
32
+
33
+ test("ensureV() should return non-array values unchanged", () => {
34
+ expect(ensureV(123)).toBe(123)
35
+ expect(ensureV("hello")).toBe("hello")
36
+ const obj = { a: 1 }
37
+ expect(ensureV(obj)).toBe(obj)
38
+ })
4
39
 
5
40
  test("a vector created with the v() function is just a number array", () => {
6
41
  const vector = v(1, 2, 3)