Merge 'remotes/trunk'
[0ad.git] / binaries / data / mods / public / globalscripts / vector.js
blobf4f61227397a9d777335579f3d86015e3dc8ce8e
1 /////////////////////////////////////////////////////////////////////
2 //      Vector2D
3 //
4 //      Class for representing and manipulating 2D vectors
5 //
6 /////////////////////////////////////////////////////////////////////
8 // TODO: Type errors if v not instanceof Vector classes
9 // TODO: Possibly implement in C++
11 function Vector2D(x = 0, y = 0)
13         this.set(x, y);
16 Vector2D.prototype.clone = function()
18         return new Vector2D(this.x, this.y);
21 // Mutating 2D functions
23 // These functions modify the current object,
24 // and always return this object to allow chaining
26 Vector2D.prototype.set = function(x, y)
28         this.x = x;
29         this.y = y;
30         return this;
33 Vector2D.prototype.setFrom = function(v)
35         this.x = v.x;
36         this.y = v.y;
37         return this;
40 Vector2D.prototype.add = function(v)
42         this.x += v.x;
43         this.y += v.y;
44         return this;
47 Vector2D.prototype.sub = function(v)
49         this.x -= v.x;
50         this.y -= v.y;
51         return this;
54 Vector2D.prototype.mult = function(f)
56         this.x *= f;
57         this.y *= f;
58         return this;
61 Vector2D.prototype.div = function(f)
63         this.x /= f;
64         this.y /= f;
65         return this;
68 Vector2D.prototype.normalize = function()
70         let magnitude = this.length();
71         if (!magnitude)
72                 return this;
74         return this.div(magnitude);
77 /**
78  * Rotate a radians anti-clockwise
79  */
80 Vector2D.prototype.rotate = function(angle)
82         let sin = Math.sin(angle);
83         let cos = Math.cos(angle);
85         return this.set(
86                 this.x * cos + this.y * sin,
87                 -this.x * sin + this.y * cos);
90 /**
91  * Rotate radians anti-clockwise around the specified rotation center.
92  */
93 Vector2D.prototype.rotateAround = function(angle, center)
95         return this.sub(center).rotate(angle).add(center);
98 /**
99  * Convert to integer coordinates.
100  */
101 Vector2D.prototype.round = function()
103         return this.set(Math.round(this.x), Math.round(this.y));
106 Vector2D.prototype.floor = function()
108         return this.set(Math.floor(this.x), Math.floor(this.y));
111 Vector2D.prototype.toFixed = function(digits)
113         return this.set(this.x.toFixed(digits), this.y.toFixed(digits));
116 // Numeric 2D info functions (non-mutating)
118 // These methods serve to get numeric info on the vector, they don't modify the vector
121  * Returns a vector that forms a right angle with this one.
122  */
123 Vector2D.prototype.perpendicular = function()
125         return new Vector2D(-this.y, this.x);
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 Vector2D.prototype.dot = function(v)
135         return this.x * v.x + this.y * v.y;
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 Vector2D.prototype.cross = function(v)
145         return this.x * v.y - this.y * v.x;
148 Vector2D.prototype.lengthSquared = function()
150         return this.dot(this);
153 Vector2D.prototype.length = function()
155         return Math.sqrt(this.lengthSquared());
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 Vector2D.prototype.compareLength = function(v)
167         return Math.sign(this.lengthSquared() - v.lengthSquared());
170 Vector2D.prototype.distanceToSquared = function(v)
172         return Math.euclidDistance2DSquared(this.x, this.y, v.x, v.y);
175 Vector2D.prototype.distanceTo = function(v)
177         return Math.euclidDistance2D(this.x, this.y, v.x, v.y);
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 Vector2D.prototype.angleTo = function(v)
186         return Math.atan2(v.x - this.x, v.y - this.y);
189 // Static 2D functions
191 // Static functions that return a new vector object.
192 // Note that object creation is slow in JS, so use them only when necessary
194 Vector2D.from3D = function(v)
196         return new Vector2D(v.x, v.z);
199 Vector2D.add = function(v1, v2)
201         return new Vector2D(v1.x + v2.x, v1.y + v2.y);
204 Vector2D.sub = function(v1, v2)
206         return new Vector2D(v1.x - v2.x, v1.y - v2.y);
209 Vector2D.isEqualTo = function(v1, v2)
211         return v1.x == v2.x && v1.y == v2.y;
214 Vector2D.mult = function(v, f)
216         return new Vector2D(v.x * f, v.y * f);
219 Vector2D.div = function(v, f)
221         return new Vector2D(v.x / f, v.y / f);
224 Vector2D.min = function(v1, v2)
226         return new Vector2D(Math.min(v1.x, v2.x), Math.min(v1.y, v2.y));
229 Vector2D.max = function(v1, v2)
231         return new Vector2D(Math.max(v1.x, v2.x), Math.max(v1.y, v2.y));
234 Vector2D.average = function(vectorList)
236         return Vector2D.sum(vectorList).div(vectorList.length);
239 Vector2D.sum = function(vectorList)
241         // Do not use for...of nor array functions for performance
242         let sum = new Vector2D();
244         for (let i = 0; i < vectorList.length; ++i)
245                 sum.add(vectorList[i]);
247         return sum;
250 Vector2D.dot = function(v1, v2)
252         return v1.x * v2.x + v1.y * v2.y;
255 /////////////////////////////////////////////////////////////////////
256 //      Vector3D
258 //      Class for representing and manipulating 3D vectors
260 /////////////////////////////////////////////////////////////////////
262 function Vector3D(x = 0, y = 0, z = 0)
264         this.set(x, y, z);
267 Vector3D.prototype.clone = function()
269         return new Vector3D(this.x, this.y, this.z);
272 // Mutating 3D functions
274 // These functions modify the current object,
275 // and always return this object to allow chaining
277 Vector3D.prototype.set = function(x, y, z)
279         this.x = x;
280         this.y = y;
281         this.z = z;
282         return this;
285 Vector3D.prototype.add = function(v)
287         this.x += v.x;
288         this.y += v.y;
289         this.z += v.z;
290         return this;
293 Vector3D.prototype.sub = function(v)
295         this.x -= v.x;
296         this.y -= v.y;
297         this.z -= v.z;
298         return this;
301 Vector3D.prototype.mult = function(f)
303         this.x *= f;
304         this.y *= f;
305         this.z *= f;
306         return this;
309 Vector3D.prototype.div = function(f)
311         this.x /= f;
312         this.y /= f;
313         this.z /= f;
314         return this;
317 Vector3D.prototype.normalize = function()
319         let magnitude = this.length();
320         if (!magnitude)
321                 return this;
323         return this.div(magnitude);
327  * Convert to integer coordinates.
328  */
329 Vector3D.prototype.round = function()
331         return this.set(Math.round(this.x), Math.round(this.y), Math.round(this.z));
334 Vector3D.prototype.floor = function()
336         return this.set(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
339 Vector3D.prototype.toFixed = function(digits)
341         return this.set(this.x.toFixed(digits), this.y.toFixed(digits), this.z.toFixed(digits));
344 // Numeric 3D info functions (non-mutating)
346 // These methods serve to get numeric info on the vector, they don't modify the vector
348 Vector3D.prototype.dot = function(v)
350         return this.x * v.x + this.y * v.y + this.z * v.z;
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 Vector3D.prototype.cross = function(v)
359         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);
365 Vector3D.prototype.lengthSquared = function()
367         return this.dot(this);
370 Vector3D.prototype.length = function()
372         return Math.sqrt(this.lengthSquared());
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 Vector3D.prototype.compareLength = function(v)
384         return Math.sign(this.lengthSquared() - v.lengthSquared());
387 Vector3D.prototype.distanceToSquared = function(v)
389         return Math.euclidDistance3DSquared(this.x, this.y, this.z, v.x, v.y, v.z);
392 Vector3D.prototype.distanceTo = function(v)
394         return Math.euclidDistance3D(this.x, this.y, this.z, v.x, v.y, v.z);
397 Vector3D.prototype.horizDistanceToSquared = function(v)
399         return Math.euclidDistance2DSquared(this.x, this.z, v.x, v.z);
402 Vector3D.prototype.horizDistanceTo = function(v)
404         return Math.sqrt(this.horizDistanceToSquared(v));
408  * Returns the angle going from this position to v.
409  */
410 Vector3D.prototype.horizAngleTo = function(v)
412         return Math.atan2(v.x - this.x, v.z - this.z);
415 // Static 3D functions
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
420 Vector3D.add = function(v1, v2)
422         return new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
425 Vector3D.sub = function(v1, v2)
427         return new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
430 Vector3D.isEqualTo = function(v1, v2)
432         return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
435 Vector3D.mult = function(v, f)
437         return new Vector3D(v.x * f, v.y * f, v.z * f);
440 Vector3D.div = function(v, f)
442         return new Vector3D(v.x / f, v.y / f, v.z / f);