@benev/math 0.0.0-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.
Files changed (48) hide show
  1. package/license +23 -0
  2. package/package.json +41 -0
  3. package/readme.md +3 -0
  4. package/s/concepts/angles.ts +74 -0
  5. package/s/concepts/circular.ts +72 -0
  6. package/s/concepts/noise.ts +23 -0
  7. package/s/concepts/quat.ts +131 -0
  8. package/s/concepts/randy.ts +116 -0
  9. package/s/concepts/scalar.ts +224 -0
  10. package/s/concepts/spline.ts +88 -0
  11. package/s/concepts/vec2.ts +392 -0
  12. package/s/concepts/vec3.ts +439 -0
  13. package/s/concepts/vec4.ts +74 -0
  14. package/s/index.ts +12 -0
  15. package/x/concepts/angles.d.ts +29 -0
  16. package/x/concepts/angles.js +62 -0
  17. package/x/concepts/angles.js.map +1 -0
  18. package/x/concepts/circular.d.ts +17 -0
  19. package/x/concepts/circular.js +70 -0
  20. package/x/concepts/circular.js.map +1 -0
  21. package/x/concepts/noise.d.ts +9 -0
  22. package/x/concepts/noise.js +20 -0
  23. package/x/concepts/noise.js.map +1 -0
  24. package/x/concepts/quat.d.ts +35 -0
  25. package/x/concepts/quat.js +110 -0
  26. package/x/concepts/quat.js.map +1 -0
  27. package/x/concepts/randy.d.ts +39 -0
  28. package/x/concepts/randy.js +95 -0
  29. package/x/concepts/randy.js.map +1 -0
  30. package/x/concepts/scalar.d.ts +51 -0
  31. package/x/concepts/scalar.js +219 -0
  32. package/x/concepts/scalar.js.map +1 -0
  33. package/x/concepts/spline.d.ts +9 -0
  34. package/x/concepts/spline.js +59 -0
  35. package/x/concepts/spline.js.map +1 -0
  36. package/x/concepts/vec2.d.ts +114 -0
  37. package/x/concepts/vec2.js +314 -0
  38. package/x/concepts/vec2.js.map +1 -0
  39. package/x/concepts/vec3.d.ts +117 -0
  40. package/x/concepts/vec3.js +357 -0
  41. package/x/concepts/vec3.js.map +1 -0
  42. package/x/concepts/vec4.d.ts +21 -0
  43. package/x/concepts/vec4.js +62 -0
  44. package/x/concepts/vec4.js.map +1 -0
  45. package/x/importmap.json +9 -0
  46. package/x/index.d.ts +10 -0
  47. package/x/index.js +11 -0
  48. package/x/index.js.map +1 -0
package/license ADDED
@@ -0,0 +1,23 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2025 Chase Moskal
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@benev/math",
3
+ "version": "0.0.0-0",
4
+ "description": "game math toolkit",
5
+ "license": "MIT",
6
+ "author": "Chase Moskal <chasemoskal@gmail.com>",
7
+ "type": "module",
8
+ "main": "x/index.js",
9
+ "files": [
10
+ "x",
11
+ "s"
12
+ ],
13
+ "scripts": {
14
+ "build": "turtle build --out=x -v",
15
+ "start": "http-server x",
16
+ "test": "exit 0"
17
+ },
18
+ "dependencies": {
19
+ "@e280/stz": "^0.0.0-3",
20
+ "simplex-noise": "^4.0.3"
21
+ },
22
+ "devDependencies": {
23
+ "@benev/turtle": "^0.6.8",
24
+ "http-server": "^14.1.1",
25
+ "npm-run-all": "^4.1.5",
26
+ "typescript": "^5.8.3"
27
+ },
28
+ "keywords": [
29
+ "game",
30
+ "math",
31
+ "vectors"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/benevolent-games/math.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/benevolent-games/math/issues"
39
+ },
40
+ "homepage": "https://github.com/benevolent-games/math#readme"
41
+ }
package/readme.md ADDED
@@ -0,0 +1,3 @@
1
+
2
+ # @benev/math
3
+
@@ -0,0 +1,74 @@
1
+
2
+ import {Scalar} from "./scalar.js"
3
+
4
+ const pi = Math.PI
5
+ const circle = 2 * Math.PI
6
+
7
+ const to = {
8
+ degrees(r: number) {
9
+ return r * (180 / pi)
10
+ },
11
+ arcseconds(r: number) {
12
+ return to.degrees(r) * 3600
13
+ },
14
+ turns(r: number) {
15
+ return r / circle
16
+ },
17
+ }
18
+
19
+ const from = {
20
+ turns(t: number) {
21
+ return t * circle
22
+ },
23
+ degrees(d: number) {
24
+ return d * (pi / 180)
25
+ },
26
+ arcseconds(a: number) {
27
+ return from.degrees(a / 3600)
28
+ }
29
+ }
30
+
31
+ export const Radians = {
32
+ pi,
33
+ circle,
34
+
35
+ /** @deprecated use Radians.toDegrees instead */
36
+ to,
37
+
38
+ /** @deprecated use Degrees.toRadians instead */
39
+ from,
40
+
41
+ toDegrees(r: number) {
42
+ return r * (180 / pi)
43
+ },
44
+ toArcseconds(r: number) {
45
+ return to.degrees(r) * 3600
46
+ },
47
+ toTurns(r: number) {
48
+ return r / circle
49
+ },
50
+
51
+ circleDistance(radiansA: number, radiansB: number): number {
52
+ const diff = Math.abs(Scalar.wrap(radiansA - radiansB, 0, Radians.circle))
53
+ return Math.min(diff, Radians.circle - diff)
54
+ },
55
+ }
56
+
57
+ export const Turns = {
58
+ toRadians(t: number) {
59
+ return t * circle
60
+ },
61
+ }
62
+
63
+ export const Arcseconds = {
64
+ toRadians(a: number) {
65
+ return from.degrees(a / 3600)
66
+ },
67
+ }
68
+
69
+ export const Degrees = {
70
+ toRadians(d: number) {
71
+ return d * (pi / 180)
72
+ },
73
+ }
74
+
@@ -0,0 +1,72 @@
1
+
2
+ import {Scalar} from "./scalar.js"
3
+
4
+ export class Circular {
5
+ constructor(public x: number) {}
6
+
7
+ clone() {
8
+ return new Circular(this.x)
9
+ }
10
+
11
+ set(x: number) {
12
+ this.x = x
13
+ return this
14
+ }
15
+
16
+ static value(x: number | Circular) {
17
+ return typeof x === "number"
18
+ ? x
19
+ : x.x
20
+ }
21
+
22
+ static normalize(x: number) {
23
+ return Scalar.wrap(x, 0, 2 * Math.PI)
24
+ } normalize() {
25
+ this.x = Circular.normalize(this.x)
26
+ return this
27
+ }
28
+
29
+ static difference(x: number, y: number) {
30
+ x = this.normalize(x)
31
+ y = this.normalize(y)
32
+ let delta = y - x
33
+ if (delta > Math.PI) delta -= 2 * Math.PI
34
+ if (delta < -Math.PI) delta += 2 * Math.PI
35
+ return delta
36
+ } difference(y: number | Circular) {
37
+ return Circular.difference(this.x, Circular.value(y))
38
+ }
39
+
40
+ static lerp(x: number, y: number, fraction: number, max?: number) {
41
+ const difference = this.difference(x, y)
42
+ let delta = difference * fraction
43
+ if (max !== undefined && Math.abs(delta) > max)
44
+ delta = Math.sign(delta) * max
45
+ return this.normalize(x + delta)
46
+ } lerp(y: number | Circular, fraction: number, max?: number) {
47
+ this.x = Circular.lerp(this.x, Circular.value(y), fraction, max)
48
+ return this
49
+ }
50
+
51
+ static step(x: number, y: number, delta: number) {
52
+ const difference = this.difference(x, y)
53
+ return this.normalize(
54
+ Math.abs(difference) <= delta
55
+ ? y
56
+ : x + (Math.sign(difference) * delta)
57
+ )
58
+ } step(y: number | Circular, delta: number) {
59
+ this.x = Circular.step(this.x, Circular.value(y), delta)
60
+ return this
61
+ }
62
+
63
+ static approach(x: number, y: number, speed: number, deltaTime: number, speedLimit?: number) {
64
+ const difference = this.difference(x, y)
65
+ const change = Scalar.creep(difference, speed, deltaTime, speedLimit)
66
+ return this.normalize(x + change)
67
+ } approach(y: number | Circular, speed: number, deltaTime: number, speedLimit?: number) {
68
+ this.x = Circular.approach(this.x, Circular.value(y), speed, deltaTime, speedLimit)
69
+ return this
70
+ }
71
+ }
72
+
@@ -0,0 +1,23 @@
1
+
2
+ import {Randy, Random} from "./randy.js"
3
+ import {createNoise2D} from "simplex-noise"
4
+
5
+ export class Noise {
6
+ static seed(seed: number = Randy.randomSeed()) {
7
+ const random = Randy.makeRandom(seed)
8
+ return new this(random)
9
+ }
10
+
11
+ #n2d: (x: number, y: number) => number
12
+
13
+ constructor(public readonly random: Random) {
14
+ this.#n2d = createNoise2D(random)
15
+ }
16
+
17
+ /** sample the noise field, returning a number from 0 to 1. */
18
+ sample(x: number, y = 0, scale = 1) {
19
+ const s = this.#n2d(x * scale, y * scale)
20
+ return (s + 1) / 2
21
+ }
22
+ }
23
+
@@ -0,0 +1,131 @@
1
+
2
+ import {Xyz} from "./vec3.js"
3
+
4
+ export type QuatArray = [number, number, number, number]
5
+ export type Xyzw = {x: number, y: number, z: number, w: number}
6
+
7
+ export class Quat {
8
+ constructor(
9
+ public x: number,
10
+ public y: number,
11
+ public z: number,
12
+ public w: number,
13
+ ) {}
14
+
15
+ static new(x: number, y: number, z: number, w: number) {
16
+ return new this(x, y, z, w)
17
+ }
18
+
19
+ static identity() {
20
+ return new this(0, 0, 0, 1)
21
+ }
22
+
23
+ static array(q: QuatArray) {
24
+ return new this(...q)
25
+ }
26
+
27
+ static import({x, y, z, w}: Xyzw) {
28
+ return new this(x, y, z, w)
29
+ }
30
+
31
+ static from(q: QuatArray | Xyzw) {
32
+ return Array.isArray(q)
33
+ ? this.array(q)
34
+ : this.import(q)
35
+ }
36
+
37
+ static rotate_(pitch: number, yaw: number, roll: number) {
38
+ return this.identity().rotate_(pitch, yaw, roll)
39
+ }
40
+
41
+ static rotate(vec: Xyz) {
42
+ return this.identity().rotate(vec)
43
+ }
44
+
45
+ array(): QuatArray {
46
+ const {x, y, z, w} = this
47
+ return [x, y, z, w]
48
+ }
49
+
50
+ toString() {
51
+ return `(Quat x${this.x.toFixed(2)}, y${this.y.toFixed(2)}, z${this.z.toFixed(2)}, w${this.w.toFixed(2)})`
52
+ }
53
+
54
+ clone() {
55
+ return new Quat(...this.array())
56
+ }
57
+
58
+ transform_(x: number, y: number, z: number, w: number, global = false) {
59
+ if (global) {
60
+ const original = this.array()
61
+ return this.set_(x, y, z, w).multiply_(...original)
62
+ }
63
+ else {
64
+ return this.multiply_(x, y, z, w)
65
+ }
66
+ }
67
+
68
+ transform({x, y, z, w}: Xyzw, global = false) {
69
+ return this.transform_(x, y, z, w, global)
70
+ }
71
+
72
+ rotate_(pitch: number, yaw: number, roll: number, global = false): Quat {
73
+ const cx = Math.cos(pitch * 0.5), sx = Math.sin(pitch * 0.5)
74
+ const cy = Math.cos(yaw * 0.5), sy = Math.sin(yaw * 0.5)
75
+ const cz = Math.cos(roll * 0.5), sz = Math.sin(roll * 0.5)
76
+ const x = sx * cy * cz + cx * sy * sz
77
+ const y = cx * sy * cz - sx * cy * sz
78
+ const z = cx * cy * sz + sx * sy * cz
79
+ const w = cx * cy * cz - sx * sy * sz
80
+ return this.transform_(x, y, z, w, global)
81
+ }
82
+
83
+ rotate({x: pitch, y: yaw, z: roll}: Xyz, global = false) {
84
+ return this.rotate_(pitch, yaw, roll, global)
85
+ }
86
+
87
+ rotateAroundAxis_(angle: number, axisX: number, axisY: number, axisZ: number, global = false): Quat {
88
+ const halfAngle = angle * 0.5
89
+ const sinHalf = Math.sin(halfAngle)
90
+ const x = axisX * sinHalf
91
+ const y = axisY * sinHalf
92
+ const z = axisZ * sinHalf
93
+ const w = Math.cos(halfAngle)
94
+ return this.transform_(x, y, z, w, global)
95
+ }
96
+
97
+ rotateAroundAxis(angle: number, axis: Xyz, global = false): Quat {
98
+ return this.rotateAroundAxis_(angle, axis.x, axis.y, axis.z, global)
99
+ }
100
+
101
+ set_(x: number, y: number, z: number, w: number) {
102
+ this.x = x
103
+ this.y = y
104
+ this.z = z
105
+ this.w = w
106
+ return this
107
+ }
108
+
109
+ set({x, y, z, w}: Xyzw) {
110
+ this.x = x
111
+ this.y = y
112
+ this.z = z
113
+ this.w = w
114
+ return this
115
+ }
116
+
117
+ multiply_(x2: number, y2: number, z2: number, w2: number): Quat {
118
+ const {x, y, z, w} = this
119
+ this.x = w * x2 + x * w2 + y * z2 - z * y2
120
+ this.y = w * y2 - x * z2 + y * w2 + z * x2
121
+ this.z = w * z2 + x * y2 - y * x2 + z * w2
122
+ this.w = w * w2 - x * x2 - y * y2 - z * z2
123
+ return this
124
+ }
125
+
126
+ multiply(...quats: Quat[]) {
127
+ for (const {x, y, z, w} of quats) this.multiply_(x, y, z, w)
128
+ return this
129
+ }
130
+ }
131
+
@@ -0,0 +1,116 @@
1
+
2
+ export type Random = () => number
3
+
4
+ /** utility for generating and using random numbers. */
5
+ export class Randy {
6
+ random: Random
7
+
8
+ constructor(public readonly seed: number = Randy.randomSeed()) {
9
+ this.random = Randy.makeRandom(seed)
10
+ }
11
+
12
+ /** obtain a random positive 32 bit integer. */
13
+ static randomSeed() {
14
+ return Math.floor(Math.random() * 2147483647)
15
+ }
16
+
17
+ /** seed a pseudo-random number generator function that produces numbers between 0 and 1. */
18
+ static makeRandom(seed: number): Random {
19
+ seed = (seed ^ 0x6D2B79F5) + 0x1E35A7BD
20
+ seed = (Math.abs(seed | 0) % 2147483647) || 1
21
+
22
+ function random() {
23
+ seed = (Math.imul(48271, seed) | 0) % 2147483647
24
+ return (seed & 2147483647) / 2147483648
25
+ }
26
+
27
+ random() // discard first value
28
+ return random
29
+ }
30
+
31
+ /** obtain a random positive integer. */
32
+ integer() {
33
+ return Math.floor(this.random() * 2147483647)
34
+ }
35
+
36
+ /** return true or false, given a 0 to 1 probability fraction. */
37
+ roll(chance = 0.5) {
38
+ return this.random() <= chance
39
+ }
40
+
41
+ /** generate a random number between two numbers. */
42
+ range(a: number, b: number) {
43
+ const difference = b - a
44
+ const value = difference * this.random()
45
+ return a + value
46
+ }
47
+
48
+ /** generate a random integer between two numbers (inclusive). */
49
+ integerRange(a: number, b: number) {
50
+ return Math.round(this.range(a, b))
51
+ }
52
+
53
+ /** randomly choose an index given an array length. */
54
+ index(length: number) {
55
+ return Math.floor(this.random() * length)
56
+ }
57
+
58
+ /** return a random item from the given array. */
59
+ choose<T>(array: T[]) {
60
+ return array[this.index(array.length)]
61
+ }
62
+
63
+ /** remove and return a random item from the given array. */
64
+ yoink<T>(array: T[]) {
65
+ const index = this.index(array.length)
66
+ const [item] = array.splice(index, 1)
67
+ return item
68
+ }
69
+
70
+ /** randomly select a number of array items. */
71
+ select<T>(count: number, array: T[]) {
72
+ const copy = [...array]
73
+ if (count >= array.length)
74
+ return copy
75
+
76
+ const selection: T[] = []
77
+ for (let i = 0; i < count; i++)
78
+ selection.push(this.yoink(copy))
79
+ return selection
80
+ }
81
+
82
+ /** remove and return a number of items from the given array. */
83
+ take<T>(count: number, array: T[]) {
84
+ const selection: T[] = []
85
+ for (let i = 0; i < count; i++) {
86
+ if (array.length === 0)
87
+ return selection
88
+ selection.push(this.yoink(array))
89
+ }
90
+ return selection
91
+ }
92
+
93
+ /** shuffle an array in-place using (fisher-yates) */
94
+ shuffle<T>(array: T[]) {
95
+ for (let i = array.length - 1; i > 0; i--) {
96
+ const j = Math.floor(this.random() * (i + 1))
97
+ ;[array[i], array[j]] = [array[j], array[i]]
98
+ }
99
+ return array
100
+ }
101
+
102
+ /** @deprecated create an instance with the given seed number. */
103
+ static seed(seed: number): Randy {
104
+ return new this(seed)
105
+ }
106
+
107
+ /** @deprecated renamed to `take` */
108
+ extract<T>(count: number, array: T[]) { return this.take(count, array) }
109
+
110
+ /** @deprecated renamed to `range` */
111
+ between(a: number, b: number) { return this.range(a, b) }
112
+
113
+ /** @deprecated renamed to `integerRange` */
114
+ integerBetween(a: number, b: number) { return this.integerRange(a, b) }
115
+ }
116
+
@@ -0,0 +1,224 @@
1
+
2
+ export class Scalar {
3
+ constructor(public x: number) {}
4
+
5
+ static new(x: number) {
6
+ return new this(x)
7
+ }
8
+
9
+ clone() {
10
+ return new Scalar(this.x)
11
+ }
12
+
13
+ set(x: number) {
14
+ this.x = x
15
+ return this
16
+ }
17
+
18
+ //
19
+ // queries
20
+ //
21
+
22
+ static isBetween(x: number, a: number = 0, b: number = 1) {
23
+ const min = Math.min(a, b)
24
+ const max = Math.max(a, b)
25
+ return (x >= min) && (x <= max)
26
+ } isBetween(a: number = 0, b: number = 1) {
27
+ return Scalar.isBetween(this.x, a, b)
28
+ }
29
+
30
+ static isNear(x: number, y: number, epsilon: number = 0.01) {
31
+ return Math.abs(x - y) <= epsilon
32
+ } isNear(y: number, epsilon: number = 0.01) {
33
+ return Scalar.isNear(this.x, y, epsilon)
34
+ }
35
+
36
+ //
37
+ // chainable transforms
38
+ //
39
+
40
+ static add(...nums: number[]) {
41
+ let x = 0
42
+ for (const n of nums)
43
+ x += n
44
+ return x
45
+ } add(...nums: number[]) {
46
+ this.x = Scalar.add(...nums)
47
+ return this
48
+ }
49
+
50
+ subtract(...nums: number[]) {
51
+ for (const n of nums)
52
+ this.x -= n
53
+ return this
54
+ }
55
+
56
+ static min(x: number, minimum: number = 0) {
57
+ return Math.max(x, minimum)
58
+ } min(minimum: number = 0) {
59
+ this.x = Scalar.min(this.x, minimum)
60
+ return this
61
+ }
62
+
63
+ static max(x: number, maximum: number = 1) {
64
+ return Math.min(x, maximum)
65
+ } max(maximum: number = 1) {
66
+ this.x = Scalar.max(this.x, maximum)
67
+ return this
68
+ }
69
+
70
+ static clamp(x: number, a: number = 0, b: number = 1) {
71
+ x = Scalar.min(x, Math.min(a, b))
72
+ x = Scalar.max(x, Math.max(a, b))
73
+ return x
74
+ } clamp(a: number = 0, b: number = 1) {
75
+ this.x = Scalar.clamp(this.x, a, b)
76
+ return this
77
+ }
78
+
79
+ static lerp(x: number, y: number, fraction: number, max?: number) {
80
+ const difference = y - x
81
+ let delta = difference * fraction
82
+ if (max !== undefined && Math.abs(delta) > max)
83
+ delta = Math.sign(delta) * max
84
+ return x + delta
85
+ } lerp(y: number, fraction: number, max?: number) {
86
+ this.x = Scalar.lerp(this.x, y, fraction, max)
87
+ return this
88
+ }
89
+
90
+ static step(x: number, y: number, delta: number) {
91
+ const difference = y - x
92
+ return (Math.abs(difference) <= delta)
93
+ ? y
94
+ : x + (Math.sign(difference) * delta)
95
+ } step(y: number, delta: number) {
96
+ this.x = Scalar.step(this.x, y, delta)
97
+ return this
98
+ }
99
+
100
+ static creep(difference: number, speed: number, deltaTime: number, speedLimit?: number) {
101
+ let change = (difference * (1 - Math.exp(-speed * deltaTime)))
102
+ if (speedLimit !== undefined) {
103
+ const changeLimit = speedLimit * deltaTime
104
+ if (Math.abs(change) > changeLimit)
105
+ change = Math.sign(change) * changeLimit
106
+ }
107
+ return change
108
+ }
109
+
110
+ static approach(x: number, y: number, speed: number, deltaTime: number, speedLimit?: number) {
111
+ const difference = y - x
112
+ const change = this.creep(difference, speed, deltaTime, speedLimit)
113
+ return x + change
114
+ } approach(y: number, speed: number, deltaTime: number, speedLimit?: number) {
115
+ this.x = Scalar.approach(this.x, y, speed, deltaTime, speedLimit)
116
+ return this
117
+ }
118
+
119
+ static wrap(x: number, a: number = 0, b: number = 1) {
120
+ const min = Math.min(a, b)
121
+ const max = Math.max(a, b)
122
+ const span = max - min
123
+ const adjusted = x - min
124
+ const wrapped = (adjusted < 0)
125
+ ? span - (-adjusted % span)
126
+ : adjusted % span
127
+ return min + wrapped
128
+ } wrap(a: number = 0, b: number = 1) {
129
+ this.x = Scalar.wrap(this.x, a, b)
130
+ return this
131
+ }
132
+
133
+ static constrainProximity(x: number, y: number, range: number) {
134
+ const trueDiff = y - x
135
+ const positiveDiff = Math.abs(trueDiff)
136
+ const cappedDiff = (positiveDiff > range)
137
+ ? range
138
+ : positiveDiff
139
+ const newDiff = (trueDiff < 0) ? -cappedDiff : cappedDiff
140
+ return x + newDiff
141
+ } constrainProximity(y: number, range: number) {
142
+ this.x = Scalar.constrainProximity(this.x, y, range)
143
+ return this
144
+ }
145
+
146
+ static inverse(x: number) {
147
+ return 1 - x
148
+ } inverse() {
149
+ this.x = Scalar.inverse(this.x)
150
+ return this
151
+ }
152
+
153
+ static center(x: number) {
154
+ return (x * 2) - 1
155
+ } center() {
156
+ this.x = Scalar.center(this.x)
157
+ return this
158
+ }
159
+
160
+ static uncenter(x: number) {
161
+ return (x + 1) / 2
162
+ } uncenter() {
163
+ this.x = Scalar.uncenter(this.x)
164
+ return this
165
+ }
166
+
167
+ static map(x: number, a: number, b: number) {
168
+ const difference = b - a
169
+ const value = difference * x
170
+ return a + value
171
+ } map(a: number, b: number) {
172
+ this.x = Scalar.map(this.x, a, b)
173
+ return this
174
+ }
175
+
176
+ static remap(x: number, a1: number, a2: number, b1: number = 0, b2: number = 1, clamp = false) {
177
+ const fraction = (x - a1) / (a2 - a1)
178
+ const result = (fraction * (b2 - b1)) + b1
179
+ return clamp
180
+ ? Scalar.clamp(result, b1, b2)
181
+ : result
182
+ } remap(a1: number, a2: number, b1: number = 0, b2: number = 1, clamp = false) {
183
+ this.x = Scalar.remap(this.x, a1, a2, b1, b2, clamp)
184
+ return this
185
+ }
186
+
187
+ static magnify(x: number) {
188
+ return 4 * Math.pow(x - 0.5, 3) + 0.5
189
+ } magnify() {
190
+ this.x = Scalar.magnify(this.x)
191
+ return this
192
+ }
193
+
194
+ static floor(x: number) {
195
+ return Math.floor(x)
196
+ } floor() {
197
+ this.x = Scalar.floor(this.x)
198
+ return this
199
+ }
200
+
201
+ static ceil(x: number) {
202
+ return Math.ceil(x)
203
+ } ceil() {
204
+ this.x = Scalar.ceil(this.x)
205
+ return this
206
+ }
207
+
208
+ static round(x: number) {
209
+ return Math.round(x)
210
+ } round() {
211
+ this.x = Scalar.round(this.x)
212
+ return this
213
+ }
214
+
215
+ static smooth(x: number, y: number, smoothing: number) {
216
+ return smoothing <= 1
217
+ ? y
218
+ : x + ((y - x) / smoothing)
219
+ } smooth(y: number, smoothing: number) {
220
+ this.x = Scalar.smooth(this.x, y, smoothing)
221
+ return this
222
+ }
223
+ }
224
+