@inglorious/utils 3.3.0 → 3.5.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.3.0",
3
+ "version": "3.5.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,22 +1,47 @@
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).
6
- * - If one argument is provided, returns a random integer between 0 and the given number (inclusive).
7
- * - If two or more arguments are provided, returns a random integer within the specified range and step.
8
- * @param {number} [to] - The upper bound (inclusive) if one argument is provided.
9
- * @param {number} [from] - The lower bound (inclusive) if two arguments are provided.
19
+ * - If one argument (`to`) is provided, returns a random integer between 0 and `to` (inclusive).
20
+ * - If two arguments (`from`, `to`) are provided:
21
+ * - If they are integers, returns a random integer between `from` and `to` (inclusive).
22
+ * - If they are floats, returns a random float between `from` (inclusive) and `to` (exclusive).
23
+ * - If three arguments (`from`, `to`, `step`) are provided, returns a random integer within the specified range and step.
24
+ * @param {number} [to] - The upper bound.
25
+ * @param {number} [from] - The lower bound.
10
26
  * @param {number} [step=1] - The step size if two or more arguments are provided.
11
27
  * @returns {number} A random number based on the provided arguments.
12
28
  */
13
29
  export function random(...args) {
14
- let step, from, to
15
-
16
30
  if (!args.length) {
17
31
  return Math.random()
18
32
  }
19
33
 
34
+ if (
35
+ args.length > 1 &&
36
+ !Number.isInteger(args[0]) &&
37
+ !Number.isInteger(args[1])
38
+ ) {
39
+ const [from, to] = args
40
+ return Math.random() * (to - from) + from
41
+ }
42
+
43
+ let step, from, to
44
+
20
45
  if (args.length === 1) {
21
46
  step = 1
22
47
  from = 0
@@ -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()
@@ -31,6 +38,15 @@ test("it should create a random even number in the range [2, 6]", () => {
31
38
  expect(randomNumber % 2).toBe(0)
32
39
  })
33
40
 
41
+ test("it should create a random float in the range [1.5, 6.5)", () => {
42
+ const from = 1.5
43
+ const to = 6.5
44
+ const randomNumber = random(from, to)
45
+
46
+ expect(randomNumber).toBeGreaterThanOrEqual(from)
47
+ expect(randomNumber).toBeLessThan(to)
48
+ })
49
+
34
50
  test("it should create a random number in the range (-1, 1)", () => {
35
51
  const randomNumber = randomBinomial()
36
52
 
@@ -93,7 +93,7 @@ export function conjugate(vector) {
93
93
  * @returns {Vector3} The created 3D vector.
94
94
  */
95
95
  export function createVector(magnitude, angle) {
96
- return multiply(fromAngle(angle), magnitude)
96
+ return scale(fromAngle(angle), magnitude)
97
97
  }
98
98
 
99
99
  /**
@@ -221,9 +221,9 @@ export const remainder = mod
221
221
  export function rotate(vector, angle) {
222
222
  const is2D = vector.length === TWO_COORDINATES
223
223
 
224
- let v = is2D ? from2D(vector) : vector
224
+ const vector3 = is2D ? from2D(vector) : vector
225
225
 
226
- let result = rotateWithQuaternion(v, angle)
226
+ const result = rotateWithQuaternion(vector3, angle)
227
227
 
228
228
  return is2D ? to2D(result) : result
229
229
  }
@@ -254,7 +254,7 @@ export const setLength = setMagnitude
254
254
  */
255
255
  export function setMagnitude(vector, length) {
256
256
  const normalized = normalize(vector)
257
- return multiply(normalized, length)
257
+ return scale(normalized, length)
258
258
  }
259
259
 
260
260
  /**
@@ -389,7 +389,7 @@ function rotateWithQuaternion(vector, angle) {
389
389
  const [w, ...r] = quaternion(angle)
390
390
  const result = sum(
391
391
  vector,
392
- 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
393
393
  )
394
394
 
395
395
  return conjugate(result) // HACK: not really sure why I should invert the result, it just works this way
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)