@benev/math 0.1.0 → 0.2.0-1
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/README.md +110 -23
- package/package.json +3 -1
- package/s/{concepts → core}/quat.ts +56 -16
- package/s/{concepts → core}/vec2.ts +52 -38
- package/s/{concepts → core}/vec3.ts +57 -47
- package/s/{concepts → core}/vec4.ts +13 -16
- package/s/index.ts +25 -10
- package/s/optimizers/hash-map.ts +71 -0
- package/s/optimizers/hash-set.ts +50 -0
- package/s/optimizers/zen.ts +151 -0
- package/s/physics/2d/collide2d.ts +51 -0
- package/s/physics/2d/intersect2d.ts +80 -0
- package/s/shapes/2d/circle.ts +43 -0
- package/s/shapes/2d/edge.ts +3 -0
- package/s/shapes/2d/index.ts +6 -0
- package/s/shapes/2d/pill.ts +3 -0
- package/s/shapes/2d/rect.ts +74 -0
- package/s/shapes/3d/box.ts +84 -0
- package/s/shapes/3d/capsule.ts +3 -0
- package/s/shapes/3d/index.ts +6 -0
- package/s/shapes/3d/segment.ts +48 -0
- package/s/shapes/3d/sphere.ts +3 -0
- package/s/tools/angles.ts +49 -0
- package/s/{concepts → tools}/circular.ts +5 -3
- package/s/{concepts → tools}/randy.ts +0 -14
- package/s/{concepts → tools}/scalar.ts +11 -11
- package/s/{concepts → tools}/spline.ts +11 -10
- package/x/{concepts → core}/quat.d.ts +10 -5
- package/x/{concepts → core}/quat.js +49 -12
- package/x/core/quat.js.map +1 -0
- package/x/{concepts → core}/vec2.d.ts +16 -17
- package/x/{concepts → core}/vec2.js +37 -21
- package/x/core/vec2.js.map +1 -0
- package/x/{concepts → core}/vec3.d.ts +19 -20
- package/x/{concepts → core}/vec3.js +46 -27
- package/x/core/vec3.js.map +1 -0
- package/x/{concepts → core}/vec4.d.ts +5 -7
- package/x/{concepts → core}/vec4.js +12 -12
- package/x/core/vec4.js.map +1 -0
- package/x/index.d.ts +21 -10
- package/x/index.js +21 -10
- package/x/index.js.map +1 -1
- package/x/optimizers/hash-map.d.ts +17 -0
- package/x/optimizers/hash-map.js +55 -0
- package/x/optimizers/hash-map.js.map +1 -0
- package/x/optimizers/hash-set.d.ts +13 -0
- package/x/optimizers/hash-set.js +39 -0
- package/x/optimizers/hash-set.js.map +1 -0
- package/x/optimizers/zen.d.ts +33 -0
- package/x/optimizers/zen.js +121 -0
- package/x/optimizers/zen.js.map +1 -0
- package/x/physics/2d/collide2d.d.ts +8 -0
- package/x/physics/2d/collide2d.js +36 -0
- package/x/physics/2d/collide2d.js.map +1 -0
- package/x/physics/2d/intersect2d.d.ts +13 -0
- package/x/physics/2d/intersect2d.js +57 -0
- package/x/physics/2d/intersect2d.js.map +1 -0
- package/x/shapes/2d/circle.d.ts +18 -0
- package/x/shapes/2d/circle.js +34 -0
- package/x/shapes/2d/circle.js.map +1 -0
- package/x/shapes/2d/edge.d.ts +1 -0
- package/x/shapes/2d/edge.js +3 -0
- package/x/shapes/2d/edge.js.map +1 -0
- package/x/shapes/2d/index.d.ts +4 -0
- package/x/shapes/2d/index.js +5 -0
- package/x/shapes/2d/index.js.map +1 -0
- package/x/shapes/2d/pill.d.ts +1 -0
- package/x/shapes/2d/pill.js +3 -0
- package/x/shapes/2d/pill.js.map +1 -0
- package/x/shapes/2d/rect.d.ts +23 -0
- package/x/shapes/2d/rect.js +59 -0
- package/x/shapes/2d/rect.js.map +1 -0
- package/x/shapes/3d/box.d.ts +24 -0
- package/x/shapes/3d/box.js +68 -0
- package/x/shapes/3d/box.js.map +1 -0
- package/x/shapes/3d/capsule.d.ts +1 -0
- package/x/shapes/3d/capsule.js +3 -0
- package/x/shapes/3d/capsule.js.map +1 -0
- package/x/shapes/3d/index.d.ts +4 -0
- package/x/shapes/3d/index.js +5 -0
- package/x/shapes/3d/index.js.map +1 -0
- package/x/shapes/3d/segment.d.ts +13 -0
- package/x/shapes/3d/segment.js +37 -0
- package/x/shapes/3d/segment.js.map +1 -0
- package/x/shapes/3d/sphere.d.ts +1 -0
- package/x/shapes/3d/sphere.js +3 -0
- package/x/shapes/3d/sphere.js.map +1 -0
- package/x/{concepts → tools}/angles.d.ts +3 -13
- package/x/tools/angles.js +41 -0
- package/x/tools/angles.js.map +1 -0
- package/x/{concepts → tools}/circular.js +4 -3
- package/x/tools/circular.js.map +1 -0
- package/x/tools/noise.js.map +1 -0
- package/x/{concepts → tools}/randy.d.ts +0 -8
- package/x/{concepts → tools}/randy.js +0 -10
- package/x/tools/randy.js.map +1 -0
- package/x/{concepts → tools}/scalar.d.ts +4 -4
- package/x/{concepts → tools}/scalar.js +11 -11
- package/x/tools/scalar.js.map +1 -0
- package/x/{concepts → tools}/spline.d.ts +3 -3
- package/x/{concepts → tools}/spline.js +3 -1
- package/x/tools/spline.js.map +1 -0
- package/s/concepts/angles.ts +0 -74
- package/x/concepts/angles.js +0 -62
- package/x/concepts/angles.js.map +0 -1
- package/x/concepts/circular.js.map +0 -1
- package/x/concepts/noise.js.map +0 -1
- package/x/concepts/quat.js.map +0 -1
- package/x/concepts/randy.js.map +0 -1
- package/x/concepts/scalar.js.map +0 -1
- package/x/concepts/spline.js.map +0 -1
- package/x/concepts/vec2.js.map +0 -1
- package/x/concepts/vec3.js.map +0 -1
- package/x/concepts/vec4.js.map +0 -1
- /package/s/{concepts → tools}/noise.ts +0 -0
- /package/x/{concepts → tools}/circular.d.ts +0 -0
- /package/x/{concepts → tools}/noise.d.ts +0 -0
- /package/x/{concepts → tools}/noise.js +0 -0
package/README.md
CHANGED
|
@@ -1,26 +1,113 @@
|
|
|
1
1
|
|
|
2
2
|
# @benev/math
|
|
3
|
-
> benevolent's
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
3
|
+
> benevolent's typescript math library for games
|
|
4
|
+
|
|
5
|
+
<br/>
|
|
6
|
+
|
|
7
|
+
## 🍋 CORE
|
|
8
|
+
> common numerical structures
|
|
9
|
+
|
|
10
|
+
> [!TIP]
|
|
11
|
+
> until real docs are written, see the relevant sourcecode in [s/core/](./s/core/)
|
|
12
|
+
|
|
13
|
+
### 🍏 conventions for all core classes
|
|
14
|
+
- **mutable by default.**
|
|
15
|
+
operations happen in-place, for efficiency (we're trying to reduce gc churn).
|
|
16
|
+
```ts
|
|
17
|
+
// allocate a single vector instance
|
|
18
|
+
const vector = new Vec2(0, 0)
|
|
19
|
+
.add({x: 1, y: 2})
|
|
20
|
+
.multiplyBy(2)
|
|
21
|
+
```
|
|
22
|
+
- **explicit cloning.**
|
|
23
|
+
use `.clone()` to avoid mutating the original.
|
|
24
|
+
```ts
|
|
25
|
+
// modify a clone (not the original)
|
|
26
|
+
const vector2 = vector
|
|
27
|
+
.clone()
|
|
28
|
+
.normalize()
|
|
29
|
+
```
|
|
30
|
+
- **underscore-suffixed methods take direct args.**
|
|
31
|
+
- methods normally take in other class instances:
|
|
32
|
+
```ts
|
|
33
|
+
vectorA.add(vectorB)
|
|
34
|
+
```
|
|
35
|
+
- but underscore-suffixed methods take raw naked args (helping you avoid making instances)
|
|
36
|
+
```ts
|
|
37
|
+
vectorA.add_(x, y)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 🍏 Vec2
|
|
41
|
+
### 🍏 Vec3
|
|
42
|
+
### 🍏 Vec4
|
|
43
|
+
### 🍏 Quat
|
|
44
|
+
|
|
45
|
+
<br/>
|
|
46
|
+
|
|
47
|
+
## 🍋 TOOLS
|
|
48
|
+
> handy utilities
|
|
49
|
+
|
|
50
|
+
> [!TIP]
|
|
51
|
+
> until real docs are written, see the relevant sourcecode in [s/tools/](./s/tools/)
|
|
52
|
+
|
|
53
|
+
### 🍏 Scalar
|
|
54
|
+
### 🍏 Circular
|
|
55
|
+
### 🍏 Randy
|
|
56
|
+
### 🍏 Noise
|
|
57
|
+
### 🍏 Spline
|
|
58
|
+
### 🍏 Angles
|
|
59
|
+
- **Radians**
|
|
60
|
+
- **Degrees**
|
|
61
|
+
- **Turns**
|
|
62
|
+
- **Arcseconds**
|
|
63
|
+
|
|
64
|
+
<br/>
|
|
65
|
+
|
|
66
|
+
## 🍋 SHAPES
|
|
67
|
+
> geometric concepts
|
|
68
|
+
|
|
69
|
+
> [!TIP]
|
|
70
|
+
> until real docs are written, see the relevant sourcecode in [s/shapes/](./s/shapes/)
|
|
71
|
+
|
|
72
|
+
### 🍏 2d shapes
|
|
73
|
+
- **Edge** — a line segment
|
|
74
|
+
- **Pill** — a fat line segment (like a sausage)
|
|
75
|
+
- **Rect** — a rectangle
|
|
76
|
+
- **Circle** — a point with a radius
|
|
77
|
+
|
|
78
|
+
### 🍏 3d shapes
|
|
79
|
+
- **Segment** — a line segment
|
|
80
|
+
- **Capsule** — a fat line segment (like a hoagie)
|
|
81
|
+
- **Box** — a cuboid
|
|
82
|
+
- **Sphere** — a point with a radius
|
|
83
|
+
|
|
84
|
+
<br/>
|
|
85
|
+
|
|
86
|
+
## 🍋 OPTIMIZERS
|
|
87
|
+
> spatial optimization data structures
|
|
88
|
+
|
|
89
|
+
> [!TIP]
|
|
90
|
+
> until real docs are written, see the relevant sourcecode in [s/optimizers/](./s/optimizers/)
|
|
91
|
+
|
|
92
|
+
### 🍏 HashMap
|
|
93
|
+
### 🍏 HashSet
|
|
94
|
+
### 🍏 ZenGrid
|
|
95
|
+
|
|
96
|
+
<br/>
|
|
97
|
+
|
|
98
|
+
## 🍋 PHYSICS
|
|
99
|
+
> functionality for doing basic physics
|
|
100
|
+
|
|
101
|
+
> [!TIP]
|
|
102
|
+
> until real docs are written, see the relevant sourcecode in [s/physics/](./s/physics/)
|
|
103
|
+
|
|
104
|
+
### 🍏 collide2d
|
|
105
|
+
### 🍏 collide3d
|
|
106
|
+
### 🍏 intersect2d
|
|
107
|
+
### 🍏 intersect3d
|
|
108
|
+
|
|
109
|
+
<br/>
|
|
110
|
+
|
|
111
|
+
## 👼 https://benevolent.games/
|
|
112
|
+
star this on github if you use it 👍
|
|
26
113
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@benev/math",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-1",
|
|
4
4
|
"description": "game math toolkit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Chase Moskal <chasemoskal@gmail.com>",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
8
9
|
"main": "./x/index.js",
|
|
9
10
|
"exports": {
|
|
10
11
|
".": "./x/index.js"
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
25
|
+
"@e280/stz": "^0.1.0",
|
|
24
26
|
"simplex-noise": "^4.0.3"
|
|
25
27
|
},
|
|
26
28
|
"devDependencies": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import {Xyz} from "./vec3.js"
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type XyzwArray = [x: number, y: number, z: number, w: number]
|
|
5
5
|
export type Xyzw = {x: number, y: number, z: number, w: number}
|
|
6
6
|
|
|
7
7
|
export class Quat {
|
|
@@ -20,18 +20,10 @@ export class Quat {
|
|
|
20
20
|
return new this(0, 0, 0, 1)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
static
|
|
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) {
|
|
23
|
+
static from(q: XyzwArray | Xyzw) {
|
|
32
24
|
return Array.isArray(q)
|
|
33
|
-
? this
|
|
34
|
-
: this
|
|
25
|
+
? new this(...q)
|
|
26
|
+
: new this(q.x, q.y, q.z, q.w)
|
|
35
27
|
}
|
|
36
28
|
|
|
37
29
|
static rotate_(pitch: number, yaw: number, roll: number) {
|
|
@@ -42,7 +34,7 @@ export class Quat {
|
|
|
42
34
|
return this.identity().rotate(vec)
|
|
43
35
|
}
|
|
44
36
|
|
|
45
|
-
|
|
37
|
+
toJSON(): XyzwArray {
|
|
46
38
|
const {x, y, z, w} = this
|
|
47
39
|
return [x, y, z, w]
|
|
48
40
|
}
|
|
@@ -52,13 +44,61 @@ export class Quat {
|
|
|
52
44
|
}
|
|
53
45
|
|
|
54
46
|
clone() {
|
|
55
|
-
return new Quat(...this.
|
|
47
|
+
return new Quat(...this.toJSON())
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
lengthSquared() {
|
|
51
|
+
return this.x*this.x + this.y*this.y + this.z*this.z + this.w*this.w
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
length() {
|
|
55
|
+
return Math.sqrt(this.lengthSquared())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
isUnit(epsilon=1e-6) {
|
|
59
|
+
return Math.abs(this.length() - 1) <= epsilon
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ensureUnit(){
|
|
63
|
+
if (!this.isUnit()) this.normalize()
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
normalize(){
|
|
68
|
+
const m = this.length()
|
|
69
|
+
if (!m) {
|
|
70
|
+
this.x = this.y = this.z = 0
|
|
71
|
+
this.w = 1
|
|
72
|
+
return this
|
|
73
|
+
}
|
|
74
|
+
this.x /= m
|
|
75
|
+
this.y /= m
|
|
76
|
+
this.z /= m
|
|
77
|
+
this.w /= m
|
|
78
|
+
return this
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
conjugate() {
|
|
82
|
+
this.x *= -1
|
|
83
|
+
this.y *= -1
|
|
84
|
+
this.z *= -1
|
|
85
|
+
return this
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
invert() {
|
|
89
|
+
const len2 = this.lengthSquared()
|
|
90
|
+
if (!len2) return this.set_(0, 0, 0, 1)
|
|
91
|
+
this.x = -this.x / len2
|
|
92
|
+
this.y = -this.y / len2
|
|
93
|
+
this.z = -this.z / len2
|
|
94
|
+
this.w = this.w / len2
|
|
95
|
+
return this
|
|
56
96
|
}
|
|
57
97
|
|
|
58
98
|
transform_(x: number, y: number, z: number, w: number, global = false) {
|
|
59
99
|
if (global) {
|
|
60
|
-
const original = this.
|
|
61
|
-
return this.set_(x, y, z, w).
|
|
100
|
+
const original = this.clone()
|
|
101
|
+
return this.set_(x, y, z, w).multiply(original)
|
|
62
102
|
}
|
|
63
103
|
else {
|
|
64
104
|
return this.multiply_(x, y, z, w)
|
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
|
|
2
|
-
import {Scalar} from "
|
|
2
|
+
import {Scalar} from "../tools/scalar.js"
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type XyArray = [x: number, y: number]
|
|
5
5
|
export type Xy = {x: number, y: number}
|
|
6
6
|
|
|
7
|
-
/** https://github.com/microsoft/TypeScript/issues/5863 */
|
|
8
|
-
type TsHack<T> = {new(...a: ConstructorParameters<typeof Vec2>): T}
|
|
9
|
-
|
|
10
7
|
export class Vec2 implements Xy {
|
|
11
8
|
constructor(
|
|
12
9
|
public x: number,
|
|
13
10
|
public y: number,
|
|
14
11
|
) {}
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
static new<T extends Vec2>(this: TsHack<T>, x: number, y: number): T {
|
|
13
|
+
static new(x: number, y: number) {
|
|
19
14
|
return new this(x, y)
|
|
20
15
|
}
|
|
21
16
|
|
|
22
|
-
static zero
|
|
17
|
+
static zero() {
|
|
23
18
|
return new this(0, 0)
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
static all
|
|
21
|
+
static all(value: number) {
|
|
27
22
|
return new this(value, value)
|
|
28
23
|
}
|
|
29
24
|
|
|
30
|
-
static
|
|
31
|
-
return new this(...v)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static import<T extends Vec2>(this: TsHack<T>, {x, y}: Xy) {
|
|
35
|
-
return new this(x, y)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
static from(v: Vec2Array | Xy) {
|
|
25
|
+
static from(v: XyArray | Xy) {
|
|
39
26
|
return Array.isArray(v)
|
|
40
|
-
? this
|
|
41
|
-
: this
|
|
27
|
+
? new this(...v)
|
|
28
|
+
: new this(v.x, v.y)
|
|
42
29
|
}
|
|
43
30
|
|
|
44
31
|
static magnitudeSquared(x: number, y: number) {
|
|
@@ -55,27 +42,37 @@ export class Vec2 implements Xy {
|
|
|
55
42
|
.divideBy(vectors.length)
|
|
56
43
|
}
|
|
57
44
|
|
|
58
|
-
static min(...vecs:
|
|
59
|
-
return new
|
|
45
|
+
static min(...vecs: Xy[]) {
|
|
46
|
+
return new this(
|
|
60
47
|
Math.min(...vecs.map(v => v.x)),
|
|
61
48
|
Math.min(...vecs.map(v => v.y)),
|
|
62
49
|
)
|
|
63
50
|
}
|
|
64
51
|
|
|
65
|
-
static max(...vecs:
|
|
66
|
-
return new
|
|
52
|
+
static max(...vecs: Xy[]) {
|
|
53
|
+
return new this(
|
|
67
54
|
Math.max(...vecs.map(v => v.x)),
|
|
68
55
|
Math.max(...vecs.map(v => v.y)),
|
|
69
56
|
)
|
|
70
57
|
}
|
|
71
58
|
|
|
72
|
-
|
|
59
|
+
static fromAngle(radians: number) {
|
|
60
|
+
return new this(
|
|
61
|
+
Math.cos(radians),
|
|
62
|
+
Math.sin(radians),
|
|
63
|
+
)
|
|
64
|
+
}
|
|
73
65
|
|
|
74
66
|
clone() {
|
|
75
67
|
return new Vec2(this.x, this.y)
|
|
76
68
|
}
|
|
77
69
|
|
|
78
|
-
|
|
70
|
+
*[Symbol.iterator]() {
|
|
71
|
+
yield this.x
|
|
72
|
+
yield this.y
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
toJSON(): XyArray {
|
|
79
76
|
return [this.x, this.y]
|
|
80
77
|
}
|
|
81
78
|
|
|
@@ -97,8 +94,6 @@ export class Vec2 implements Xy {
|
|
|
97
94
|
return this
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
///////////////////////////////////////////////////////////////////////
|
|
101
|
-
|
|
102
97
|
magnitudeSquared() {
|
|
103
98
|
return Vec2.magnitudeSquared(this.x, this.y)
|
|
104
99
|
}
|
|
@@ -122,6 +117,17 @@ export class Vec2 implements Xy {
|
|
|
122
117
|
return vecs.every(({x, y}) => this.equals_(x, y))
|
|
123
118
|
}
|
|
124
119
|
|
|
120
|
+
near_(x: number, y: number, epsilon = 1e-6) {
|
|
121
|
+
return (
|
|
122
|
+
Math.abs(this.x - x) <= epsilon &&
|
|
123
|
+
Math.abs(this.y - y) <= epsilon
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
near({x, y}: Xy, epsilon = 1e-6) {
|
|
128
|
+
return this.near_(x, y, epsilon)
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
dot_(x: number, y: number) {
|
|
126
132
|
return (this.x * x) + (this.y * y)
|
|
127
133
|
}
|
|
@@ -151,15 +157,15 @@ export class Vec2 implements Xy {
|
|
|
151
157
|
angleBetween_(x: number, y: number) {
|
|
152
158
|
const dot = this.dot_(x, y)
|
|
153
159
|
const magnitudes = this.magnitude() * Vec2.magnitude(x, y)
|
|
154
|
-
|
|
160
|
+
if (magnitudes === 0) return 0
|
|
161
|
+
const ratio = Scalar.clamp(dot / magnitudes, -1, 1)
|
|
162
|
+
return Math.acos(ratio)
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
angleBetween({x, y}: Xy) {
|
|
158
166
|
return this.angleBetween_(x, y)
|
|
159
167
|
}
|
|
160
168
|
|
|
161
|
-
///////////////////////////////////////////////////////////////////////
|
|
162
|
-
|
|
163
169
|
/** mutator */
|
|
164
170
|
normalize() {
|
|
165
171
|
return this.divideBy(this.magnitude())
|
|
@@ -233,9 +239,17 @@ export class Vec2 implements Xy {
|
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
/** mutator */
|
|
236
|
-
clamp(min:
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
clamp(min: Xy, max: Xy) {
|
|
243
|
+
this.x = Scalar.clamp(this.x, min.x, max.x)
|
|
244
|
+
this.y = Scalar.clamp(this.y, min.y, max.y)
|
|
245
|
+
return this
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** mutator */
|
|
249
|
+
clampBy(min: number, max: number) {
|
|
250
|
+
this.x = Scalar.clamp(this.x, min, max)
|
|
251
|
+
this.y = Scalar.clamp(this.y, min, max)
|
|
252
|
+
return this
|
|
239
253
|
}
|
|
240
254
|
|
|
241
255
|
/** mutator */
|
|
@@ -315,8 +329,8 @@ export class Vec2 implements Xy {
|
|
|
315
329
|
|
|
316
330
|
/** mutator */
|
|
317
331
|
divide_(x: number, y: number) {
|
|
318
|
-
this.x /= x
|
|
319
|
-
this.y /= y
|
|
332
|
+
if (x !== 0) this.x /= x
|
|
333
|
+
if (y !== 0) this.y /= y
|
|
320
334
|
return this
|
|
321
335
|
}
|
|
322
336
|
|
|
@@ -373,7 +387,7 @@ export class Vec2 implements Xy {
|
|
|
373
387
|
}
|
|
374
388
|
|
|
375
389
|
/** mutator */
|
|
376
|
-
rotateAroundPoint({x, y}:
|
|
390
|
+
rotateAroundPoint({x, y}: Xy, radians: number) {
|
|
377
391
|
return this.rotateAroundPoint_(x, y, radians)
|
|
378
392
|
}
|
|
379
393
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
import {Scalar} from "
|
|
2
|
+
import {Scalar} from "../tools/scalar.js"
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type XyzArray = [x: number, y: number, z: number]
|
|
5
5
|
export type Xyz = {x: number, y: number, z: number}
|
|
6
6
|
|
|
7
|
-
/** https://github.com/microsoft/TypeScript/issues/5863 */
|
|
8
|
-
type TsHack<T> = {new(...a: ConstructorParameters<typeof Vec3>): T}
|
|
9
|
-
|
|
10
7
|
export class Vec3 {
|
|
11
8
|
constructor(
|
|
12
9
|
public x: number,
|
|
@@ -14,32 +11,22 @@ export class Vec3 {
|
|
|
14
11
|
public z: number,
|
|
15
12
|
) {}
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
static new<T extends Vec3>(this: TsHack<T>, x: number, y: number, z: number) {
|
|
14
|
+
static new(x: number, y: number, z: number) {
|
|
20
15
|
return new this(x, y, z)
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
static zero
|
|
18
|
+
static zero() {
|
|
24
19
|
return new this(0, 0, 0)
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
static all
|
|
22
|
+
static all(value: number) {
|
|
28
23
|
return new this(value, value, value)
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
static
|
|
32
|
-
return new this(...v)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
static import<T extends Vec3>(this: TsHack<T>, {x, y, z}: Xyz): Vec3 {
|
|
36
|
-
return new this(x, y, z)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static from(v: Vec3Array | Xyz) {
|
|
26
|
+
static from(v: XyzArray | Xyz) {
|
|
40
27
|
return Array.isArray(v)
|
|
41
|
-
? this
|
|
42
|
-
: this
|
|
28
|
+
? new this(...v)
|
|
29
|
+
: new this(v.x, v.y, v.z)
|
|
43
30
|
}
|
|
44
31
|
|
|
45
32
|
static magnitudeSquared(x: number, y: number, z: number) {
|
|
@@ -56,23 +43,23 @@ export class Vec3 {
|
|
|
56
43
|
.divideBy(vecs.length)
|
|
57
44
|
}
|
|
58
45
|
|
|
59
|
-
static min(...vecs:
|
|
60
|
-
return new
|
|
46
|
+
static min(...vecs: Xyz[]) {
|
|
47
|
+
return new this(
|
|
61
48
|
Math.min(...vecs.map(v => v.x)),
|
|
62
49
|
Math.min(...vecs.map(v => v.y)),
|
|
63
50
|
Math.min(...vecs.map(v => v.z)),
|
|
64
51
|
)
|
|
65
52
|
}
|
|
66
53
|
|
|
67
|
-
static max(...vecs:
|
|
68
|
-
return new
|
|
54
|
+
static max(...vecs: Xyz[]) {
|
|
55
|
+
return new this(
|
|
69
56
|
Math.max(...vecs.map(v => v.x)),
|
|
70
57
|
Math.max(...vecs.map(v => v.y)),
|
|
71
58
|
Math.max(...vecs.map(v => v.z)),
|
|
72
59
|
)
|
|
73
60
|
}
|
|
74
61
|
|
|
75
|
-
static
|
|
62
|
+
static fromHex(hex: string): Vec3 {
|
|
76
63
|
if (hex.startsWith("#") && hex.length === 7) {
|
|
77
64
|
const r = parseInt(hex.slice(1, 3), 16) / 255
|
|
78
65
|
const g = parseInt(hex.slice(3, 5), 16) / 255
|
|
@@ -83,13 +70,17 @@ export class Vec3 {
|
|
|
83
70
|
}
|
|
84
71
|
}
|
|
85
72
|
|
|
86
|
-
///////////////////////////////////////////////////////////////////////
|
|
87
|
-
|
|
88
73
|
clone() {
|
|
89
74
|
return new Vec3(this.x, this.y, this.z)
|
|
90
75
|
}
|
|
91
76
|
|
|
92
|
-
|
|
77
|
+
*[Symbol.iterator]() {
|
|
78
|
+
yield this.x
|
|
79
|
+
yield this.y
|
|
80
|
+
yield this.z
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toJSON(): XyzArray {
|
|
93
84
|
return [this.x, this.y, this.z]
|
|
94
85
|
}
|
|
95
86
|
|
|
@@ -111,8 +102,6 @@ export class Vec3 {
|
|
|
111
102
|
return this
|
|
112
103
|
}
|
|
113
104
|
|
|
114
|
-
///////////////////////////////////////////////////////////////////////
|
|
115
|
-
|
|
116
105
|
magnitudeSquared() {
|
|
117
106
|
return Vec3.magnitudeSquared(this.x, this.y, this.z)
|
|
118
107
|
}
|
|
@@ -121,14 +110,13 @@ export class Vec3 {
|
|
|
121
110
|
return Vec3.magnitude(this.x, this.y, this.z)
|
|
122
111
|
}
|
|
123
112
|
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
/** rgb values from 0-1 */
|
|
114
|
+
toHex() {
|
|
115
|
+
const to255 = (val: number) => Math.round(Scalar.clamp(val * 255, 0, 255))
|
|
126
116
|
const toHex = (val: number) => to255(val).toString(16).padStart(2, '0')
|
|
127
117
|
return `#${toHex(this.x)}${toHex(this.y)}${toHex(this.z)}`
|
|
128
118
|
}
|
|
129
119
|
|
|
130
|
-
///////////////////////////////////////////////////////////////////////
|
|
131
|
-
|
|
132
120
|
equals_(x: number, y: number, z: number) {
|
|
133
121
|
return (
|
|
134
122
|
this.x === x &&
|
|
@@ -138,7 +126,19 @@ export class Vec3 {
|
|
|
138
126
|
}
|
|
139
127
|
|
|
140
128
|
equals(...vecs: Xyz[]) {
|
|
141
|
-
return vecs.every(
|
|
129
|
+
return vecs.every(({x, y, z}) => this.equals_(x, y, z))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
near_(x: number, y: number, z: number, epsilon = 1e-6) {
|
|
133
|
+
return (
|
|
134
|
+
Math.abs(this.x - x) <= epsilon &&
|
|
135
|
+
Math.abs(this.y - y) <= epsilon &&
|
|
136
|
+
Math.abs(this.z - z) <= epsilon
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
near({x, y, z}: Xyz, epsilon = 1e-6) {
|
|
141
|
+
return this.near_(x, y, z, epsilon)
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
distanceSquared_(x: number, y: number, z: number) {
|
|
@@ -179,16 +179,16 @@ export class Vec3 {
|
|
|
179
179
|
|
|
180
180
|
angleBetween_(x: number, y: number, z: number) {
|
|
181
181
|
const dotProduct = this.dot_(x, y, z)
|
|
182
|
-
const magnitudes = this.magnitude() * Vec3.
|
|
183
|
-
|
|
182
|
+
const magnitudes = this.magnitude() * Vec3.magnitude(x, y, z)
|
|
183
|
+
if (magnitudes === 0) return 0
|
|
184
|
+
const ratio = Scalar.clamp(dotProduct / magnitudes, -1, 1)
|
|
185
|
+
return Math.acos(ratio)
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
angleBetween({x, y, z}: Xyz) {
|
|
187
189
|
return this.angleBetween_(x, y, z)
|
|
188
190
|
}
|
|
189
191
|
|
|
190
|
-
///////////////////////////////////////////////////////////////////////
|
|
191
|
-
|
|
192
192
|
/** mutator */
|
|
193
193
|
add_(x: number, y: number, z: number) {
|
|
194
194
|
this.x += x
|
|
@@ -226,21 +226,21 @@ export class Vec3 {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/** mutator */
|
|
229
|
-
multiply(...vecs:
|
|
229
|
+
multiply(...vecs: Xyz[]) {
|
|
230
230
|
for (const {x, y, z} of vecs) this.multiply_(x, y, z)
|
|
231
231
|
return this
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
/** mutator */
|
|
235
235
|
divide_(x: number, y: number, z: number) {
|
|
236
|
-
this.x /= x
|
|
237
|
-
this.y /= y
|
|
238
|
-
this.z /= z
|
|
236
|
+
if (x !== 0) this.x /= x
|
|
237
|
+
if (y !== 0) this.y /= y
|
|
238
|
+
if (z !== 0) this.z /= z
|
|
239
239
|
return this
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
/** mutator */
|
|
243
|
-
divide(...vecs:
|
|
243
|
+
divide(...vecs: Xyz[]) {
|
|
244
244
|
for (const {x, y, z} of vecs) this.divide_(x, y, z)
|
|
245
245
|
return this
|
|
246
246
|
}
|
|
@@ -286,6 +286,14 @@ export class Vec3 {
|
|
|
286
286
|
return this
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
/** mutator */
|
|
290
|
+
subtractBy(delta: number) {
|
|
291
|
+
this.x -= delta
|
|
292
|
+
this.y -= delta
|
|
293
|
+
this.z -= delta
|
|
294
|
+
return this
|
|
295
|
+
}
|
|
296
|
+
|
|
289
297
|
/** mutator */
|
|
290
298
|
multiplyBy(delta: number) {
|
|
291
299
|
this.x *= delta
|
|
@@ -381,7 +389,9 @@ export class Vec3 {
|
|
|
381
389
|
|
|
382
390
|
/** mutator */
|
|
383
391
|
projectOnto_(x: number, y: number, z: number) {
|
|
384
|
-
const
|
|
392
|
+
const m2 = Vec3.magnitudeSquared(x, y, z)
|
|
393
|
+
if (!m2) return this
|
|
394
|
+
const scalar = this.dot_(x, y, z) / m2
|
|
385
395
|
this.x = scalar * x
|
|
386
396
|
this.y = scalar * y
|
|
387
397
|
this.z = scalar * z
|
|
@@ -389,7 +399,7 @@ export class Vec3 {
|
|
|
389
399
|
}
|
|
390
400
|
|
|
391
401
|
/** mutator */
|
|
392
|
-
projectOnto({x, y, z}:
|
|
402
|
+
projectOnto({x, y, z}: Xyz) {
|
|
393
403
|
return this.projectOnto_(x, y, z)
|
|
394
404
|
}
|
|
395
405
|
|