@inglorious/utils 3.5.0 → 3.5.2

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.5.0",
3
+ "version": "3.5.2",
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",
@@ -21,10 +21,18 @@
21
21
  ],
22
22
  "type": "module",
23
23
  "exports": {
24
- "./*": "./src/*"
24
+ ".": {
25
+ "types": "./types/index.d.ts",
26
+ "import": "./src/index.js"
27
+ },
28
+ "./*": {
29
+ "types": "./types/*.d.ts",
30
+ "import": "./src/*"
31
+ }
25
32
  },
26
33
  "files": [
27
- "src"
34
+ "src",
35
+ "types"
28
36
  ],
29
37
  "publishConfig": {
30
38
  "access": "public"
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import("./types").Conditions} Conditions
3
- * @typedef {import("./types").Outcome} Outcome
4
- * @typedef {import("./types").DecisionTree} DecisionTree
2
+ * @typedef {import("../../types/algorithms").Conditions} Conditions
3
+ * @typedef {import("../../types/algorithms").Outcome} Outcome
4
+ * @typedef {import("../../types/algorithms").DecisionTree} DecisionTree
5
5
  */
6
6
 
7
7
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @typedef {import("./types").Node} Node
3
- * @typedef {import("./types").Graph} Graph
2
+ * @typedef {import("../../types/data-structures").Node} Node
3
+ * @typedef {import("../../types/data-structures").Graph} Graph
4
4
  */
5
5
 
6
6
  import { contains, push, remove, root } from "../data-structures/heap.js"
@@ -1,4 +1,4 @@
1
- /** @typedef {import('./types').Tree} Tree */
1
+ /** @typedef {import('../../types/data-structures').Tree} Tree */
2
2
 
3
3
  /**
4
4
  * Performs a breadth-first search (BFS) traversal on a tree structure.
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @typedef {import("./geometry/types.js").Point} Point
3
- * @typedef {import("./geometry/types.js").Circle} Circle
4
- * @typedef {import("./geometry/types.js").Rectangle} Rectangle
5
- * @typedef {import("./geometry/types.js").Platform} Platform
2
+ * @typedef {import("../../types/math").Point} Point
3
+ * @typedef {import("../../types/math").Circle} Circle
4
+ * @typedef {import("../../types/math").Rectangle} Rectangle
5
+ * @typedef {import("../../types/math").Platform} Platform
6
6
  */
7
7
 
8
8
  import { intersectsCircle as pointIntersectsCircle } from "./point.js"
@@ -1,10 +1,11 @@
1
1
  // @see https://jonathanwhiting.com/tutorial/collision/
2
2
 
3
+ import { isBetween } from "./numbers.js"
3
4
  import { intersectsRectangle } from "./rectangle.js"
4
5
 
5
- const FIRST_TILE = 0
6
+ const FIRST = 0
6
7
  const HALF = 2
7
- const LAST_ROW = 1
8
+ const LAST = 1
8
9
 
9
10
  export function findCollisions(hitmask, target) {
10
11
  const [tilemapX, tilemapY, tilemapZ] = hitmask.position
@@ -37,15 +38,13 @@ export function findCollisions(hitmask, target) {
37
38
  for (let i = minTileX; i <= maxTileX; i++) {
38
39
  for (let j = minTileZ; j <= maxTileZ; j++) {
39
40
  if (
40
- i < FIRST_TILE ||
41
- i >= hitmask.columns ||
42
- j < FIRST_TILE ||
43
- j >= dRows
41
+ !isBetween(i, FIRST, hitmask.columns - LAST) ||
42
+ !isBetween(j, FIRST, dRows - LAST)
44
43
  ) {
45
44
  continue
46
45
  }
47
46
 
48
- const invertedRow = dRows - LAST_ROW - j
47
+ const invertedRow = dRows - LAST - j
49
48
  const tileIndex = invertedRow * hitmask.columns + i
50
49
  const tileHeightValue = hitmask.heights[tileIndex]
51
50
 
package/src/math/line.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import("./geometry/types.js").Line} Line
3
- * @typedef {import("./geometry/types.js").Point} Point
4
- * @typedef {import("./geometry/types.js").Circle} Circle
2
+ * @typedef {import("../../types/math").Line} Line
3
+ * @typedef {import("../../types/math").Point} Point
4
+ * @typedef {import("../../types/math").Circle} Circle
5
5
  */
6
6
 
7
7
  import { abs } from "./numbers.js"
@@ -30,6 +30,17 @@ export function clamp(num, min, max) {
30
30
  return num
31
31
  }
32
32
 
33
+ /**
34
+ * Checks if a number is between a minimum and maximum value, inclusive.
35
+ * @param {number} num - The number to check.
36
+ * @param {number} min - The minimum value.
37
+ * @param {number} max - The maximum value.
38
+ * @returns {boolean} True if the number is between min and max, false otherwise.
39
+ */
40
+ export function isBetween(num, min, max) {
41
+ return num >= min && num <= max
42
+ }
43
+
33
44
  /**
34
45
  * Checks if two numbers are close to each other within a given tolerance.
35
46
  * @param {number} num1 - The first number.
@@ -1,6 +1,15 @@
1
1
  import { expect, test } from "vitest"
2
2
 
3
- import { abs, clamp, isClose, mod, sign, snap, sqrt } from "./numbers.js"
3
+ import {
4
+ abs,
5
+ clamp,
6
+ isBetween,
7
+ isClose,
8
+ mod,
9
+ sign,
10
+ snap,
11
+ sqrt,
12
+ } from "./numbers.js"
4
13
 
5
14
  test("it should return the same number if already positive", () => {
6
15
  const num = 42
@@ -43,6 +52,51 @@ test("it should clamp a number too big", () => {
43
52
  expect(clamp(num, min, max)).toBe(expectedResult)
44
53
  })
45
54
 
55
+ test("it should return true if a number is between min and max", () => {
56
+ const num = 50
57
+ const min = 0
58
+ const max = 100
59
+ const expectedResult = true
60
+
61
+ expect(isBetween(num, min, max)).toBe(expectedResult)
62
+ })
63
+
64
+ test("it should return true if a number is equal to min", () => {
65
+ const num = 0
66
+ const min = 0
67
+ const max = 100
68
+ const expectedResult = true
69
+
70
+ expect(isBetween(num, min, max)).toBe(expectedResult)
71
+ })
72
+
73
+ test("it should return true if a number is equal to max", () => {
74
+ const num = 100
75
+ const min = 0
76
+ const max = 100
77
+ const expectedResult = true
78
+
79
+ expect(isBetween(num, min, max)).toBe(expectedResult)
80
+ })
81
+
82
+ test("it should return false if a number is lower than min", () => {
83
+ const num = -1
84
+ const min = 0
85
+ const max = 100
86
+ const expectedResult = false
87
+
88
+ expect(isBetween(num, min, max)).toBe(expectedResult)
89
+ })
90
+
91
+ test("it should return false if a number is greater than max", () => {
92
+ const num = 101
93
+ const min = 0
94
+ const max = 100
95
+ const expectedResult = false
96
+
97
+ expect(isBetween(num, min, max)).toBe(expectedResult)
98
+ })
99
+
46
100
  test("it should check if two numbers are close given some tolerance", () => {
47
101
  const num1 = 42
48
102
  const num2 = 42.1
package/src/math/point.js CHANGED
@@ -1,14 +1,15 @@
1
1
  /**
2
- * @typedef {import("./geometry/types.js").Point} Point
3
- * @typedef {import("./geometry/types.js").Line} Line
4
- * @typedef {import("./geometry/types.js").Circle} Circle
5
- * @typedef {import("./geometry/types.js").Rectangle} Rectangle
2
+ * @typedef {import("../../types/math").Point} Point
3
+ * @typedef {import("../../types/math").Line} Line
4
+ * @typedef {import("../../types/math").Circle} Circle
5
+ * @typedef {import("../../types/math").Rectangle} Rectangle
6
6
  */
7
7
 
8
8
  const SQUARED = 2
9
9
  const HALF = 2
10
10
 
11
11
  import { distanceFromPoint } from "./line.js"
12
+ import { isBetween } from "./numbers.js"
12
13
  import { isVector } from "./vector.js"
13
14
 
14
15
  /**
@@ -69,12 +70,9 @@ export function intersectsRectangle(point, rectangle) {
69
70
  const front = rectZ + depth / HALF
70
71
 
71
72
  return (
72
- x >= left &&
73
- x <= right &&
74
- y >= bottom &&
75
- y <= top &&
76
- z >= back &&
77
- z <= front
73
+ isBetween(x, left, right) &&
74
+ isBetween(y, bottom, top) &&
75
+ isBetween(z, back, front)
78
76
  )
79
77
  }
80
78
 
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @typedef {import('../../../types').Vector3} Vector3
3
- * @typedef {import('../../../types').Quaternion} Quaternion
2
+ * @typedef {import("../../types/math").Vector3} Vector3
3
+ * @typedef {import("../../types/math").Quaternion} Quaternion
4
4
  */
5
5
 
6
6
  import { v } from "../v.js"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import('./types').Quaternion} Quaternion
2
+ * @typedef {import("../../types/math").Quaternion} Quaternion
3
3
  */
4
4
 
5
5
  import { v } from "../v.js"
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import("./geometry/types.js").Circle} Circle
3
- * @typedef {import("./geometry/types.js").Rectangle} Rectangle
4
- * @typedef {import("./geometry/types.js").Platform} Platform
2
+ * @typedef {import("../../types/math").Circle} Circle
3
+ * @typedef {import("../../types/math").Rectangle} Rectangle
4
+ * @typedef {import("../../types/math").Platform} Platform
5
5
  */
6
6
 
7
7
  import { intersectsRectangle as circleIntersectsRectangle } from "./circle.js"
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import("./geometry/types.js").Segment} Segment
3
- * @typedef {import("./geometry/types.js").Point} Point
4
- * @typedef {import("./geometry/types.js").Circle} Circle
2
+ * @typedef {import("../../types/math").Segment} Segment
3
+ * @typedef {import("../../types/math").Point} Point
4
+ * @typedef {import("../../types/math").Circle} Circle
5
5
  */
6
6
 
7
7
  import { magnitude, setMagnitude } from "./vector.js"
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import('./types').Vector} Vector
3
- * @typedef {import('./types').Vector2} Vector2
4
- * @typedef {import('./types').Vector3} Vector3
2
+ * @typedef {import("../../types/math").Vector} Vector
3
+ * @typedef {import("../../types/math").Vector2} Vector2
4
+ * @typedef {import("../../types/math").Vector3} Vector3
5
5
  */
6
6
 
7
7
  import { v } from "../v.js"
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @typedef {import('./types').Vector2} Vector2
3
- * @typedef {import('./types').Vector3} Vector3
4
- * @typedef {import('./types').Vector7} Vector7
2
+ * @typedef {import("../../types/math").Vector2} Vector2
3
+ * @typedef {import("../../types/math").Vector3} Vector3
4
+ * @typedef {import("../../types/math").Vector7} Vector7
5
5
  */
6
6
 
7
7
  import { v } from "../v.js"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import('../math/types.js').Vector3} Vector3
2
+ * @typedef {import("../../types/math").Vector3} Vector3
3
3
  */
4
4
 
5
5
  import { clamp, multiply, zero } from "../math/vector.js"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import('../math/types.js').Vector3} Vector3
2
+ * @typedef {import("../../types/math").Vector3} Vector3
3
3
  */
4
4
 
5
5
  import { magnitude, setMagnitude, zero } from "../math/vector.js"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import('../math/types.js').Vector3} Vector3
2
+ * @typedef {import("../../types/math").Vector3} Vector3
3
3
  */
4
4
 
5
5
  /* eslint-disable no-magic-numbers */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import('../math/types').Vector3} Vector3
2
+ * @typedef {import("../../types/math").Vector3} Vector3
3
3
  */
4
4
 
5
5
  /* eslint-disable no-magic-numbers */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import('../math/types.js').Vector3} Vector3
2
+ * @typedef {import("../../types/math").Vector3} Vector3
3
3
  */
4
4
 
5
5
  import { clamp, multiply, zero } from "../math/vector.js"
package/src/v.js CHANGED
@@ -1,4 +1,8 @@
1
- import { isArray } from "./data-structures/array"
1
+ /**
2
+ * @typedef {import("../types/math").Vector} Vector
3
+ */
4
+
5
+ import { isArray } from "./data-structures/array.js"
2
6
 
3
7
  /**
4
8
  * Ensures that a given value is a vector.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Represents the input conditions to be evaluated by a decision tree node.
3
+ * The `unknown` type is used to allow for flexible condition structures.
4
+ */
5
+ export type Conditions = unknown
6
+
7
+ /**
8
+ * Represents the result of a decision tree's `test` function, which is used
9
+ * as a key to determine the next branch.
10
+ */
11
+ export type Outcome = string
12
+
13
+ /**
14
+ * Represents a node in a decision tree. It consists of:
15
+ * - A `test` function that evaluates conditions and returns an outcome.
16
+ * - A map of outcomes to subsequent actions, which can be either a final outcome
17
+ * or another `DecisionTree` node.
18
+ */
19
+ export type DecisionTree = {
20
+ /**
21
+ * A function that takes a set of conditions and returns an outcome string.
22
+ * @param conditions - The conditions to evaluate.
23
+ * @returns The outcome of the test.
24
+ */
25
+ test: (conditions: Conditions) => Outcome
26
+ } & Record<Outcome, () => Outcome | DecisionTree>
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Represents a generic tree data structure.
3
+ * Each node in the tree has a value and an array of child nodes.
4
+ * @template T - The type of the value stored in the tree nodes. Defaults to `any`.
5
+ */
6
+ export interface Tree<T = any> {
7
+ /** The value of the current node. */
8
+ value: T
9
+ /** An array of child `Tree` nodes. */
10
+ children: Tree<T>[]
11
+ }
12
+
13
+ /**
14
+ * A map where keys are node identifiers and values are their [x, y] coordinates.
15
+ * Used for efficient node lookups by ID.
16
+ */
17
+ export interface NodeMap {
18
+ [id: string]: [number, number] // [x, y]
19
+ }
20
+
21
+ /**
22
+ * Represents a node in a graph.
23
+ */
24
+ export interface Node {
25
+ /** A unique identifier for the node. */
26
+ id: string
27
+ /** The [x, y] coordinates of the node. */
28
+ position: [number, number] // [x, y]
29
+ /** An optional cost associated with traversing to this node. */
30
+ cost?: number
31
+ }
32
+
33
+ /**
34
+ * Represents a directed edge (or arc) between two nodes in a graph.
35
+ */
36
+ export interface Arc {
37
+ /** The ID of the starting node. */
38
+ from: string
39
+ /** The ID of the ending node. */
40
+ to: string
41
+ /** An optional cost associated with traversing this arc. */
42
+ cost?: number
43
+ }
44
+
45
+ /**
46
+ * Represents a graph structure consisting of nodes and arcs.
47
+ */
48
+ export interface Graph {
49
+ /** The nodes of the graph, as either an array or a map for efficient lookups. */
50
+ nodes: NodeMap | Node[]
51
+ /** An array of arcs connecting the nodes. */
52
+ arcs: Arc[]
53
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./algorithms"
2
+ export * from "./data-structures"
3
+ export * from "./math"
@@ -0,0 +1,90 @@
1
+ // geometry
2
+
3
+ /**
4
+ * Represents a point in 3D space using a readonly tuple of numbers.
5
+ * @public
6
+ */
7
+ export type Point = readonly [number, number, number] // [x, y, z]
8
+
9
+ /**
10
+ * Represents the dimensions of an object in 3D space.
11
+ * @public
12
+ */
13
+ export type Size = readonly [number, number, number] // [width, height, depth]
14
+
15
+ /**
16
+ * Represents a line in 2D space in the form `ax + by + c = 0`.
17
+ * @public
18
+ */
19
+ export type Line = readonly [number, number, number] // [a, b, c]
20
+
21
+ /**
22
+ * Represents a circle with a position and radius.
23
+ * @public
24
+ */
25
+ export interface Circle {
26
+ /** The center point of the circle. */
27
+ position: Point
28
+ /** The radius of the circle. */
29
+ radius: number
30
+ }
31
+
32
+ /**
33
+ * Represents a rectangle with a position and size.
34
+ * @public
35
+ */
36
+ export interface Rectangle {
37
+ /** The top-left corner point of the rectangle. */
38
+ position: Point
39
+ /** The dimensions of the rectangle. */
40
+ size: Size
41
+ }
42
+
43
+ /**
44
+ * Represents a platform, typically for physics or collision detection.
45
+ * @public
46
+ */
47
+ export interface Platform {
48
+ /** The position of the platform. */
49
+ position: Point
50
+ /** The dimensions of the platform. */
51
+ size: Size
52
+ }
53
+
54
+ /**
55
+ * Represents a line segment defined by two points.
56
+ * @public
57
+ */
58
+ export interface Segment {
59
+ /** The starting point of the segment. */
60
+ from: Point
61
+ /** The ending point of the segment. */
62
+ to: Point
63
+ }
64
+
65
+ // linear algebra
66
+
67
+ /**
68
+ * A nominal type brand to identify vector types.
69
+ * This prevents accidental assignment of a plain number array to a vector.
70
+ * @internal
71
+ */
72
+ type IsVector = {
73
+ readonly __isVector__: true
74
+ }
75
+
76
+ /** A generic, dynamically-sized vector. */
77
+ export type Vector = IsVector & readonly number[]
78
+
79
+ /** A 2-dimensional vector. */
80
+ export type Vector2 = IsVector & readonly [number, number]
81
+
82
+ /** A 3-dimensional vector. */
83
+ export type Vector3 = IsVector & readonly [number, number, number]
84
+
85
+ /** A 4-dimensional vector, often used for quaternions. */
86
+ export type Quaternion = IsVector & readonly [number, number, number, number]
87
+
88
+ /** A 7-dimensional vector. */
89
+ export type Vector7 = IsVector &
90
+ readonly [number, number, number, number, number, number, number]
@@ -1,28 +0,0 @@
1
- export type Conditions = unknown
2
- export type Outcome = string | boolean
3
-
4
- export interface DecisionTree {
5
- test: (conditions: Conditions) => Outcome
6
- [outcome: Outcome]: () => Outcome | DecisionTree
7
- }
8
-
9
- interface NodeMap {
10
- [id: string]: [number, number] // [x, y]
11
- }
12
-
13
- interface Node {
14
- id: string
15
- position: [number, number] // [x, y]
16
- cost?: number
17
- }
18
-
19
- interface Arc {
20
- from: string
21
- to: string
22
- cost?: number
23
- }
24
-
25
- export interface Graph {
26
- nodes: NodeMap | Node[]
27
- arcs: Arc[]
28
- }
@@ -1,4 +0,0 @@
1
- export interface Tree {
2
- value: any
3
- children: Tree[]
4
- }
@@ -1,33 +0,0 @@
1
- // geometry
2
-
3
- export type Point = [number, number, number] // [x, y, z]
4
- export type Size = [number, number, number] // [width, height, depth]
5
- export type Line = [number, number, number] // [a, b, c]
6
-
7
- export interface Circle {
8
- position: Point
9
- radius: number
10
- }
11
-
12
- export interface Rectangle {
13
- position: Point
14
- size: Size
15
- }
16
-
17
- export interface Platform {
18
- position: Point
19
- size: Size
20
- }
21
-
22
- export interface Segment {
23
- from: Point
24
- to: Point
25
- }
26
-
27
- // linear algebra
28
-
29
- export type Vector = number[]
30
- export type Vector2 = [number, number]
31
- export type Vector3 = [number, number, number]
32
- export type Quaternion = [number, number, number, number]
33
- export type Vector7 = [number, number, number, number, number, number, number]