@gitborlando/geo 4.1.0 → 5.1.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 +143 -0
- package/dist/index.js +348 -0
- package/package.json +5 -1
- 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 +24 -25
- package/src/xy.ts +64 -143
- package/.github/workflows/publish.yml +0 -36
- package/.github/workflows/release.yml.txt +0 -44
- package/.husky/pre-commit +0 -3
- package/.vscode/settings.json +0 -3
- 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/tsconfig.json +0 -31
- package/tsup.config.ts +0 -10
- package/vitest.config.ts +0 -8
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)
|
|
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,10 +47,10 @@ 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
|
|
|
@@ -59,34 +58,34 @@ export class OBB {
|
|
|
59
58
|
return new OBB(this.x, this.y, this.width, this.height, this.rotation)
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
projectAt = (anotherAxis: IXY) => {
|
|
63
62
|
const { widthAxis, heightAxis } = this.axis
|
|
64
63
|
return (
|
|
65
|
-
Math.abs(
|
|
66
|
-
Math.abs(
|
|
64
|
+
Math.abs(XY.dot(widthAxis, anotherAxis)) * this.width +
|
|
65
|
+
Math.abs(XY.dot(heightAxis, anotherAxis)) * this.height
|
|
67
66
|
)
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
collide = (another: OBB) => {
|
|
71
|
-
const centerVector =
|
|
70
|
+
const centerVector = XY.vector(this.center, another.center)
|
|
72
71
|
if (
|
|
73
|
-
this.
|
|
74
|
-
2 * Math.abs(
|
|
72
|
+
this.projectAt(another.axis.widthAxis) + another.width <=
|
|
73
|
+
2 * Math.abs(XY.dot(centerVector, another.axis.widthAxis))
|
|
75
74
|
)
|
|
76
75
|
return false
|
|
77
76
|
if (
|
|
78
|
-
this.
|
|
79
|
-
2 * Math.abs(
|
|
77
|
+
this.projectAt(another.axis.heightAxis) + another.height <=
|
|
78
|
+
2 * Math.abs(XY.dot(centerVector, another.axis.heightAxis))
|
|
80
79
|
)
|
|
81
80
|
return false
|
|
82
81
|
if (
|
|
83
|
-
another.
|
|
84
|
-
2 * Math.abs(
|
|
82
|
+
another.projectAt(this.axis.widthAxis) + this.width <=
|
|
83
|
+
2 * Math.abs(XY.dot(centerVector, this.axis.widthAxis))
|
|
85
84
|
)
|
|
86
85
|
return false
|
|
87
86
|
if (
|
|
88
|
-
another.
|
|
89
|
-
2 * Math.abs(
|
|
87
|
+
another.projectAt(this.axis.heightAxis) + this.height <=
|
|
88
|
+
2 * Math.abs(XY.dot(centerVector, this.axis.heightAxis))
|
|
90
89
|
)
|
|
91
90
|
return false
|
|
92
91
|
return true
|
|
@@ -104,7 +103,7 @@ export class OBB {
|
|
|
104
103
|
static fromCenter(center: IXY, width: number, height: number, rotation = 0) {
|
|
105
104
|
const dx = center.x - width / 2
|
|
106
105
|
const dy = center.y - height / 2
|
|
107
|
-
const xy = XY.
|
|
106
|
+
const xy = XY.from(dx, dy).rotate(center, rotation)
|
|
108
107
|
return new OBB(xy.x, xy.y, width, height, rotation)
|
|
109
108
|
}
|
|
110
109
|
|
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
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
name: 发布包
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
publish:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
|
|
12
|
-
steps:
|
|
13
|
-
- name: 检出代码
|
|
14
|
-
uses: actions/checkout@v4
|
|
15
|
-
|
|
16
|
-
- name: 设置 Node.js
|
|
17
|
-
uses: actions/setup-node@v4
|
|
18
|
-
with:
|
|
19
|
-
node-version: '20'
|
|
20
|
-
registry-url: 'https://registry.npmjs.org'
|
|
21
|
-
|
|
22
|
-
- name: 安装 pnpm
|
|
23
|
-
uses: pnpm/action-setup@v2
|
|
24
|
-
with:
|
|
25
|
-
version: 8
|
|
26
|
-
|
|
27
|
-
- name: 安装依赖
|
|
28
|
-
run: pnpm install
|
|
29
|
-
|
|
30
|
-
- name: 构建项目
|
|
31
|
-
run: pnpm build
|
|
32
|
-
|
|
33
|
-
- name: 发布到 npm
|
|
34
|
-
run: pnpm publish --no-git-checks
|
|
35
|
-
env:
|
|
36
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
release:
|
|
10
|
-
name: Release
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- name: Checkout
|
|
14
|
-
uses: actions/checkout@v3
|
|
15
|
-
with:
|
|
16
|
-
fetch-depth: 0
|
|
17
|
-
|
|
18
|
-
- name: Setup Node.js
|
|
19
|
-
uses: actions/setup-node@v3
|
|
20
|
-
with:
|
|
21
|
-
node-version: 20
|
|
22
|
-
|
|
23
|
-
- name: Setup PNPM
|
|
24
|
-
uses: pnpm/action-setup@v2
|
|
25
|
-
with:
|
|
26
|
-
version: 8
|
|
27
|
-
|
|
28
|
-
- name: Install Dependencies
|
|
29
|
-
run: pnpm install
|
|
30
|
-
|
|
31
|
-
# 添加构建步骤
|
|
32
|
-
- name: Build Packages
|
|
33
|
-
run: pnpm build
|
|
34
|
-
|
|
35
|
-
- name: Create Release Pull Request or Publish
|
|
36
|
-
id: changeset
|
|
37
|
-
uses: changesets/action@v1
|
|
38
|
-
with:
|
|
39
|
-
publish: pnpm changeset publish
|
|
40
|
-
commit: 'chore: version packages'
|
|
41
|
-
title: 'chore: version packages'
|
|
42
|
-
env:
|
|
43
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/.husky/pre-commit
DELETED
package/.vscode/settings.json
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// 测试轴对齐包围盒功能
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
import { AABB } from '../aabb'
|
|
5
|
-
import { OBB } from '../obb'
|
|
6
|
-
|
|
7
|
-
describe('AABB', () => {
|
|
8
|
-
it('should create AABB correctly', () => {
|
|
9
|
-
const aabb = new AABB(0, 0, 10, 10)
|
|
10
|
-
expect(aabb.minX).toBe(0)
|
|
11
|
-
expect(aabb.minY).toBe(0)
|
|
12
|
-
expect(aabb.maxX).toBe(10)
|
|
13
|
-
expect(aabb.maxY).toBe(10)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('should convert to rect correctly', () => {
|
|
17
|
-
const aabb = new AABB(5, 10, 15, 25)
|
|
18
|
-
const rect = AABB.rect(aabb)
|
|
19
|
-
expect(rect.x).toBe(5)
|
|
20
|
-
expect(rect.y).toBe(10)
|
|
21
|
-
expect(rect.width).toBe(10)
|
|
22
|
-
expect(rect.height).toBe(15)
|
|
23
|
-
expect(rect.centerX).toBe(10)
|
|
24
|
-
expect(rect.centerY).toBe(17.5)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('should detect collision correctly', () => {
|
|
28
|
-
const aabb1 = new AABB(0, 0, 10, 10)
|
|
29
|
-
const aabb2 = new AABB(5, 5, 15, 15)
|
|
30
|
-
const aabb3 = new AABB(20, 20, 30, 30)
|
|
31
|
-
|
|
32
|
-
expect(AABB.collide(aabb1, aabb2)).toBe(true)
|
|
33
|
-
expect(AABB.collide(aabb1, aabb3)).toBe(false)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should detect inclusion correctly', () => {
|
|
37
|
-
const large = new AABB(0, 0, 20, 20)
|
|
38
|
-
const small = new AABB(5, 5, 15, 15)
|
|
39
|
-
const outside = new AABB(25, 25, 35, 35)
|
|
40
|
-
|
|
41
|
-
expect(AABB.include(large, small)).toBe(1) // large包含small
|
|
42
|
-
expect(AABB.include(small, large)).toBe(0) // small被large包含
|
|
43
|
-
expect(AABB.include(large, outside)).toBe(-1) // 不包含
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should expand correctly with single value', () => {
|
|
47
|
-
const aabb = new AABB(5, 5, 15, 15)
|
|
48
|
-
const expanded = AABB.extend(aabb, 2)
|
|
49
|
-
expect(expanded.minX).toBe(3)
|
|
50
|
-
expect(expanded.minY).toBe(3)
|
|
51
|
-
expect(expanded.maxX).toBe(17)
|
|
52
|
-
expect(expanded.maxY).toBe(17)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('should expand correctly with four values', () => {
|
|
56
|
-
const aabb = new AABB(5, 5, 15, 15)
|
|
57
|
-
const expanded = AABB.extend(aabb, 1, 2, 3, 4)
|
|
58
|
-
expect(expanded.minX).toBe(4)
|
|
59
|
-
expect(expanded.minY).toBe(3)
|
|
60
|
-
expect(expanded.maxX).toBe(18)
|
|
61
|
-
expect(expanded.maxY).toBe(19)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('should merge multiple AABBs correctly', () => {
|
|
65
|
-
const aabb1 = new AABB(0, 0, 5, 5)
|
|
66
|
-
const aabb2 = new AABB(10, 10, 15, 15)
|
|
67
|
-
const aabb3 = new AABB(-5, -5, 0, 0)
|
|
68
|
-
|
|
69
|
-
const merged = AABB.merge(aabb1, aabb2, aabb3)
|
|
70
|
-
expect(merged.minX).toBe(-5)
|
|
71
|
-
expect(merged.minY).toBe(-5)
|
|
72
|
-
expect(merged.maxX).toBe(15)
|
|
73
|
-
expect(merged.maxY).toBe(15)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('should create from OBB correctly', () => {
|
|
77
|
-
const obb = new OBB(0, 0, 10, 20, 0)
|
|
78
|
-
const aabb = AABB.fromOBB(obb)
|
|
79
|
-
expect(aabb.minX).toBe(0)
|
|
80
|
-
expect(aabb.minY).toBe(0)
|
|
81
|
-
expect(aabb.maxX).toBe(10)
|
|
82
|
-
expect(aabb.maxY).toBe(20)
|
|
83
|
-
})
|
|
84
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { Angle, PI } from '../angle'
|
|
3
|
-
|
|
4
|
-
describe('Angle', () => {
|
|
5
|
-
it('should export PI constant correctly', () => {
|
|
6
|
-
expect(PI).toBe(Math.PI)
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
it('should calculate cosine correctly', () => {
|
|
10
|
-
expect(Angle.cos(0)).toBe(1)
|
|
11
|
-
expect(Angle.cos(90)).toBeCloseTo(0, 10)
|
|
12
|
-
expect(Angle.cos(180)).toBeCloseTo(-1, 10)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should calculate sine correctly', () => {
|
|
16
|
-
expect(Angle.sin(0)).toBe(0)
|
|
17
|
-
expect(Angle.sin(90)).toBeCloseTo(1, 10)
|
|
18
|
-
expect(Angle.sin(180)).toBeCloseTo(0, 10)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should calculate tangent correctly', () => {
|
|
22
|
-
expect(Angle.tan(0)).toBe(0)
|
|
23
|
-
expect(Angle.tan(45)).toBeCloseTo(1, 10)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should convert radians to degrees correctly', () => {
|
|
27
|
-
expect(Angle.angleFy(Math.PI)).toBe(180)
|
|
28
|
-
expect(Angle.angleFy(Math.PI / 2)).toBe(90)
|
|
29
|
-
expect(Angle.angleFy(0)).toBe(0)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('should convert degrees to radians correctly', () => {
|
|
33
|
-
expect(Angle.radianFy(180)).toBe(Math.PI)
|
|
34
|
-
expect(Angle.radianFy(90)).toBe(Math.PI / 2)
|
|
35
|
-
expect(Angle.radianFy(0)).toBe(0)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('should normalize angles correctly', () => {
|
|
39
|
-
expect(Angle.normal(370)).toBe(10)
|
|
40
|
-
expect(Angle.normal(-10)).toBe(350)
|
|
41
|
-
expect(Angle.normal(0)).toBe(0)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('should snap angles to steps correctly', () => {
|
|
45
|
-
expect(Angle.snap(45)).toBe(90) // 45度四舍五入到90度
|
|
46
|
-
expect(Angle.snap(80)).toBe(90)
|
|
47
|
-
expect(Angle.snap(120)).toBe(90)
|
|
48
|
-
expect(Angle.snap(135)).toBe(180)
|
|
49
|
-
expect(Angle.snap(0)).toBe(0)
|
|
50
|
-
expect(Angle.snap(30)).toBe(0) // 30度四舍五入到0度
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should rotate point correctly', () => {
|
|
54
|
-
const result = Angle.rotatePoint(1, 0, 0, 0, 90)
|
|
55
|
-
expect(result.x).toBeCloseTo(0, 10)
|
|
56
|
-
expect(result.y).toBeCloseTo(1, 10)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import * as Geo from '../index'
|
|
3
|
-
|
|
4
|
-
describe('Index exports', () => {
|
|
5
|
-
it('should export all modules correctly', () => {
|
|
6
|
-
// 验证AABB相关导出
|
|
7
|
-
expect(typeof Geo.AABB).toBe('function')
|
|
8
|
-
|
|
9
|
-
// 验证Angle相关导出
|
|
10
|
-
expect(typeof Geo.Angle).toBe('function')
|
|
11
|
-
expect(typeof Geo.PI).toBe('number')
|
|
12
|
-
|
|
13
|
-
// 验证数学函数导出
|
|
14
|
-
expect(typeof Geo.sqrt).toBe('function')
|
|
15
|
-
expect(typeof Geo.abs).toBe('function')
|
|
16
|
-
expect(typeof Geo.pow2).toBe('function')
|
|
17
|
-
expect(typeof Geo.pow3).toBe('function')
|
|
18
|
-
expect(typeof Geo.multiply).toBe('function')
|
|
19
|
-
expect(typeof Geo.divide).toBe('function')
|
|
20
|
-
expect(typeof Geo.numberHalfFix).toBe('function')
|
|
21
|
-
|
|
22
|
-
// 验证Matrix相关导出
|
|
23
|
-
expect(typeof Geo.Matrix).toBe('function')
|
|
24
|
-
|
|
25
|
-
// 验证OBB相关导出
|
|
26
|
-
expect(typeof Geo.OBB).toBe('function')
|
|
27
|
-
|
|
28
|
-
// 验证贝塞尔曲线相关导出
|
|
29
|
-
expect(typeof Geo.pointsOnBezierCurves).toBe('function')
|
|
30
|
-
expect(typeof Geo.simplify).toBe('function')
|
|
31
|
-
expect(typeof Geo.simplifyPoints).toBe('function')
|
|
32
|
-
|
|
33
|
-
// 验证XY相关导出
|
|
34
|
-
expect(typeof Geo.xy_).toBe('function')
|
|
35
|
-
expect(typeof Geo.xy_client).toBe('function')
|
|
36
|
-
expect(typeof Geo.xy_from).toBe('function')
|
|
37
|
-
expect(typeof Geo.xy_plus).toBe('function')
|
|
38
|
-
expect(typeof Geo.xy_minus).toBe('function')
|
|
39
|
-
expect(typeof Geo.xy_multiply).toBe('function')
|
|
40
|
-
expect(typeof Geo.xy_divide).toBe('function')
|
|
41
|
-
expect(typeof Geo.xy_distance).toBe('function')
|
|
42
|
-
expect(typeof Geo.xy_dot).toBe('function')
|
|
43
|
-
expect(typeof Geo.XY).toBe('function')
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should export type interfaces', () => {
|
|
47
|
-
// 验证可以使用导出的类型
|
|
48
|
-
const point: Geo.IXY = { x: 10, y: 20 }
|
|
49
|
-
const rect: Geo.IRect = { x: 0, y: 0, width: 100, height: 50 }
|
|
50
|
-
const rectWithCenter: Geo.IRectWithCenter = {
|
|
51
|
-
x: 0,
|
|
52
|
-
y: 0,
|
|
53
|
-
width: 100,
|
|
54
|
-
height: 50,
|
|
55
|
-
centerX: 50,
|
|
56
|
-
centerY: 25,
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
expect(point.x).toBe(10)
|
|
60
|
-
expect(rect.width).toBe(100)
|
|
61
|
-
expect(rectWithCenter.centerX).toBe(50)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('should allow creating instances from exported classes', () => {
|
|
65
|
-
// 测试能够创建实例
|
|
66
|
-
const aabb = new Geo.AABB(0, 0, 10, 10)
|
|
67
|
-
const obb = new Geo.OBB(0, 0, 10, 10, 0)
|
|
68
|
-
const xy = new Geo.XY(5, 5)
|
|
69
|
-
|
|
70
|
-
expect(aabb.minX).toBe(0)
|
|
71
|
-
expect(obb.width).toBe(10)
|
|
72
|
-
expect(xy.x).toBe(5)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should allow using static methods from exported classes', () => {
|
|
76
|
-
// 测试静态方法
|
|
77
|
-
const matrix = Geo.Matrix.create()
|
|
78
|
-
const normalizedAngle = Geo.Angle.normal(370)
|
|
79
|
-
const distance = Geo.xy_distance({ x: 0, y: 0 }, { x: 3, y: 4 })
|
|
80
|
-
|
|
81
|
-
expect(matrix).toEqual([1, 0, 0, 1, 0, 0])
|
|
82
|
-
expect(normalizedAngle).toBe(10)
|
|
83
|
-
expect(distance).toBe(5)
|
|
84
|
-
})
|
|
85
|
-
})
|