LCOV - code coverage report
Current view: top level - app/libs/gmath/src - Vector2.hpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 9 39 23.1 %
Date: 2023-08-17 16:45:52 Functions: 1 2 50.0 %

          Line data    Source code
       1             : /**
       2             :  *  ============================================================================
       3             :  *  MIT License
       4             :  *
       5             :  *  Copyright (c) 2016 Eric Phillips
       6             :  *
       7             :  *  Permission is hereby granted, free of charge, to any person obtaining a
       8             :  *  copy of this software and associated documentation files (the "Software"),
       9             :  *  to deal in the Software without restriction, including without limitation
      10             :  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
      11             :  *  and/or sell copies of the Software, and to permit persons to whom the
      12             :  *  Software is furnished to do so, subject to the following conditions:
      13             :  *
      14             :  *  The above copyright notice and this permission notice shall be included in
      15             :  *  all copies or substantial portions of the Software.
      16             :  *
      17             :  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      18             :  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      19             :  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      20             :  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      21             :  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      22             :  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      23             :  *  DEALINGS IN THE SOFTWARE.
      24             :  *  ============================================================================
      25             :  *
      26             :  *
      27             :  *  This file implements a series of math functions for manipulating a
      28             :  *  2D vector.
      29             :  *
      30             :  *  Created by Eric Phillips on October 15, 2016.
      31             :  */
      32             : 
      33             : #pragma once
      34             : 
      35             : #define _USE_MATH_DEFINES
      36             : #include <math.h>
      37             : 
      38             : 
      39             : struct Vector2
      40             : {
      41             :     union
      42             :     {
      43             :         struct
      44             :         {
      45             :             double X;
      46             :             double Y;
      47             :         };
      48             :         double data[2];
      49             :     };
      50             : 
      51             : 
      52             :     /**
      53             :      * Constructors.
      54             :      */
      55             :     inline Vector2();
      56             :     inline Vector2(double data[]);
      57             :     inline Vector2(double value);
      58             :     inline Vector2(double x, double y);
      59             : 
      60             : 
      61             :     /**
      62             :      * Constants for common vectors.
      63             :      */
      64             :     static inline Vector2 Zero();
      65             :     static inline Vector2 One();
      66             :     static inline Vector2 Right();
      67             :     static inline Vector2 Left();
      68             :     static inline Vector2 Up();
      69             :     static inline Vector2 Down();
      70             : 
      71             : 
      72             :     /**
      73             :      * Returns the angle between two vectors in radians.
      74             :      * @param a: The first vector.
      75             :      * @param b: The second vector.
      76             :      * @return: A scalar value.
      77             :      */
      78             :     static inline double Angle(Vector2 a, Vector2 b);
      79             : 
      80             :     /**
      81             :      * Returns a vector with its magnitude clamped to maxLength.
      82             :      * @param vector: The target vector.
      83             :      * @param maxLength: The maximum length of the return vector.
      84             :      * @return: A new vector.
      85             :      */
      86             :     static inline Vector2 ClampMagnitude(Vector2 vector, double maxLength);
      87             : 
      88             :     /**
      89             :      * Returns the component of a in the direction of b (scalar projection).
      90             :      * @param a: The target vector.
      91             :      * @param b: The vector being compared against.
      92             :      * @return: A scalar value.
      93             :      */
      94             :     static inline double Component(Vector2 a, Vector2 b);
      95             : 
      96             :     /**
      97             :      * Returns the distance between a and b.
      98             :      * @param a: The first point.
      99             :      * @param b: The second point.
     100             :      * @return: A scalar value.
     101             :      */
     102             :     static inline double Distance(Vector2 a, Vector2 b);
     103             : 
     104             :     /**
     105             :      * Returns the dot product of two vectors.
     106             :      * @param lhs: The left side of the multiplication.
     107             :      * @param rhs: The right side of the multiplication.
     108             :      * @return: A scalar value.
     109             :      */
     110             :     static inline double Dot(Vector2 lhs, Vector2 rhs);
     111             : 
     112             :     /**
     113             :      * Converts a polar representation of a vector into cartesian
     114             :      * coordinates.
     115             :      * @param rad: The magnitude of the vector.
     116             :      * @param theta: The angle from the X axis.
     117             :      * @return: A new vector.
     118             :      */
     119             :     static inline Vector2 FromPolar(double rad, double theta);
     120             : 
     121             :     /**
     122             :      * Returns a vector linearly interpolated between a and b, moving along
     123             :      * a straight line. The vector is clamped to never go beyond the end points.
     124             :      * @param a: The starting point.
     125             :      * @param b: The ending point.
     126             :      * @param t: The interpolation value [0-1].
     127             :      * @return: A new vector.
     128             :      */
     129             :     static inline Vector2 Lerp(Vector2 a, Vector2 b, double t);
     130             : 
     131             :     /**
     132             :      * Returns a vector linearly interpolated between a and b, moving along
     133             :      * a straight line.
     134             :      * @param a: The starting point.
     135             :      * @param b: The ending point.
     136             :      * @param t: The interpolation value [0-1] (no actual bounds).
     137             :      * @return: A new vector.
     138             :      */
     139             :     static inline Vector2 LerpUnclamped(Vector2 a, Vector2 b, double t);
     140             : 
     141             :     /**
     142             :      * Returns the magnitude of a vector.
     143             :      * @param v: The vector in question.
     144             :      * @return: A scalar value.
     145             :      */
     146             :     static inline double Magnitude(Vector2 v);
     147             : 
     148             :     /**
     149             :      * Returns a vector made from the largest components of two other vectors.
     150             :      * @param a: The first vector.
     151             :      * @param b: The second vector.
     152             :      * @return: A new vector.
     153             :      */
     154             :     static inline Vector2 Max(Vector2 a, Vector2 b);
     155             : 
     156             :     /**
     157             :      * Returns a vector made from the smallest components of two other vectors.
     158             :      * @param a: The first vector.
     159             :      * @param b: The second vector.
     160             :      * @return: A new vector.
     161             :      */
     162             :     static inline Vector2 Min(Vector2 a, Vector2 b);
     163             : 
     164             :     /**
     165             :      * Returns a vector "maxDistanceDelta" units closer to the target. This
     166             :      * interpolation is in a straight line, and will not overshoot.
     167             :      * @param current: The current position.
     168             :      * @param target: The destination position.
     169             :      * @param maxDistanceDelta: The maximum distance to move.
     170             :      * @return: A new vector.
     171             :      */
     172             :     static inline Vector2 MoveTowards(Vector2 current, Vector2 target,
     173             :                                double maxDistanceDelta);
     174             : 
     175             :     /**
     176             :      * Returns a new vector with magnitude of one.
     177             :      * @param v: The vector in question.
     178             :      * @return: A new vector.
     179             :      */
     180             :     static inline Vector2 Normalized(Vector2 v);
     181             : 
     182             :     /**
     183             :      * Creates a new coordinate system out of the two vectors.
     184             :      * Normalizes "normal" and normalizes "tangent" and makes it orthogonal to
     185             :      * "normal"..
     186             :      * @param normal: A reference to the first axis vector.
     187             :      * @param tangent: A reference to the second axis vector.
     188             :      */
     189             :     static inline void OrthoNormalize(Vector2 &normal, Vector2 &tangent);
     190             : 
     191             :     /**
     192             :      * Returns the vector projection of a onto b.
     193             :      * @param a: The target vector.
     194             :      * @param b: The vector being projected onto.
     195             :      * @return: A new vector.
     196             :      */
     197             :     static inline Vector2 Project(Vector2 a, Vector2 b);
     198             : 
     199             :     /**
     200             :      * Returns a vector reflected about the provided line.
     201             :      * This behaves as if there is a plane with the line as its normal, and the
     202             :      * vector comes in and bounces off this plane.
     203             :      * @param vector: The vector traveling inward at the imaginary plane.
     204             :      * @param line: The line about which to reflect.
     205             :      * @return: A new vector pointing outward from the imaginary plane.
     206             :      */
     207             :     static inline Vector2 Reflect(Vector2 vector, Vector2 line);
     208             : 
     209             :     /**
     210             :      * Returns the vector rejection of a on b.
     211             :      * @param a: The target vector.
     212             :      * @param b: The vector being projected onto.
     213             :      * @return: A new vector.
     214             :      */
     215             :     static inline Vector2 Reject(Vector2 a, Vector2 b);
     216             : 
     217             :     /**
     218             :      * Rotates vector "current" towards vector "target" by "maxRadiansDelta".
     219             :      * This treats the vectors as directions and will linearly interpolate
     220             :      * between their magnitudes by "maxMagnitudeDelta". This function does not
     221             :      * overshoot. If a negative delta is supplied, it will rotate away from
     222             :      * "target" until it is pointing the opposite direction, but will not
     223             :      * overshoot that either.
     224             :      * @param current: The starting direction.
     225             :      * @param target: The destination direction.
     226             :      * @param maxRadiansDelta: The maximum number of radians to rotate.
     227             :      * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation.
     228             :      * @return: A new vector.
     229             :      */
     230             :     static inline Vector2 RotateTowards(Vector2 current, Vector2 target,
     231             :                                  double maxRadiansDelta,
     232             :                                  double maxMagnitudeDelta);
     233             : 
     234             :     /**
     235             :      * Multiplies two vectors component-wise.
     236             :      * @param a: The lhs of the multiplication.
     237             :      * @param b: The rhs of the multiplication.
     238             :      * @return: A new vector.
     239             :      */
     240             :     static inline Vector2 Scale(Vector2 a, Vector2 b);
     241             : 
     242             :     /**
     243             :      * Returns a vector rotated towards b from a by the percent t.
     244             :      * Since interpolation is done spherically, the vector moves at a constant
     245             :      * angular velocity. This rotation is clamped to 0 <= t <= 1.
     246             :      * @param a: The starting direction.
     247             :      * @param b: The ending direction.
     248             :      * @param t: The interpolation value [0-1].
     249             :      */
     250             :     static inline Vector2 Slerp(Vector2 a, Vector2 b, double t);
     251             : 
     252             :     /**
     253             :      * Returns a vector rotated towards b from a by the percent t.
     254             :      * Since interpolation is done spherically, the vector moves at a constant
     255             :      * angular velocity. This rotation is unclamped.
     256             :      * @param a: The starting direction.
     257             :      * @param b: The ending direction.
     258             :      * @param t: The interpolation value [0-1].
     259             :      */
     260             :     static inline Vector2 SlerpUnclamped(Vector2 a, Vector2 b, double t);
     261             : 
     262             :     /**
     263             :      * Returns the squared magnitude of a vector.
     264             :      * This is useful when comparing relative lengths, where the exact length
     265             :      * is not important, and much time can be saved by not calculating the
     266             :      * square root.
     267             :      * @param v: The vector in question.
     268             :      * @return: A scalar value.
     269             :      */
     270             :     static inline double SqrMagnitude(Vector2 v);
     271             : 
     272             :     /**
     273             :      * Calculates the polar coordinate space representation of a vector.
     274             :      * @param vector: The vector to convert.
     275             :      * @param rad: The magnitude of the vector.
     276             :      * @param theta: The angle from the X axis.
     277             :      */
     278             :     static inline void ToPolar(Vector2 vector, double &rad, double &theta);
     279             : 
     280             : 
     281             :     /**
     282             :      * Operator overloading.
     283             :      */
     284             :     inline struct Vector2& operator+=(const double rhs);
     285             :     inline struct Vector2& operator-=(const double rhs);
     286             :     inline struct Vector2& operator*=(const double rhs);
     287             :     inline struct Vector2& operator/=(const double rhs);
     288             :     inline struct Vector2& operator+=(const Vector2 rhs);
     289             :     inline struct Vector2& operator-=(const Vector2 rhs);
     290             : };
     291             : 
     292             : inline Vector2 operator-(Vector2 rhs);
     293             : inline Vector2 operator+(Vector2 lhs, const double rhs);
     294             : inline Vector2 operator-(Vector2 lhs, const double rhs);
     295             : inline Vector2 operator*(Vector2 lhs, const double rhs);
     296             : inline Vector2 operator/(Vector2 lhs, const double rhs);
     297             : inline Vector2 operator+(const double lhs, Vector2 rhs);
     298             : inline Vector2 operator-(const double lhs, Vector2 rhs);
     299             : inline Vector2 operator*(const double lhs, Vector2 rhs);
     300             : inline Vector2 operator/(const double lhs, Vector2 rhs);
     301             : inline Vector2 operator+(Vector2 lhs, const Vector2 rhs);
     302             : inline Vector2 operator-(Vector2 lhs, const Vector2 rhs);
     303             : inline bool operator==(const Vector2 lhs, const Vector2 rhs);
     304             : inline bool operator!=(const Vector2 lhs, const Vector2 rhs);
     305             : 
     306             : 
     307             : 
     308             : /*******************************************************************************
     309             :  * Implementation
     310             :  */
     311             : 
     312           0 : Vector2::Vector2() : X(0), Y(0) {}
     313             : Vector2::Vector2(double data[]) : X(data[0]), Y(data[1]) {}
     314             : Vector2::Vector2(double value) : X(value), Y(value) {}
     315      267647 : Vector2::Vector2(double x, double y) : X(x), Y(y) {}
     316             : 
     317             : 
     318             : Vector2 Vector2::Zero() { return Vector2(0, 0); }
     319             : Vector2 Vector2::One() { return Vector2(1, 1); }
     320             : Vector2 Vector2::Right() { return Vector2(1, 0); }
     321             : Vector2 Vector2::Left() { return Vector2(-1, 0); }
     322             : Vector2 Vector2::Up() { return Vector2(0, 1); }
     323             : Vector2 Vector2::Down() { return Vector2(0, -1); }
     324             : 
     325             : 
     326           0 : double Vector2::Angle(Vector2 a, Vector2 b)
     327             : {
     328           0 :     double v = Dot(a, b) / (Magnitude(a) * Magnitude(b));
     329           0 :     v = fmax(v, -1.0);
     330           0 :     v = fmin(v, 1.0);
     331           0 :     return acos(v);
     332             : }
     333             : 
     334             : Vector2 Vector2::ClampMagnitude(Vector2 vector, double maxLength)
     335             : {
     336             :     double length = Magnitude(vector);
     337             :     if (length > maxLength)
     338             :         vector *= maxLength / length;
     339             :     return vector;
     340             : }
     341             : 
     342             : double Vector2::Component(Vector2 a, Vector2 b)
     343             : {
     344             :     return Dot(a, b) / Magnitude(b);
     345             : }
     346             : 
     347           0 : double Vector2::Distance(Vector2 a, Vector2 b)
     348             : {
     349           0 :     return Vector2::Magnitude(a - b);
     350             : }
     351             : 
     352           0 : double Vector2::Dot(Vector2 lhs, Vector2 rhs)
     353             : {
     354           0 :     return lhs.X * rhs.X + lhs.Y * rhs.Y;
     355             : }
     356             : 
     357             : Vector2 Vector2::FromPolar(double rad, double theta)
     358             : {
     359             :     Vector2 v;
     360             :     v.X = rad * cos(theta);
     361             :     v.Y = rad * sin(theta);
     362             :     return v;
     363             : }
     364             : 
     365             : Vector2 Vector2::Lerp(Vector2 a, Vector2 b, double t)
     366             : {
     367             :     if (t < 0) return a;
     368             :     else if (t > 1) return b;
     369             :     return LerpUnclamped(a, b, t);
     370             : }
     371             : 
     372             : Vector2 Vector2::LerpUnclamped(Vector2 a, Vector2 b, double t)
     373             : {
     374             :     return (b - a) * t + a;
     375             : }
     376             : 
     377      340928 : double Vector2::Magnitude(Vector2 v)
     378             : {
     379      340928 :     return sqrt(SqrMagnitude(v));
     380             : }
     381             : 
     382             : Vector2 Vector2::Max(Vector2 a, Vector2 b)
     383             : {
     384             :     double x = a.X > b.X ? a.X : b.X;
     385             :     double y = a.Y > b.Y ? a.Y : b.Y;
     386             :     return Vector2(x, y);
     387             : }
     388             : 
     389             : Vector2 Vector2::Min(Vector2 a, Vector2 b)
     390             : {
     391             :     double x = a.X > b.X ? b.X : a.X;
     392             :     double y = a.Y > b.Y ? b.Y : a.Y;
     393             :     return Vector2(x, y);
     394             : }
     395             : 
     396             : Vector2 Vector2::MoveTowards(Vector2 current, Vector2 target,
     397             :                              double maxDistanceDelta)
     398             : {
     399             :     Vector2 d = target - current;
     400             :     double m = Magnitude(d);
     401             :     if (m < maxDistanceDelta || m == 0)
     402             :         return target;
     403             :     return current + (d * maxDistanceDelta / m);
     404             : }
     405             : 
     406             : Vector2 Vector2::Normalized(Vector2 v)
     407             : {
     408             :     double mag = Magnitude(v);
     409             :     if (mag == 0)
     410             :         return Vector2::Zero();
     411             :     return v / mag;
     412             : }
     413             : 
     414             : void Vector2::OrthoNormalize(Vector2 &normal, Vector2 &tangent)
     415             : {
     416             :     normal = Normalized(normal);
     417             :     tangent = Reject(tangent, normal);
     418             :     tangent = Normalized(tangent);
     419             : }
     420             : 
     421             : Vector2 Vector2::Project(Vector2 a, Vector2 b)
     422             : {
     423             :     double m = Magnitude(b);
     424             :     return Dot(a, b) / (m * m) * b;
     425             : }
     426             : 
     427             : Vector2 Vector2::Reflect(Vector2 vector, Vector2 planeNormal)
     428             : {
     429             :     return vector - 2 * Project(vector, planeNormal);
     430             : }
     431             : 
     432             : Vector2 Vector2::Reject(Vector2 a, Vector2 b)
     433             : {
     434             :     return a - Project(a, b);
     435             : }
     436             : 
     437             : Vector2 Vector2::RotateTowards(Vector2 current, Vector2 target,
     438             :                                double maxRadiansDelta,
     439             :                                double maxMagnitudeDelta)
     440             : {
     441             :     double magCur = Magnitude(current);
     442             :     double magTar = Magnitude(target);
     443             :     double newMag = magCur + maxMagnitudeDelta *
     444             :         ((magTar > magCur) - (magCur > magTar));
     445             :     newMag = fmin(newMag, fmax(magCur, magTar));
     446             :     newMag = fmax(newMag, fmin(magCur, magTar));
     447             : 
     448             :     double totalAngle = Angle(current, target) - maxRadiansDelta;
     449             :     if (totalAngle <= 0)
     450             :         return Normalized(target) * newMag;
     451             :     else if (totalAngle >= M_PI)
     452             :         return Normalized(-target) * newMag;
     453             : 
     454             :     double axis = current.X * target.Y - current.Y * target.X;
     455             :     axis = axis / fabs(axis);
     456             :     if (!(1 - fabs(axis) < 0.00001))
     457             :         axis = 1;
     458             :     current = Normalized(current);
     459             :     Vector2 newVector = current * cos(maxRadiansDelta) +
     460             :         Vector2(-current.Y, current.X) * sin(maxRadiansDelta) * axis;
     461             :     return newVector * newMag;
     462             : }
     463             : 
     464             : Vector2 Vector2::Scale(Vector2 a, Vector2 b)
     465             : {
     466             :     return Vector2(a.X * b.X, a.Y * b.Y);
     467             : }
     468             : 
     469             : Vector2 Vector2::Slerp(Vector2 a, Vector2 b, double t)
     470             : {
     471             :     if (t < 0) return a;
     472             :     else if (t > 1) return b;
     473             :     return SlerpUnclamped(a, b, t);
     474             : }
     475             : 
     476             : Vector2 Vector2::SlerpUnclamped(Vector2 a, Vector2 b, double t)
     477             : {
     478             :     double magA = Magnitude(a);
     479             :     double magB = Magnitude(b);
     480             :     a /= magA;
     481             :     b /= magB;
     482             :     double dot = Dot(a, b);
     483             :     dot = fmax(dot, -1.0);
     484             :     dot = fmin(dot, 1.0);
     485             :     double theta = acos(dot) * t;
     486             :     Vector2 relativeVec = Normalized(b - a * dot);
     487             :     Vector2 newVec = a * cos(theta) + relativeVec * sin(theta);
     488             :     return newVec * (magA + (magB - magA) * t);
     489             : }
     490             : 
     491      340928 : double Vector2::SqrMagnitude(Vector2 v)
     492             : {
     493      340928 :     return v.X * v.X + v.Y * v.Y;
     494             : }
     495             : 
     496      340928 : void Vector2::ToPolar(Vector2 vector, double &rad, double &theta)
     497             : {
     498      340928 :     rad = Magnitude(vector);
     499      340928 :     theta = atan2(vector.Y, vector.X);
     500      340928 : }
     501             : 
     502             : 
     503             : struct Vector2& Vector2::operator+=(const double rhs)
     504             : {
     505             :     X += rhs;
     506             :     Y += rhs;
     507             :     return *this;
     508             : }
     509             : 
     510             : struct Vector2& Vector2::operator-=(const double rhs)
     511             : {
     512             :     X -= rhs;
     513             :     Y -= rhs;
     514             :     return *this;
     515             : }
     516             : 
     517           0 : struct Vector2& Vector2::operator*=(const double rhs)
     518             : {
     519           0 :     X *= rhs;
     520           0 :     Y *= rhs;
     521           0 :     return *this;
     522             : }
     523             : 
     524           0 : struct Vector2& Vector2::operator/=(const double rhs)
     525             : {
     526           0 :     X /= rhs;
     527           0 :     Y /= rhs;
     528           0 :     return *this;
     529             : }
     530             : 
     531           0 : struct Vector2& Vector2::operator+=(const Vector2 rhs)
     532             : {
     533           0 :     X += rhs.X;
     534           0 :     Y += rhs.Y;
     535           0 :     return *this;
     536             : }
     537             : 
     538           0 : struct Vector2& Vector2::operator-=(const Vector2 rhs)
     539             : {
     540           0 :     X -= rhs.X;
     541           0 :     Y -= rhs.Y;
     542           0 :     return *this;
     543             : }
     544             : 
     545             : Vector2 operator-(Vector2 rhs) { return rhs * -1; }
     546             : Vector2 operator+(Vector2 lhs, const double rhs) { return lhs += rhs; }
     547             : Vector2 operator-(Vector2 lhs, const double rhs) { return lhs -= rhs; }
     548           0 : Vector2 operator*(Vector2 lhs, const double rhs) { return lhs *= rhs; }
     549           0 : Vector2 operator/(Vector2 lhs, const double rhs) { return lhs /= rhs; }
     550             : Vector2 operator+(const double lhs, Vector2 rhs) { return rhs += lhs; }
     551             : Vector2 operator-(const double lhs, Vector2 rhs) { return rhs -= lhs; }
     552             : Vector2 operator*(const double lhs, Vector2 rhs) { return rhs *= lhs; }
     553             : Vector2 operator/(const double lhs, Vector2 rhs) { return rhs /= lhs; }
     554           0 : Vector2 operator+(Vector2 lhs, const Vector2 rhs) { return lhs += rhs; }
     555           0 : Vector2 operator-(Vector2 lhs, const Vector2 rhs) { return lhs -= rhs; }
     556             : 
     557             : bool operator==(const Vector2 lhs, const Vector2 rhs)
     558             : {
     559             :     return lhs.X == rhs.X && lhs.Y == rhs.Y;
     560             : }
     561             : 
     562             : bool operator!=(const Vector2 lhs, const Vector2 rhs)
     563             : {
     564             :     return !(lhs == rhs);
     565             : }

Generated by: LCOV version 1.14