Line data Source code
1 : /////////////////////////////////////////////////////////////////////
2 : // Vector2D
3 : //
4 : // Class for representing and manipulating 2D vectors
5 : //
6 : /////////////////////////////////////////////////////////////////////
7 :
8 : // TODO: Type errors if v not instanceof Vector classes
9 : // TODO: Possibly implement in C++
10 :
11 : function Vector2D(x = 0, y = 0)
12 : {
13 26489 : this.set(x, y);
14 : }
15 :
16 72 : Vector2D.prototype.clone = function()
17 : {
18 23906 : return new Vector2D(this.x, this.y);
19 : };
20 :
21 : // Mutating 2D functions
22 : //
23 : // These functions modify the current object,
24 : // and always return this object to allow chaining
25 :
26 72 : Vector2D.prototype.set = function(x, y)
27 : {
28 26806 : this.x = x;
29 26806 : this.y = y;
30 26806 : return this;
31 : };
32 :
33 72 : Vector2D.prototype.setFrom = function(v)
34 : {
35 0 : this.x = v.x;
36 0 : this.y = v.y;
37 0 : return this;
38 : };
39 :
40 72 : Vector2D.prototype.add = function(v)
41 : {
42 68 : this.x += v.x;
43 68 : this.y += v.y;
44 68 : return this;
45 : };
46 :
47 72 : Vector2D.prototype.sub = function(v)
48 : {
49 26 : this.x -= v.x;
50 26 : this.y -= v.y;
51 26 : return this;
52 : };
53 :
54 72 : Vector2D.prototype.mult = function(f)
55 : {
56 12 : this.x *= f;
57 12 : this.y *= f;
58 12 : return this;
59 : };
60 :
61 72 : Vector2D.prototype.div = function(f)
62 : {
63 23 : this.x /= f;
64 23 : this.y /= f;
65 23 : return this;
66 : };
67 :
68 72 : Vector2D.prototype.normalize = function()
69 : {
70 8 : let magnitude = this.length();
71 8 : if (!magnitude)
72 2 : return this;
73 :
74 6 : return this.div(magnitude);
75 : };
76 :
77 : /**
78 : * Rotate a radians anti-clockwise
79 : */
80 72 : Vector2D.prototype.rotate = function(angle)
81 : {
82 282 : let sin = Math.sin(angle);
83 282 : let cos = Math.cos(angle);
84 :
85 282 : return this.set(
86 : this.x * cos + this.y * sin,
87 : -this.x * sin + this.y * cos);
88 : };
89 :
90 : /**
91 : * Rotate radians anti-clockwise around the specified rotation center.
92 : */
93 72 : Vector2D.prototype.rotateAround = function(angle, center)
94 : {
95 1 : return this.sub(center).rotate(angle).add(center);
96 : };
97 :
98 : /**
99 : * Convert to integer coordinates.
100 : */
101 72 : Vector2D.prototype.round = function()
102 : {
103 10 : return this.set(Math.round(this.x), Math.round(this.y));
104 : };
105 :
106 72 : Vector2D.prototype.floor = function()
107 : {
108 8 : return this.set(Math.floor(this.x), Math.floor(this.y));
109 : };
110 :
111 72 : Vector2D.prototype.toFixed = function(digits)
112 : {
113 0 : return this.set(this.x.toFixed(digits), this.y.toFixed(digits));
114 : };
115 :
116 : // Numeric 2D info functions (non-mutating)
117 : //
118 : // These methods serve to get numeric info on the vector, they don't modify the vector
119 :
120 : /**
121 : * Returns a vector that forms a right angle with this one.
122 : */
123 72 : Vector2D.prototype.perpendicular = function()
124 : {
125 2 : return new Vector2D(-this.y, this.x);
126 : };
127 :
128 : /**
129 : * Computes the scalar product of the two vectors.
130 : * Geometrically, this is the product of the length of the two vectors and the cosine of the angle between them.
131 : * If the vectors are orthogonal, the product is zero.
132 : */
133 72 : Vector2D.prototype.dot = function(v)
134 : {
135 24 : return this.x * v.x + this.y * v.y;
136 : };
137 :
138 : /**
139 : * Computes the non-zero coordinate of the cross product of the two vectors.
140 : * Geometrically, the cross of the vectors is a 3D vector perpendicular to the two 2D vectors.
141 : * The returned number corresponds to the area of the parallelogram with the vectors for sides.
142 : */
143 72 : Vector2D.prototype.cross = function(v)
144 : {
145 7 : return this.x * v.y - this.y * v.x;
146 : };
147 :
148 72 : Vector2D.prototype.lengthSquared = function()
149 : {
150 16 : return this.dot(this);
151 : };
152 :
153 72 : Vector2D.prototype.length = function()
154 : {
155 9 : return Math.sqrt(this.lengthSquared());
156 : };
157 :
158 : /**
159 : * Compare this length to the length of v.
160 : * @return 0 if the lengths are equal
161 : * @return 1 if this is longer than v
162 : * @return -1 if this is shorter than v
163 : * @return NaN if the vectors aren't comparable
164 : */
165 72 : Vector2D.prototype.compareLength = function(v)
166 : {
167 4 : return Math.sign(this.lengthSquared() - v.lengthSquared());
168 : };
169 :
170 72 : Vector2D.prototype.distanceToSquared = function(v)
171 : {
172 31308 : return Math.euclidDistance2DSquared(this.x, this.y, v.x, v.y);
173 : };
174 :
175 72 : Vector2D.prototype.distanceTo = function(v)
176 : {
177 6 : return Math.euclidDistance2D(this.x, this.y, v.x, v.y);
178 : };
179 :
180 : /**
181 : * Returns the angle going from this position to v.
182 : * Angles are between -PI and PI. E.g., north is 0, east is PI/2.
183 : */
184 72 : Vector2D.prototype.angleTo = function(v)
185 : {
186 6 : return Math.atan2(v.x - this.x, v.y - this.y);
187 : };
188 :
189 : // Static 2D functions
190 : //
191 : // Static functions that return a new vector object.
192 : // Note that object creation is slow in JS, so use them only when necessary
193 :
194 72 : Vector2D.from3D = function(v)
195 : {
196 37 : return new Vector2D(v.x, v.z);
197 : };
198 :
199 72 : Vector2D.add = function(v1, v2)
200 : {
201 472 : return new Vector2D(v1.x + v2.x, v1.y + v2.y);
202 : };
203 :
204 72 : Vector2D.sub = function(v1, v2)
205 : {
206 0 : return new Vector2D(v1.x - v2.x, v1.y - v2.y);
207 : };
208 :
209 72 : Vector2D.isEqualTo = function(v1, v2)
210 : {
211 1 : return v1.x == v2.x && v1.y == v2.y;
212 : };
213 :
214 72 : Vector2D.mult = function(v, f)
215 : {
216 0 : return new Vector2D(v.x * f, v.y * f);
217 : };
218 :
219 72 : Vector2D.div = function(v, f)
220 : {
221 0 : return new Vector2D(v.x / f, v.y / f);
222 : };
223 :
224 72 : Vector2D.min = function(v1, v2)
225 : {
226 1 : return new Vector2D(Math.min(v1.x, v2.x), Math.min(v1.y, v2.y));
227 : };
228 :
229 72 : Vector2D.max = function(v1, v2)
230 : {
231 1 : return new Vector2D(Math.max(v1.x, v2.x), Math.max(v1.y, v2.y));
232 : };
233 :
234 72 : Vector2D.average = function(vectorList)
235 : {
236 17 : return Vector2D.sum(vectorList).div(vectorList.length);
237 : };
238 :
239 72 : Vector2D.sum = function(vectorList)
240 : {
241 : // Do not use for...of nor array functions for performance
242 18 : let sum = new Vector2D();
243 :
244 18 : for (let i = 0; i < vectorList.length; ++i)
245 61 : sum.add(vectorList[i]);
246 :
247 18 : return sum;
248 : };
249 :
250 72 : Vector2D.dot = function(v1, v2)
251 : {
252 0 : return v1.x * v2.x + v1.y * v2.y;
253 : };
254 :
255 : /////////////////////////////////////////////////////////////////////
256 : // Vector3D
257 : //
258 : // Class for representing and manipulating 3D vectors
259 : //
260 : /////////////////////////////////////////////////////////////////////
261 :
262 : function Vector3D(x = 0, y = 0, z = 0)
263 : {
264 29100 : this.set(x, y, z);
265 : }
266 :
267 72 : Vector3D.prototype.clone = function()
268 : {
269 1 : return new Vector3D(this.x, this.y, this.z);
270 : };
271 :
272 : // Mutating 3D functions
273 : //
274 : // These functions modify the current object,
275 : // and always return this object to allow chaining
276 :
277 72 : Vector3D.prototype.set = function(x, y, z)
278 : {
279 29106 : this.x = x;
280 29106 : this.y = y;
281 29106 : this.z = z;
282 29106 : return this;
283 : };
284 :
285 72 : Vector3D.prototype.add = function(v)
286 : {
287 10570 : this.x += v.x;
288 10570 : this.y += v.y;
289 10570 : this.z += v.z;
290 10570 : return this;
291 : };
292 :
293 72 : Vector3D.prototype.sub = function(v)
294 : {
295 0 : this.x -= v.x;
296 0 : this.y -= v.y;
297 0 : this.z -= v.z;
298 0 : return this;
299 : };
300 :
301 72 : Vector3D.prototype.mult = function(f)
302 : {
303 1 : this.x *= f;
304 1 : this.y *= f;
305 1 : this.z *= f;
306 1 : return this;
307 : };
308 :
309 72 : Vector3D.prototype.div = function(f)
310 : {
311 2 : this.x /= f;
312 2 : this.y /= f;
313 2 : this.z /= f;
314 2 : return this;
315 : };
316 :
317 72 : Vector3D.prototype.normalize = function()
318 : {
319 0 : let magnitude = this.length();
320 0 : if (!magnitude)
321 0 : return this;
322 :
323 0 : return this.div(magnitude);
324 : };
325 :
326 : /**
327 : * Convert to integer coordinates.
328 : */
329 72 : Vector3D.prototype.round = function()
330 : {
331 4 : return this.set(Math.round(this.x), Math.round(this.y), Math.round(this.z));
332 : };
333 :
334 72 : Vector3D.prototype.floor = function()
335 : {
336 2 : return this.set(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
337 : };
338 :
339 72 : Vector3D.prototype.toFixed = function(digits)
340 : {
341 0 : return this.set(this.x.toFixed(digits), this.y.toFixed(digits), this.z.toFixed(digits));
342 : };
343 :
344 : // Numeric 3D info functions (non-mutating)
345 : //
346 : // These methods serve to get numeric info on the vector, they don't modify the vector
347 :
348 72 : Vector3D.prototype.dot = function(v)
349 : {
350 2 : return this.x * v.x + this.y * v.y + this.z * v.z;
351 : };
352 :
353 : /**
354 : * Returns a vector perpendicular to the two given vectors.
355 : * The length of the returned vector corresponds to the area of the parallelogram with the vectors for sides.
356 : */
357 72 : Vector3D.prototype.cross = function(v)
358 : {
359 1 : return new Vector3D(
360 : this.y * v.z - this.z * v.y,
361 : this.z * v.x - this.x * v.z,
362 : this.x * v.y - this.y * v.x);
363 : };
364 :
365 72 : Vector3D.prototype.lengthSquared = function()
366 : {
367 1 : return this.dot(this);
368 : };
369 :
370 72 : Vector3D.prototype.length = function()
371 : {
372 0 : return Math.sqrt(this.lengthSquared());
373 : };
374 :
375 : /**
376 : * Compare this length to the length of v,
377 : * @return 0 if the lengths are equal
378 : * @return 1 if this is longer than v
379 : * @return -1 if this is shorter than v
380 : * @return NaN if the vectors aren't comparable
381 : */
382 72 : Vector3D.prototype.compareLength = function(v)
383 : {
384 1 : return Math.sign(this.lengthSquared() - v.lengthSquared());
385 : };
386 :
387 72 : Vector3D.prototype.distanceToSquared = function(v)
388 : {
389 0 : return Math.euclidDistance3DSquared(this.x, this.y, this.z, v.x, v.y, v.z);
390 : };
391 :
392 72 : Vector3D.prototype.distanceTo = function(v)
393 : {
394 5 : return Math.euclidDistance3D(this.x, this.y, this.z, v.x, v.y, v.z);
395 : };
396 :
397 72 : Vector3D.prototype.horizDistanceToSquared = function(v)
398 : {
399 10597 : return Math.euclidDistance2DSquared(this.x, this.z, v.x, v.z);
400 : };
401 :
402 72 : Vector3D.prototype.horizDistanceTo = function(v)
403 : {
404 10570 : return Math.sqrt(this.horizDistanceToSquared(v));
405 : };
406 :
407 : /**
408 : * Returns the angle going from this position to v.
409 : */
410 72 : Vector3D.prototype.horizAngleTo = function(v)
411 : {
412 7 : return Math.atan2(v.x - this.x, v.z - this.z);
413 : };
414 :
415 : // Static 3D functions
416 : //
417 : // Static functions that return a new vector object.
418 : // Note that object creation is slow in JS, so use them only when really necessary
419 :
420 72 : Vector3D.add = function(v1, v2)
421 : {
422 1 : return new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
423 : };
424 :
425 72 : Vector3D.sub = function(v1, v2)
426 : {
427 18272 : return new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
428 : };
429 :
430 72 : Vector3D.isEqualTo = function(v1, v2)
431 : {
432 1 : return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
433 : };
434 :
435 72 : Vector3D.mult = function(v, f)
436 : {
437 10569 : return new Vector3D(v.x * f, v.y * f, v.z * f);
438 : };
439 :
440 72 : Vector3D.div = function(v, f)
441 : {
442 0 : return new Vector3D(v.x / f, v.y / f, v.z / f);
443 : };
|