@gitborlando/geo 4.1.1 → 5.2.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/dist/index.d.ts +47 -149
- package/dist/index.js +180 -397
- package/package.json +9 -6
- package/src/aabb.ts +56 -12
- package/src/angle.ts +19 -23
- package/src/index.ts +1 -3
- package/src/misc.ts +7 -0
- package/src/obb.ts +48 -25
- package/src/xy.ts +64 -143
- package/src/__test__/aabb.test.ts +0 -84
- package/src/__test__/angle.test.ts +0 -58
- package/src/__test__/index.test.ts +0 -85
- package/src/__test__/math.test.ts +0 -61
- package/src/__test__/matrix.test.ts +0 -73
- package/src/__test__/obb.test.ts +0 -95
- package/src/__test__/points-of-bezier.test.ts +0 -103
- package/src/__test__/types.test.ts +0 -31
- package/src/__test__/xy.test.ts +0 -143
- package/src/math.ts +0 -26
- package/src/matrix.ts +0 -48
- package/src/points-of-bezier.ts +0 -156
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitborlando/geo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"author": "",
|
|
22
22
|
"license": "ISC",
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@vitest/ui": "^3.2.4",
|
|
25
|
-
"vitest": "^3.2.4",
|
|
26
24
|
"@changesets/cli": "^2.29.5",
|
|
27
25
|
"@types/node": "^24.0.15",
|
|
28
|
-
"
|
|
26
|
+
"@vitest/ui": "^3.2.4",
|
|
29
27
|
"pretty-quick": "^4.2.2",
|
|
30
28
|
"tsup": "^8.5.0",
|
|
31
29
|
"tsx": "^4.20.3",
|
|
32
|
-
"typescript": "^5.8.3"
|
|
30
|
+
"typescript": "^5.8.3",
|
|
31
|
+
"vitest": "^3.2.4",
|
|
32
|
+
"simple-git-hooks": "^2.13.1"
|
|
33
33
|
},
|
|
34
34
|
"prettier": {
|
|
35
35
|
"printWidth": 85,
|
|
@@ -40,10 +40,13 @@
|
|
|
40
40
|
"singleQuote": true,
|
|
41
41
|
"jsxSingleQuote": true
|
|
42
42
|
},
|
|
43
|
+
"simple-git-hooks": {
|
|
44
|
+
"pre-commit": "npx pretty-quick --staged"
|
|
45
|
+
},
|
|
43
46
|
"scripts": {
|
|
44
47
|
"dev": "tsup --watch",
|
|
45
48
|
"build": "tsup",
|
|
46
|
-
"
|
|
49
|
+
"re-i": "rm -rf node_modules pnpm-lock.yaml",
|
|
47
50
|
"test": "vitest",
|
|
48
51
|
"test:run": "vitest run",
|
|
49
52
|
"test:ui": "vitest --ui"
|
package/src/aabb.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { max, min } from './math'
|
|
2
1
|
import { OBB } from './obb'
|
|
3
|
-
import { IRectWithCenter } from './types'
|
|
2
|
+
import { IRect, IRectWithCenter, IXY } from './types'
|
|
4
3
|
import { XY } from './xy'
|
|
5
4
|
|
|
6
5
|
export class AABB {
|
|
@@ -31,6 +30,32 @@ export class AABB {
|
|
|
31
30
|
] as const
|
|
32
31
|
}
|
|
33
32
|
|
|
33
|
+
static update(aabb: AABB, minX: number, minY: number, maxX: number, maxY: number) {
|
|
34
|
+
aabb.minX = minX
|
|
35
|
+
aabb.minY = minY
|
|
36
|
+
aabb.maxX = maxX
|
|
37
|
+
aabb.maxY = maxY
|
|
38
|
+
return aabb
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static updateFromRect(aabb: AABB, rect: IRect) {
|
|
42
|
+
return AABB.update(
|
|
43
|
+
aabb,
|
|
44
|
+
rect.x,
|
|
45
|
+
rect.y,
|
|
46
|
+
rect.x + rect.width,
|
|
47
|
+
rect.y + rect.height,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static shift(aabb: AABB, delta: IXY) {
|
|
52
|
+
aabb.minX += delta.x
|
|
53
|
+
aabb.minY += delta.y
|
|
54
|
+
aabb.maxX += delta.x
|
|
55
|
+
aabb.maxY += delta.y
|
|
56
|
+
return aabb
|
|
57
|
+
}
|
|
58
|
+
|
|
34
59
|
static collide(one: AABB, another: AABB): boolean {
|
|
35
60
|
return (
|
|
36
61
|
one.minX <= another.maxX &&
|
|
@@ -74,20 +99,29 @@ export class AABB {
|
|
|
74
99
|
}
|
|
75
100
|
}
|
|
76
101
|
|
|
77
|
-
static merge(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
static merge(aabbList: AABB[] | Set<AABB>) {
|
|
103
|
+
if (Array.isArray(aabbList)) {
|
|
104
|
+
if (aabbList.length === 0) return new AABB(0, 0, 0, 0)
|
|
105
|
+
} else {
|
|
106
|
+
if (aabbList.size === 0) return new AABB(0, 0, 0, 0)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let xMin = Infinity,
|
|
110
|
+
yMin = Infinity,
|
|
111
|
+
xMax = -Infinity,
|
|
112
|
+
yMax = -Infinity
|
|
113
|
+
for (const aabb of aabbList) {
|
|
114
|
+
xMin = Math.min(xMin, aabb.minX)
|
|
115
|
+
yMin = Math.min(yMin, aabb.minY)
|
|
116
|
+
xMax = Math.max(xMax, aabb.maxX)
|
|
117
|
+
yMax = Math.max(yMax, aabb.maxY)
|
|
118
|
+
}
|
|
85
119
|
return new AABB(xMin, yMin, xMax, yMax)
|
|
86
120
|
}
|
|
87
121
|
|
|
88
122
|
static fromOBB(obb: OBB) {
|
|
89
|
-
const width = obb.
|
|
90
|
-
const height = obb.
|
|
123
|
+
const width = obb.projectAt(XY.$(1, 0))
|
|
124
|
+
const height = obb.projectAt(XY.$(0, 1))
|
|
91
125
|
return new AABB(
|
|
92
126
|
obb.center.x - width / 2,
|
|
93
127
|
obb.center.y - height / 2,
|
|
@@ -95,4 +129,14 @@ export class AABB {
|
|
|
95
129
|
obb.center.y + height / 2,
|
|
96
130
|
)
|
|
97
131
|
}
|
|
132
|
+
|
|
133
|
+
static updateFromOBB(aabb: AABB, obb: OBB) {
|
|
134
|
+
const width = obb.projectAt(XY.$(1, 0))
|
|
135
|
+
const height = obb.projectAt(XY.$(0, 1))
|
|
136
|
+
aabb.minX = obb.center.x - width / 2
|
|
137
|
+
aabb.minY = obb.center.y - height / 2
|
|
138
|
+
aabb.maxX = obb.center.x + width / 2
|
|
139
|
+
aabb.maxY = obb.center.y + height / 2
|
|
140
|
+
return aabb
|
|
141
|
+
}
|
|
98
142
|
}
|
package/src/angle.ts
CHANGED
|
@@ -1,32 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
import { IXY } from './types'
|
|
2
|
+
import { XY } from './xy'
|
|
2
3
|
|
|
3
4
|
export class Angle {
|
|
4
5
|
static cos(angle: number) {
|
|
5
|
-
return cos(Angle.radianFy(angle))
|
|
6
|
+
return Math.cos(Angle.radianFy(angle))
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
static sin(angle: number) {
|
|
9
|
-
return sin(Angle.radianFy(angle))
|
|
10
|
+
return Math.sin(Angle.radianFy(angle))
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
static
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
static acos(angle: number) {
|
|
17
|
-
return Angle.angleFy(acos(Angle.radianFy(angle)))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
static asin(angle: number) {
|
|
21
|
-
return Angle.angleFy(asin(Angle.radianFy(angle)))
|
|
13
|
+
static cosSin(angle: number) {
|
|
14
|
+
const radians = Angle.radianFy(angle)
|
|
15
|
+
return { cos: Math.cos(radians), sin: Math.sin(radians) }
|
|
22
16
|
}
|
|
23
17
|
|
|
24
|
-
static
|
|
25
|
-
return
|
|
18
|
+
static tan(angle: number) {
|
|
19
|
+
return Math.tan(Angle.radianFy(angle))
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
static atan2(y: number, x: number) {
|
|
29
|
-
return Angle.angleFy(atan2(y, x))
|
|
23
|
+
return Angle.angleFy(Math.atan2(y, x))
|
|
30
24
|
}
|
|
31
25
|
|
|
32
26
|
static angleFy(radians: number) {
|
|
@@ -38,18 +32,20 @@ export class Angle {
|
|
|
38
32
|
}
|
|
39
33
|
|
|
40
34
|
static normal(angle: number) {
|
|
41
|
-
return (angle + 360) % 360
|
|
35
|
+
return ((angle % 360) + 360) % 360
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static minor(angle: number) {
|
|
39
|
+
return Math.min(angle, 360 - angle)
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
static snap(angle: number, step = 90) {
|
|
45
43
|
return Angle.normal(Math.round(angle / step) * step)
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
static
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
y: (ax - ox) * sin(radian) + (ay - oy) * cos(radian) + oy,
|
|
53
|
-
}
|
|
46
|
+
static sweep(v1: IXY, v2: IXY = XY.xAxis(), clockwise = false) {
|
|
47
|
+
const dot = v1.x * v2.x + v1.y * v2.y
|
|
48
|
+
const det = v1.x * v2.y - v1.y * v2.x
|
|
49
|
+
return Angle.normal(Angle.atan2(det, dot) * (clockwise ? 1 : -1))
|
|
54
50
|
}
|
|
55
51
|
}
|
package/src/index.ts
CHANGED
package/src/misc.ts
ADDED
package/src/obb.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AABB } from './aabb'
|
|
2
2
|
import { Angle } from './angle'
|
|
3
3
|
import { IRect, IXY } from './types'
|
|
4
|
-
import { XY
|
|
4
|
+
import { XY } from './xy'
|
|
5
5
|
|
|
6
6
|
type IAxis = { widthAxis: IXY; heightAxis: IXY }
|
|
7
7
|
|
|
@@ -25,19 +25,18 @@ export class OBB {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
get xy() {
|
|
28
|
-
return
|
|
28
|
+
return XY.$(this.x, this.y)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
#calcCenter = () => {
|
|
32
|
-
const center =
|
|
33
|
-
return
|
|
32
|
+
const center = XY.center(this).plus(this.xy)
|
|
33
|
+
return center.rotate(this.xy, this.rotation)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
#calcAxis = () => {
|
|
37
|
-
const cos = Angle.
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const heightAxis = xy_(sin, cos)
|
|
37
|
+
const { cos, sin } = Angle.cosSin(this.rotation)
|
|
38
|
+
const widthAxis = XY.$(cos, -sin)
|
|
39
|
+
const heightAxis = XY.$(sin, cos)
|
|
41
40
|
return (this.axis = { widthAxis, heightAxis })
|
|
42
41
|
}
|
|
43
42
|
|
|
@@ -48,45 +47,69 @@ export class OBB {
|
|
|
48
47
|
const sinWidth = sin * this.width
|
|
49
48
|
const cosHeight = cos * this.height
|
|
50
49
|
const sinHeight = sin * this.height
|
|
51
|
-
const TL =
|
|
52
|
-
const TR =
|
|
53
|
-
const BR =
|
|
54
|
-
const BL =
|
|
50
|
+
const TL = XY.$(this.x, this.y)
|
|
51
|
+
const TR = XY.$(this.x + cosWidth, this.y + sinWidth)
|
|
52
|
+
const BR = XY.$(this.x + cosWidth - sinHeight, this.y + sinWidth + cosHeight)
|
|
53
|
+
const BL = XY.$(this.x - sinHeight, this.y + cosHeight)
|
|
55
54
|
return (this.vertexes = [TL, TR, BR, BL])
|
|
56
55
|
}
|
|
57
56
|
|
|
57
|
+
plain = () => {
|
|
58
|
+
return {
|
|
59
|
+
x: this.x,
|
|
60
|
+
y: this.y,
|
|
61
|
+
width: this.width,
|
|
62
|
+
height: this.height,
|
|
63
|
+
rotation: this.rotation,
|
|
64
|
+
center: this.center,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
clone = () => {
|
|
59
69
|
return new OBB(this.x, this.y, this.width, this.height, this.rotation)
|
|
60
70
|
}
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
shift = (delta: IXY) => {
|
|
73
|
+
this.x += delta.x
|
|
74
|
+
this.y += delta.y
|
|
75
|
+
this.center.x += delta.x
|
|
76
|
+
this.center.y += delta.y
|
|
77
|
+
this.vertexes.forEach((vertex) => {
|
|
78
|
+
vertex.x += delta.x
|
|
79
|
+
vertex.y += delta.y
|
|
80
|
+
})
|
|
81
|
+
AABB.updateFromOBB(this.aabb, this)
|
|
82
|
+
return this
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
projectAt = (anotherAxis: IXY) => {
|
|
63
86
|
const { widthAxis, heightAxis } = this.axis
|
|
64
87
|
return (
|
|
65
|
-
Math.abs(
|
|
66
|
-
Math.abs(
|
|
88
|
+
Math.abs(XY.dot(widthAxis, anotherAxis)) * this.width +
|
|
89
|
+
Math.abs(XY.dot(heightAxis, anotherAxis)) * this.height
|
|
67
90
|
)
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
collide = (another: OBB) => {
|
|
71
|
-
const centerVector =
|
|
94
|
+
const centerVector = XY.vector(this.center, another.center)
|
|
72
95
|
if (
|
|
73
|
-
this.
|
|
74
|
-
2 * Math.abs(
|
|
96
|
+
this.projectAt(another.axis.widthAxis) + another.width <=
|
|
97
|
+
2 * Math.abs(XY.dot(centerVector, another.axis.widthAxis))
|
|
75
98
|
)
|
|
76
99
|
return false
|
|
77
100
|
if (
|
|
78
|
-
this.
|
|
79
|
-
2 * Math.abs(
|
|
101
|
+
this.projectAt(another.axis.heightAxis) + another.height <=
|
|
102
|
+
2 * Math.abs(XY.dot(centerVector, another.axis.heightAxis))
|
|
80
103
|
)
|
|
81
104
|
return false
|
|
82
105
|
if (
|
|
83
|
-
another.
|
|
84
|
-
2 * Math.abs(
|
|
106
|
+
another.projectAt(this.axis.widthAxis) + this.width <=
|
|
107
|
+
2 * Math.abs(XY.dot(centerVector, this.axis.widthAxis))
|
|
85
108
|
)
|
|
86
109
|
return false
|
|
87
110
|
if (
|
|
88
|
-
another.
|
|
89
|
-
2 * Math.abs(
|
|
111
|
+
another.projectAt(this.axis.heightAxis) + this.height <=
|
|
112
|
+
2 * Math.abs(XY.dot(centerVector, this.axis.heightAxis))
|
|
90
113
|
)
|
|
91
114
|
return false
|
|
92
115
|
return true
|
|
@@ -104,7 +127,7 @@ export class OBB {
|
|
|
104
127
|
static fromCenter(center: IXY, width: number, height: number, rotation = 0) {
|
|
105
128
|
const dx = center.x - width / 2
|
|
106
129
|
const dy = center.y - height / 2
|
|
107
|
-
const xy = XY.
|
|
130
|
+
const xy = XY.from(dx, dy).rotate(center, rotation)
|
|
108
131
|
return new OBB(xy.x, xy.y, width, height, rotation)
|
|
109
132
|
}
|
|
110
133
|
|
package/src/xy.ts
CHANGED
|
@@ -1,108 +1,13 @@
|
|
|
1
1
|
import { Angle } from './angle'
|
|
2
|
-
import { sqrt } from './math'
|
|
3
2
|
import { IXY } from './types'
|
|
4
3
|
|
|
5
|
-
export function xy_(x: number = 0, y: number = 0) {
|
|
6
|
-
return { x, y }
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function xy_client(e: any) {
|
|
10
|
-
return { x: e.clientX, y: e.clientY }
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function xy_from(xy: IXY) {
|
|
14
|
-
return { x: xy.x, y: xy.y }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function xy_center(xy: { centerX: number; centerY: number }) {
|
|
18
|
-
return { x: xy.centerX, y: xy.centerY }
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function xy_mutate(self: IXY, another: IXY) {
|
|
22
|
-
self.x = another.x
|
|
23
|
-
self.y = another.y
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function xy_plus(self: IXY, another: IXY) {
|
|
27
|
-
return { x: self.x + another.x, y: self.y + another.y }
|
|
28
|
-
}
|
|
29
|
-
export function xy_plus_mutate(self: IXY, another: IXY) {
|
|
30
|
-
self.x = self.x + another.x
|
|
31
|
-
self.y = self.y + another.y
|
|
32
|
-
}
|
|
33
|
-
export function xy_plus_all(...xys: IXY[]) {
|
|
34
|
-
return xys.reduce((a, b) => xy_plus(a, b))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function xy_minus(self: IXY, another: IXY) {
|
|
38
|
-
return { x: self.x - another.x, y: self.y - another.y }
|
|
39
|
-
}
|
|
40
|
-
export function xy_minus_mutate(self: IXY, another: IXY) {
|
|
41
|
-
self.x = self.x - another.x
|
|
42
|
-
self.y = self.y - another.y
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function xy_multiply(self: IXY, ...numbers: number[]) {
|
|
46
|
-
const n = numbers.reduce((a, b) => a * b, 1)
|
|
47
|
-
return { x: self.x * n, y: self.y * n }
|
|
48
|
-
}
|
|
49
|
-
export function xy_multiply_mutate(self: IXY, ...numbers: number[]) {
|
|
50
|
-
const n = numbers.reduce((a, b) => a * b, 1)
|
|
51
|
-
self.x = self.x * n
|
|
52
|
-
self.y = self.y * n
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function xy_divide(self: IXY, ...numbers: number[]) {
|
|
56
|
-
const n = numbers.reduce((a, b) => a * b, 1)
|
|
57
|
-
return { x: self.x / n, y: self.y / n }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function xy_distance(self: IXY, another: IXY = xy_(0, 0)) {
|
|
61
|
-
return Math.sqrt((self.x - another.x) ** 2 + (self.y - another.y) ** 2)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function xy_rotate(self: IXY, origin: IXY, rotation: number) {
|
|
65
|
-
if (rotation === 0) return self
|
|
66
|
-
return Angle.rotatePoint(self.x, self.y, origin.x, origin.y, rotation)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function xy_dot(self: IXY, another: IXY) {
|
|
70
|
-
return self.x * another.x + self.y * another.y
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function xy_symmetric(self: IXY, origin: IXY) {
|
|
74
|
-
return { x: 2 * origin.x - self.x, y: 2 * origin.y - self.y }
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function xy_opposite(self: IXY) {
|
|
78
|
-
return { x: -self.x, y: -self.y }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function xy_getRotation(self: IXY, another: IXY, origin: IXY) {
|
|
82
|
-
return Angle.angleFy(
|
|
83
|
-
Math.atan2(self.y - origin.y, self.x - origin.x) -
|
|
84
|
-
Math.atan2(another.y - origin.y, another.x - origin.x),
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function xy_toArray(self: IXY) {
|
|
89
|
-
return [self.x, self.y] as [number, number]
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function xy_xAxis(rotation: number) {
|
|
93
|
-
return { x: Angle.cos(rotation), y: Angle.sin(rotation) }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function xy_yAxis(rotation: number) {
|
|
97
|
-
return { x: -Angle.sin(rotation), y: Angle.cos(rotation) }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
4
|
export class XY {
|
|
101
5
|
constructor(
|
|
102
6
|
public x: number,
|
|
103
7
|
public y: number,
|
|
104
8
|
) {}
|
|
105
|
-
|
|
9
|
+
|
|
10
|
+
$() {
|
|
106
11
|
return { x: this.x, y: this.y }
|
|
107
12
|
}
|
|
108
13
|
|
|
@@ -111,91 +16,107 @@ export class XY {
|
|
|
111
16
|
}
|
|
112
17
|
|
|
113
18
|
plus(...others: IXY[]) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
19
|
+
this.x = others.reduce((sum, cur) => sum + cur.x, this.x)
|
|
20
|
+
this.y = others.reduce((sum, cur) => sum + cur.y, this.y)
|
|
21
|
+
return this
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
plusNum(num: number) {
|
|
25
|
+
this.x += num
|
|
26
|
+
this.y += num
|
|
27
|
+
return this
|
|
117
28
|
}
|
|
118
29
|
|
|
119
30
|
minus(...others: IXY[]) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return
|
|
31
|
+
this.x = others.reduce((sum, cur) => sum - cur.x, this.x)
|
|
32
|
+
this.y = others.reduce((sum, cur) => sum - cur.y, this.y)
|
|
33
|
+
return this
|
|
123
34
|
}
|
|
124
35
|
|
|
125
36
|
multiply(...numbers: number[]) {
|
|
126
37
|
const n = numbers.reduce((a, b) => a * b, 1)
|
|
127
|
-
|
|
38
|
+
this.x *= n
|
|
39
|
+
this.y *= n
|
|
40
|
+
return this
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
multiplyNum(num: number) {
|
|
44
|
+
this.x *= num
|
|
45
|
+
this.y *= num
|
|
46
|
+
return this
|
|
128
47
|
}
|
|
129
48
|
|
|
130
49
|
divide(...numbers: number[]) {
|
|
131
50
|
const n = numbers.reduce((a, b) => a * b, 1)
|
|
132
|
-
|
|
51
|
+
this.x /= n
|
|
52
|
+
this.y /= n
|
|
53
|
+
return this
|
|
133
54
|
}
|
|
134
55
|
|
|
135
56
|
rotate(origin: IXY, rotation: number) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return
|
|
57
|
+
const { cos, sin } = Angle.cosSin(rotation)
|
|
58
|
+
const dx = this.x - origin.x
|
|
59
|
+
const dy = this.y - origin.y
|
|
60
|
+
this.x = dx * cos - dy * sin + origin.x
|
|
61
|
+
this.y = dx * sin + dy * cos + origin.y
|
|
62
|
+
return this
|
|
142
63
|
}
|
|
143
64
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const y = this.y + (another.y - this.y) * t
|
|
147
|
-
return XY.of(x, y)
|
|
65
|
+
static $(x = 0, y = 0) {
|
|
66
|
+
return { x, y }
|
|
148
67
|
}
|
|
149
68
|
|
|
150
|
-
|
|
151
|
-
return
|
|
69
|
+
static of(xy: IXY) {
|
|
70
|
+
return new XY(xy.x, xy.y)
|
|
152
71
|
}
|
|
153
72
|
|
|
154
|
-
|
|
155
|
-
return
|
|
73
|
+
static from(x: number, y: number) {
|
|
74
|
+
return new XY(x, y)
|
|
156
75
|
}
|
|
157
76
|
|
|
158
|
-
|
|
159
|
-
return
|
|
160
|
-
Math.atan2(this.y - origin.y, this.x - origin.x) -
|
|
161
|
-
Math.atan2(another.y - origin.y, another.x - origin.x),
|
|
162
|
-
)
|
|
77
|
+
static center(wh: { width: number; height: number }) {
|
|
78
|
+
return new XY(wh.width / 2, wh.height / 2)
|
|
163
79
|
}
|
|
164
80
|
|
|
165
|
-
static
|
|
166
|
-
return
|
|
81
|
+
static leftTop(e: { left: number; top: number }) {
|
|
82
|
+
return new XY(e.left, e.top)
|
|
167
83
|
}
|
|
168
84
|
|
|
169
|
-
static
|
|
170
|
-
return new XY(
|
|
85
|
+
static client(e: { clientX: number; clientY: number }) {
|
|
86
|
+
return new XY(e.clientX, e.clientY)
|
|
171
87
|
}
|
|
172
88
|
|
|
173
|
-
static
|
|
174
|
-
|
|
175
|
-
return XY
|
|
89
|
+
static xAxis(rotation: number = 0) {
|
|
90
|
+
const { cos, sin } = Angle.cosSin(rotation)
|
|
91
|
+
return new XY(cos, sin)
|
|
176
92
|
}
|
|
177
93
|
|
|
178
|
-
static
|
|
179
|
-
|
|
94
|
+
static yAxis(rotation: number = 0) {
|
|
95
|
+
const { cos, sin } = Angle.cosSin(rotation)
|
|
96
|
+
return new XY(-sin, cos)
|
|
180
97
|
}
|
|
181
98
|
|
|
182
|
-
static
|
|
183
|
-
return
|
|
99
|
+
static dot(self: IXY, another: IXY) {
|
|
100
|
+
return self.x * another.x + self.y * another.y
|
|
184
101
|
}
|
|
185
102
|
|
|
186
|
-
static
|
|
187
|
-
return
|
|
103
|
+
static distance(self: IXY, another: IXY) {
|
|
104
|
+
return Math.hypot(self.x - another.x, self.y - another.y)
|
|
188
105
|
}
|
|
189
106
|
|
|
190
|
-
static
|
|
191
|
-
return XY.
|
|
107
|
+
static vector(self: IXY, another: IXY) {
|
|
108
|
+
return new XY(self.x - another.x, self.y - another.y)
|
|
192
109
|
}
|
|
193
110
|
|
|
194
|
-
static
|
|
195
|
-
return XY
|
|
111
|
+
static symmetric(self: IXY, origin = XY.$()) {
|
|
112
|
+
return new XY(2 * origin.x - self.x, 2 * origin.y - self.y)
|
|
196
113
|
}
|
|
197
114
|
|
|
198
|
-
static
|
|
199
|
-
|
|
115
|
+
static lerp(self: IXY, origin: IXY, t: number) {
|
|
116
|
+
const distance = XY.distance(self, origin)
|
|
117
|
+
return new XY(
|
|
118
|
+
self.x + (self.x - origin.x) * (t / distance),
|
|
119
|
+
self.y + (self.y - origin.y) * (t / distance),
|
|
120
|
+
)
|
|
200
121
|
}
|
|
201
122
|
}
|