using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace TransportGame.Primitives
{
///
/// 2D vector
///
public struct Vector2
{
#region Constant vectors
///
/// Zero vector
///
public static readonly Vector2 Zero = new Vector2(0, 0);
///
/// Unit vector
///
public static readonly Vector2 Unit = new Vector2(1, 0);
#endregion
#region Properties
///
/// Gets the X component
///
[XmlAttribute("x")]
public float X { get; set; }
///
/// Gets the Y component
///
[XmlAttribute("y")]
public float Y { get; set; }
#endregion
#region Constructor
///
/// Initializes a vector2
///
/// X component
/// Y component
public Vector2(float x, float y)
: this()
{
X = x;
Y = y;
}
///
/// Gets the vector corresponding with specified angle (in radians)
///
/// Radians
/// Vector
public static Vector2 FromRadians(float rads)
{
return new Vector2((float)Math.Cos(rads), (float)Math.Sin(rads));
}
///
/// Gets the vector corresponding with specified angle (in degrees)
///
/// Degrees
/// Vector
public static Vector2 FromDegrees(float degs)
{
float rads = (degs * (float)Math.PI / 180f);
return FromRadians(rads);
}
#endregion
#region Properties (length, normalized)
///
/// Gets the length of the vector
///
public float Length
{
get
{
return (float)Math.Sqrt(LengthSq);
}
}
///
/// Gets the length of the vector squared
///
public float LengthSq
{
get
{
return X * X + Y * Y;
}
}
///
/// Gets the normalized vector
///
/// Normalized vector
public Vector2 Normalized
{
get
{
float len = Length;
return new Vector2(X / len, Y / len);
}
}
///
/// Gets the normalized vector raised to second power
///
///
/// This is less computationally expensive (no need to calculate square root).
///
/// Normalized vector
public Vector2 NormalizedSq
{
get
{
float len2 = LengthSq;
return new Vector2(X * X / len2, Y * Y / len2);
}
}
#endregion
#region Operations
///
/// Rotates vector by given number of radians
///
///
///
public Vector2 Rotate(float radians)
{
float sin = (float)Math.Sin(radians);
float cos = (float)Math.Cos(radians);
return new Vector2(X * cos - Y * sin, X * sin + Y * cos);
}
///
/// Rotates vector by given number of degrees
///
///
///
public Vector2 RotateDeg(float degrees)
{
return Rotate(degrees * (float)Math.PI / 180f);
}
///
/// Sum operator
///
/// First vector
/// Second vector
/// Result of addition
public static Vector2 operator +(Vector2 a, Vector2 b)
{
return new Vector2(a.X + b.X, a.Y + b.Y);
}
///
/// Subtract operator
///
/// First vector
/// Second vector
/// Result of subtraction
public static Vector2 operator -(Vector2 a, Vector2 b)
{
return new Vector2(a.X - b.X, a.Y - b.Y);
}
///
/// Negation operator
///
/// Vector
/// Negated vector
public static Vector2 operator -(Vector2 a)
{
return new Vector2(-a.X, -a.Y);
}
///
/// Multiply by constant
///
/// Vector
/// Constant
/// Result
public static Vector2 operator *(Vector2 a, float c)
{
return new Vector2(a.X * c, a.Y * c);
}
///
/// Multiply by constant
///
/// Constant
/// Vector
/// Result
public static Vector2 operator *(float c, Vector2 a)
{
return new Vector2(a.X * c, a.Y * c);
}
///
/// Divide by constant
///
/// Vector
/// Constant
/// Result
public static Vector2 operator /(Vector2 a, float c)
{
return new Vector2(a.X / c, a.Y / c);
}
///
/// Equality operator
///
/// First vector
/// Second vector
/// True if vectors are equal
public static bool operator ==(Vector2 a, Vector2 b)
{
return a.X == b.X && a.Y == b.Y;
}
///
/// Inequality operator
///
/// First vector
/// Second vector
/// True if vectors are not equal
public static bool operator !=(Vector2 a, Vector2 b)
{
return a.X != b.X || a.Y != b.Y;
}
///
/// Calculates dot product of two vectors
///
/// First vector
/// Second vector
/// Dot product
public static float Dot(Vector2 a, Vector2 b)
{
return a.X * b.X + a.Y * b.Y;
}
///
/// Returns the magnitude of the cross product between the two vectors (z considered 0)
///
/// First vector
/// Second vector
/// Magnitude of cross product
public static float Cross(Vector2 a, Vector2 b)
{
return (a.X * b.Y) - (a.Y * b.X);
}
///
/// Tests if two vectors are colliniar
///
/// a
/// b
/// True if vectors are colliniar
public static bool AreColliniar(Vector2 a, Vector2 b)
{
return Math.Abs(Cross(a, b)) < 1e-12;
}
#endregion
#region Object overrides
///
/// Gets string representation of vector2
///
///
public override string ToString()
{
return String.Format("({0}, {1})", X, Y);
}
///
/// Tests if two vectors are equal.
///
///
///
public override bool Equals(object obj)
{
if (obj is Vector2)
{
Vector2 other = (Vector2)obj;
return X == other.X && Y == other.Y;
}
return false;
}
///
/// Gets hash code of vector2
///
///
public override int GetHashCode()
{
return X.GetHashCode() * 29 + Y.GetHashCode();
}
#endregion
#region Comparers
private class LengthComparerImpl : IComparer
{
public int Compare(Vector2 a, Vector2 b)
{
if (a.LengthSq > b.LengthSq)
return 1;
if (a.LengthSq < b.LengthSq)
return -1;
return 0;
}
}
private class TrigonometricComparerImpl : IComparer
{
private int Quad(Vector2 v)
{
if (v.Y >= 0)
{
if (v.X >= 0)
return 0;
return 1;
}
else
{
if (v.X < 0)
return 2;
return 3;
}
}
public int Compare(Vector2 a, Vector2 b)
{
// If vectors are in different quadrants, we can use quadrant number
int qa = Quad(a), qb = Quad(b);
if (qa != qb)
{
return qa - qb;
}
// In same quadrant. Compute cross product which gives us sin(ab)*len(a)*len(b)
// Vectors are in same quadrant, so angle should be less than 90deg
float cross = Cross(a, b);
if (cross < 0) // Angle > 180 degrees => a > b
return 1;
if (cross > 0) // Angle < 180 degrees => a < b
return -1;
// Points are on the same line. Use distance
if (a.LengthSq > b.LengthSq)
return 1;
else if (a.LengthSq < b.LengthSq)
return -1;
// Points are equal
return 0;
}
}
///
/// Length comparer - compares vectors by length
///
public static readonly IComparer LengthComparer = new LengthComparerImpl();
///
/// Trigonometric comparer - compares vectors by angle
///
public static readonly IComparer TrigonometricComparer = new TrigonometricComparerImpl();
#endregion
}
}