LCOV - code coverage report
Current view: top level - globalscripts - vector.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 136 156 87.2 %
Date: 2023-04-02 12:52:40 Functions: 48 60 80.0 %

          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             : };

Generated by: LCOV version 1.14