Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sisilicon
GitHub Repository: sisilicon/worldedit-be
Path: blob/master/src/library/utils/vector.ts
1784 views
1
import { Direction, Vector3 } from "@minecraft/server";
2
import { Matrix } from "./matrix";
3
4
type anyVec = Vector3 | [number, number, number] | Direction;
5
6
export type axis = "x" | "y" | "z";
7
8
const DIRECTION_VECTORS: Record<Direction, [number, number, number]> = {
9
[Direction.Up]: [0, 1, 0],
10
[Direction.Down]: [0, -1, 0],
11
[Direction.North]: [0, 0, -1],
12
[Direction.South]: [0, 0, 1],
13
[Direction.East]: [1, 0, 0],
14
[Direction.West]: [-1, 0, 0],
15
};
16
17
export class Vector {
18
private vals: [number, number, number] = [0, 0, 0];
19
20
static get AXES(): [axis, axis, axis] {
21
return ["x", "y", "z"];
22
}
23
24
static get ZERO() {
25
return new Vector(0, 0, 0);
26
}
27
static get ONE() {
28
return new Vector(1, 1, 1);
29
}
30
static get UP() {
31
return new Vector(0, 1, 0);
32
}
33
static get DOWN() {
34
return new Vector(0, -1, 0);
35
}
36
static get INF() {
37
return new Vector(Infinity, Infinity, Infinity);
38
}
39
static get NEG_INF() {
40
return new Vector(-Infinity, -Infinity, -Infinity);
41
}
42
43
static from(loc: anyVec) {
44
if (Array.isArray(loc)) return new Vector(...loc);
45
else if (typeof loc === "string") return new Vector(...DIRECTION_VECTORS[loc]);
46
return new Vector(loc.x, loc.y, loc.z);
47
}
48
49
static add(a: anyVec, b: anyVec | number) {
50
return Vector.from(a).add(b);
51
}
52
53
static sub(a: anyVec, b: anyVec | number) {
54
return Vector.from(a).sub(b);
55
}
56
57
static mul(a: anyVec, b: anyVec | number) {
58
return Vector.from(a).mul(b);
59
}
60
61
static div(a: anyVec, b: anyVec | number) {
62
return Vector.from(a).div(b);
63
}
64
65
static min(a: anyVec, b: anyVec) {
66
return Vector.from(a).min(b);
67
}
68
69
static max(a: anyVec, b: anyVec) {
70
return Vector.from(a).max(b);
71
}
72
73
static equals(a: anyVec, b: anyVec) {
74
return Vector.from(a).equals(b);
75
}
76
77
constructor(x: number, y: number, z: number) {
78
this.vals = [x, y, z];
79
}
80
81
get x() {
82
return this.vals[0];
83
}
84
set x(val: number) {
85
this.vals[0] = val;
86
}
87
88
get y() {
89
return this.vals[1];
90
}
91
set y(val: number) {
92
this.vals[1] = val;
93
}
94
95
get z() {
96
return this.vals[2];
97
}
98
set z(val: number) {
99
this.vals[2] = val;
100
}
101
102
get lengthSqr() {
103
return this.x * this.x + this.y * this.y + this.z * this.z;
104
}
105
106
get length() {
107
return Math.hypot(...this.vals);
108
}
109
110
set length(val: number) {
111
const len = this.length;
112
this.x = (this.x / len) * val;
113
this.y = (this.y / len) * val;
114
this.z = (this.z / len) * val;
115
}
116
117
getIdx(idx: number) {
118
return this.vals[idx];
119
}
120
121
setIdx(idx: number, val: number) {
122
return (this.vals[idx] = val);
123
}
124
125
largestAxis(): axis {
126
if (this.x >= this.y && this.x >= this.z) return "x";
127
else if (this.y >= this.x && this.y >= this.z) return "y";
128
else return "z";
129
}
130
131
clone() {
132
return new Vector(...this.vals);
133
}
134
135
equals(v: anyVec) {
136
v = Vector.from(v);
137
return this.x == v.x && this.y == v.y && this.z == v.z;
138
}
139
140
offset(x: number, y: number, z: number) {
141
return new Vector(this.x + x, this.y + y, this.z + z);
142
}
143
144
distanceTo(v: anyVec) {
145
return this.sub(v).length;
146
}
147
148
add(v: anyVec | number) {
149
if (typeof v == "number") {
150
return new Vector(this.x + v, this.y + v, this.z + v);
151
} else {
152
v = Vector.from(v);
153
return new Vector(this.x + v.x, this.y + v.y, this.z + v.z);
154
}
155
}
156
157
sub(v: anyVec | number) {
158
if (typeof v == "number") {
159
return new Vector(this.x - v, this.y - v, this.z - v);
160
} else {
161
v = Vector.from(v);
162
return new Vector(this.x - v.x, this.y - v.y, this.z - v.z);
163
}
164
}
165
166
mul(v: anyVec | number) {
167
if (typeof v == "number") {
168
return new Vector(this.x * v, this.y * v, this.z * v);
169
} else {
170
v = Vector.from(v);
171
return new Vector(this.x * v.x, this.y * v.y, this.z * v.z);
172
}
173
}
174
175
div(v: anyVec | number) {
176
if (typeof v == "number") {
177
return new Vector(this.x / v, this.y / v, this.z / v);
178
} else {
179
v = Vector.from(v);
180
return new Vector(this.x / v.x, this.y / v.y, this.z / v.z);
181
}
182
}
183
184
rotate(degrees: number, axis: axis) {
185
if (!degrees) return this.clone();
186
const wrapped = ((degrees % 360) + 360) % 360;
187
const radians = (wrapped > 180 ? wrapped - 360 : wrapped) * (Math.PI / 180);
188
const cos = Math.cos(radians);
189
const sin = Math.sin(radians);
190
191
if (axis === "x") return new Vector(this.x, Math.round(10000 * (this.y * cos - this.z * sin)) / 10000, Math.round(10000 * (this.y * sin + this.z * cos)) / 10000);
192
else if (axis === "y") return new Vector(Math.round(10000 * (this.x * cos - this.z * sin)) / 10000, this.y, Math.round(10000 * (this.x * sin + this.z * cos)) / 10000);
193
else return new Vector(Math.round(10000 * (this.x * cos - this.y * sin)) / 10000, Math.round(10000 * (this.x * sin + this.y * cos)) / 10000, this.z);
194
}
195
196
min(v: anyVec) {
197
v = Vector.from(v);
198
return new Vector(Math.min(this.x, v.x), Math.min(this.y, v.y), Math.min(this.z, v.z));
199
}
200
201
max(v: anyVec) {
202
v = Vector.from(v);
203
return new Vector(Math.max(this.x, v.x), Math.max(this.y, v.y), Math.max(this.z, v.z));
204
}
205
206
floor() {
207
return new Vector(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
208
}
209
210
ceil() {
211
return new Vector(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z));
212
}
213
214
round() {
215
return new Vector(Math.round(this.x), Math.round(this.y), Math.round(this.z));
216
}
217
218
lerp(v: anyVec, t: number) {
219
v = Vector.from(v);
220
return new Vector((1 - t) * this.x + t * v.x, (1 - t) * this.y + t * v.y, (1 - t) * this.z + t * v.z);
221
}
222
223
abs() {
224
return new Vector(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z));
225
}
226
227
map(callbackfn: (value: number, index: number, array: number[]) => number) {
228
return new Vector(...(<[number, number, number]>this.vals.map(callbackfn)));
229
}
230
231
normalized() {
232
const vec = new Vector(...this.vals);
233
vec.length = 1;
234
return vec;
235
}
236
237
dot(v: anyVec) {
238
v = Vector.from(v);
239
return this.x * v.x + this.y * v.y + this.z * v.z;
240
}
241
242
cross(v: anyVec) {
243
v = Vector.from(v);
244
return new Vector(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x);
245
}
246
247
transform(mat: Matrix) {
248
const [x, y, z] = this.vals;
249
const vals = mat.vals;
250
const w = 1 / (vals[3] * x + vals[7] * y + vals[11] * z + vals[15]);
251
252
const result = new Vector(0, 0, 0);
253
result.x = (vals[0] * x + vals[4] * y + vals[8] * z + vals[12]) * w;
254
result.y = (vals[1] * x + vals[5] * y + vals[9] * z + vals[13]) * w;
255
result.z = (vals[2] * x + vals[6] * y + vals[10] * z + vals[14]) * w;
256
257
return result;
258
}
259
260
transformDirection(mat: Matrix) {
261
const [x, y, z] = this.vals;
262
const vals = mat.vals;
263
264
const result = new Vector(0, 0, 0);
265
result.x = vals[0] * x + vals[4] * y + vals[8] * z;
266
result.y = vals[1] * x + vals[5] * y + vals[9] * z;
267
result.z = vals[2] * x + vals[6] * y + vals[10] * z;
268
result.length = this.length;
269
270
return result;
271
}
272
273
print() {
274
return `${this.x} ${this.y} ${this.z}`;
275
}
276
277
/** Returns angles in radians */
278
toAngles() {
279
return { x: Math.asin(-this.y), y: Math.atan2(-this.x, this.z) };
280
}
281
282
toArray() {
283
return [this.x, this.y, this.z] as [number, number, number];
284
}
285
286
toJSON(): Vector3 {
287
return { x: this.x, y: this.y, z: this.z };
288
}
289
290
toString() {
291
return `(${this.vals[0]}, ${this.vals[1]}, ${this.vals[2]})`;
292
}
293
294
*[Symbol.iterator]() {
295
yield this.vals[0];
296
yield this.vals[1];
297
yield this.vals[2];
298
}
299
}
300
301
export class VectorSet<T extends Vector3 = Vector3> implements Set<T> {
302
private map = new Map<string, T>();
303
304
get size() {
305
return this.map.size;
306
}
307
308
add(value: T) {
309
this.map.set(`${value.x} ${value.y} ${value.z}`, value);
310
return this;
311
}
312
313
get(vector: Vector3) {
314
return this.map.get(`${vector.x} ${vector.y} ${vector.z}`);
315
}
316
317
clear() {
318
this.map.clear();
319
}
320
321
delete(value: T) {
322
return this.map.delete(`${value.x} ${value.y} ${value.z}`);
323
}
324
325
*values() {
326
for (const value of this.map.values()) yield value;
327
}
328
329
keys = this.values;
330
331
*entries() {
332
for (const value of this.map.values()) yield <[T, T]>[value, value];
333
}
334
335
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any) {
336
for (const entry of this.entries()) callbackfn.apply(thisArg, [entry[0], entry[1], this]);
337
}
338
339
has(value: Vector3) {
340
return this.map.has(`${value.x} ${value.y} ${value.z}`);
341
}
342
343
[Symbol.iterator] = this.values;
344
[Symbol.toStringTag] = "vectorSet";
345
}
346
347