2019-02-25 21:00:59 +01:00
using System ;
using System.ComponentModel ;
using System.Xml.Serialization ;
namespace JuicyGraphics.Mathematics {
using Exceptions ;
[ImmutableObject(true), Serializable]
public struct vec2
: IComparable , IComparable < vec2 > , IEquatable < vec2 > , IFormattable {
private readonly double _x ;
private readonly double _y ;
public vec2 ( double x , double y ) {
2019-02-25 22:49:19 +01:00
_x = x ;
_y = y ;
2019-02-25 21:00:59 +01:00
}
public vec2 ( double [ ] xy ) {
if ( xy . Length = = 2 ) {
2019-02-25 22:49:19 +01:00
_x = xy [ 0 ] ;
_y = xy [ 1 ] ;
2019-02-25 21:00:59 +01:00
}
else {
throw new ArgumentException ( TWO_COMPONENTS ) ;
}
}
public vec2 ( vec2 v1 ) {
2019-02-25 22:49:19 +01:00
_x = v1 . x ;
_y = v1 . y ;
2019-02-25 21:00:59 +01:00
}
public double x {
get {
return _x ;
}
}
public double y {
get {
return _y ;
}
}
public vec2 xx {
get {
return new vec2 ( _x , _x ) ;
}
}
public vec2 xy {
get {
return this ;
}
}
public vec2 yx {
get {
return new vec2 ( _y , _x ) ;
}
}
public vec2 yy {
get {
return new vec2 ( _y , _y ) ;
}
}
public vec2 normal {
get {
2019-02-25 22:49:19 +01:00
return normalize ( ) ;
2019-02-25 21:00:59 +01:00
}
}
public double magnitude {
get {
2019-02-25 22:49:19 +01:00
return Math . Sqrt ( sumComponentSqrs ( ) ) ;
2019-02-25 21:00:59 +01:00
}
}
[XmlIgnore]
public double [ ] array {
get {
2019-02-25 22:49:19 +01:00
return new [ ] { x , y } ;
2019-02-25 21:00:59 +01:00
}
}
public double this [ int index ] {
get {
switch ( index ) {
case 0 :
2019-02-25 22:49:19 +01:00
return _x ;
2019-02-25 21:00:59 +01:00
case 1 :
2019-02-25 22:49:19 +01:00
return _y ;
2019-02-25 21:00:59 +01:00
default :
throw new ArgumentException ( TWO_COMPONENTS , "index" ) ;
}
}
}
public static vec2 operator + ( vec2 v1 , vec2 v2 ) {
return new vec2 (
v1 . x + v2 . x ,
v1 . y + v2 . y ) ;
}
public static vec2 operator - ( vec2 v1 , vec2 v2 ) {
return new vec2 (
v1 . x - v2 . x ,
v1 . y - v2 . y ) ;
}
public static vec2 operator * ( vec2 v1 , double s2 ) {
return
new vec2 (
v1 . x * s2 ,
v1 . y * s2 ) ;
}
public static vec2 operator * ( double s1 , vec2 v2 ) {
return v2 * s1 ;
}
public static vec2 operator / ( vec2 v1 , double s2 ) {
return new vec2 (
v1 . x / s2 ,
v1 . y / s2 ) ;
}
public static vec2 operator - ( vec2 v1 ) {
return new vec2 (
- v1 . x ,
- v1 . y ) ;
}
public static vec2 operator + ( vec2 v1 ) {
return new vec2 (
+ v1 . x ,
+ v1 . y ) ;
}
public static bool operator < ( vec2 v1 , vec2 v2 ) {
return v1 . sumComponentSqrs ( ) < v2 . sumComponentSqrs ( ) ;
}
public static bool operator > ( vec2 v1 , vec2 v2 ) {
return v1 . sumComponentSqrs ( ) > v2 . sumComponentSqrs ( ) ;
}
public static bool operator < = ( vec2 v1 , vec2 v2 ) {
return v1 . sumComponentSqrs ( ) < = v2 . sumComponentSqrs ( ) ;
}
public static bool operator > = ( vec2 v1 , vec2 v2 ) {
return v1 . sumComponentSqrs ( ) > = v2 . sumComponentSqrs ( ) ;
}
public static bool operator = = ( vec2 v1 , vec2 v2 ) {
return
v1 . x = = v2 . x & &
v1 . y = = v2 . y ;
}
public static bool operator ! = ( vec2 v1 , vec2 v2 ) {
return ! ( v1 = = v2 ) ;
}
public static vec2 scale ( vec2 vector , double magnitude ) {
if ( magnitude < 0 ) {
throw new ArgumentOutOfRangeException ( "magnitude" , magnitude , NEGATIVE_magnitude ) ;
}
if ( vector = = new vec2 ( 0 , 0 ) ) {
throw new ArgumentException ( ORIGIN_VECTOR_magnitude , "vector" ) ;
}
return vector * ( magnitude / vector . magnitude ) ;
}
public vec2 scale ( double magnitude ) {
return vec2 . scale ( this , magnitude ) ;
}
public static double dotProduct ( vec2 v1 , vec2 v2 ) {
return
v1 . x * v2 . x +
v1 . y * v2 . y ;
}
public double dotProduct ( vec2 other ) {
return dotProduct ( this , other ) ;
}
public static vec2 normalize ( vec2 v1 ) {
if ( double . IsInfinity ( v1 . magnitude ) ) {
v1 = normalizeSpecialCasesOrOrigional ( v1 ) ;
if ( v1 . isNaN ( ) ) {
throw new normalizeVectorException ( NORMALIZE_Inf ) ;
}
}
if ( v1 . magnitude = = 0 ) {
throw new normalizeVectorException ( NORMALIZE_0 ) ;
}
if ( v1 . isNaN ( ) ) {
throw new normalizeVectorException ( NORMALIZE_NaN ) ;
}
return normalizeOrNaN ( v1 ) ;
}
public static vec2 normalizeOrDefault ( vec2 v1 ) {
v1 = normalizeSpecialCasesOrOrigional ( v1 ) ;
if ( v1 . magnitude = = 0 ) {
return origin ;
}
if ( v1 . isNaN ( ) ) {
return NaN ;
}
return normalizeOrNaN ( v1 ) ;
}
public vec2 normalize ( ) {
return normalize ( this ) ;
}
public vec2 normalizeOrDefault ( ) {
return normalizeOrDefault ( this ) ;
}
private static vec2 normalizeOrNaN ( vec2 v1 ) {
double inverse = 1 / v1 . magnitude ;
return new vec2 (
v1 . x * inverse ,
v1 . y * inverse ) ;
}
private static vec2 normalizeSpecialCasesOrOrigional ( vec2 v1 ) {
if ( double . IsInfinity ( v1 . magnitude ) ) {
var x = v1 . x = = 0 ? 0 : v1 . x = = - 0 ? - 0 : double . IsPositiveInfinity ( v1 . x ) ? 1 : double . IsNegativeInfinity ( v1 . x ) ? - 1 : double . NaN ;
var y = v1 . y = = 0 ? 0 : v1 . y = = - 0 ? - 0 : double . IsPositiveInfinity ( v1 . y ) ? 1 : double . IsNegativeInfinity ( v1 . y ) ? - 1 : double . NaN ;
return new vec2 ( x , y ) ;
}
return v1 ;
}
public static vec2 interpolate ( vec2 v1 , vec2 v2 , double control , bool allowExtrapolation ) {
if ( ! allowExtrapolation & & ( control > 1 | | control < 0 ) ) {
throw new ArgumentOutOfRangeException (
"control" ,
control ,
INTERPOLATION_RANGE + "\n" + ARGUMENT_VALUE + control ) ;
}
return new vec2 (
v1 . x * ( 1 - control ) + v2 . x * control ,
v1 . y * ( 1 - control ) + v2 . y * control ) ;
}
public static vec2 interpolate ( vec2 v1 , vec2 v2 , double control ) {
return interpolate ( v1 , v2 , control , false ) ;
}
public vec2 interpolate ( vec2 other , double control ) {
return interpolate ( this , other , control ) ;
}
public vec2 interpolate ( vec2 other , double control , bool allowExtrapolation ) {
return interpolate ( this , other , control ) ;
}
public static double distance ( vec2 v1 , vec2 v2 ) {
return Math . Sqrt (
( v1 . x - v2 . x ) * ( v1 . x - v2 . x ) +
( v1 . y - v2 . y ) * ( v1 . y - v2 . y ) ) ;
}
public double distance ( vec2 other ) {
return distance ( this , other ) ;
}
public static double angle ( vec2 v1 , vec2 v2 ) {
if ( v1 = = v2 ) {
return 0 ;
}
return
Math . Acos (
Math . Min ( 1.0f , normalizeOrDefault ( v1 ) . dotProduct ( normalizeOrDefault ( v2 ) ) ) ) ;
}
public double angle ( vec2 other ) {
return angle ( this , other ) ;
}
public static vec2 max ( vec2 v1 , vec2 v2 ) {
return v1 > = v2 ? v1 : v2 ;
}
public vec2 max ( vec2 other ) {
return max ( this , other ) ;
}
public static vec2 min ( vec2 v1 , vec2 v2 ) {
return v1 < = v2 ? v1 : v2 ;
}
public vec2 min ( vec2 other ) {
return min ( this , other ) ;
}
public static vec2 rotate ( vec2 v1 , double rad ) {
double x = ( v1 . x * Math . Cos ( rad ) ) - ( v1 . y * Math . Sin ( rad ) ) ;
double y = ( v1 . x * Math . Sin ( rad ) ) + ( v1 . y * Math . Cos ( rad ) ) ;
return new vec2 ( x , y ) ;
}
public vec2 rotate ( double rad ) {
return rotate ( this , rad ) ;
}
public static vec2 rotate ( vec2 v1 , double xOff , double yOff , double rad ) {
double x = ( v1 . x * Math . Cos ( rad ) ) - ( v1 . y * Math . Sin ( rad ) ) + ( xOff * ( 1 - Math . Cos ( rad ) ) + yOff * Math . Sin ( rad ) ) ;
double y = ( v1 . x * Math . Sin ( rad ) ) + ( v1 . y * Math . Cos ( rad ) ) + ( yOff * ( 1 - Math . Cos ( rad ) ) - xOff * Math . Sin ( rad ) ) ;
return new vec2 ( x , y ) ;
}
public vec2 rotate ( double xOff , double yOff , double rad ) {
return rotate ( this , xOff , yOff , rad ) ;
}
public static vec2 projection ( vec2 v1 , vec2 v2 ) {
return new vec2 ( v2 * ( v1 . dotProduct ( v2 ) / Math . Pow ( v2 . magnitude , 2 ) ) ) ;
}
public vec2 projection ( vec2 direction ) {
return projection ( this , direction ) ;
}
public static vec2 rejection ( vec2 v1 , vec2 v2 ) {
return v1 - v1 . projection ( v2 ) ;
}
public vec2 rejection ( vec2 direction ) {
return rejection ( this , direction ) ;
}
public vec2 reflection ( vec2 reflector ) {
this = vec2 . reflection ( this , reflector ) ;
return this ;
}
public static vec2 reflection ( vec2 v1 , vec2 v2 ) {
if ( Math . Abs ( Math . Abs ( v1 . angle ( v2 ) ) - Math . PI / 2 ) < Double . Epsilon ) {
return - v1 ;
}
vec2 retval = new vec2 ( 2 * v1 . projection ( v2 ) - v1 ) ;
return retval . scale ( v1 . magnitude ) ;
}
public static Double abs ( vec2 v1 ) {
return v1 . magnitude ;
}
public double abs ( ) {
2019-02-25 22:49:19 +01:00
return magnitude ;
2019-02-25 21:00:59 +01:00
}
public static double sumComponents ( vec2 v1 ) {
return v1 . x + v1 . y ;
}
public double sumComponents ( ) {
return sumComponents ( this ) ;
}
public static double sumComponentSqrs ( vec2 v1 ) {
vec2 v2 = sqrComponents ( v1 ) ;
return v2 . sumComponents ( ) ;
}
public double sumComponentSqrs ( ) {
return sumComponentSqrs ( this ) ;
}
public static vec2 powComponents ( vec2 v1 , double power ) {
return new vec2 (
Math . Pow ( v1 . x , power ) ,
Math . Pow ( v1 . y , power ) ) ;
}
public vec2 powComponents ( double power ) {
return powComponents ( this , power ) ;
}
public static vec2 sqrtComponents ( vec2 v1 ) {
return new vec2 (
Math . Sqrt ( v1 . x ) ,
Math . Sqrt ( v1 . y ) ) ;
}
public vec2 sqrtComponents ( ) {
return sqrtComponents ( this ) ;
}
public static vec2 sqrComponents ( vec2 v1 ) {
return new vec2 (
v1 . x * v1 . x ,
v1 . y * v1 . y ) ;
}
public vec2 sqrComponents ( ) {
return sqrComponents ( this ) ;
}
public static vec2 round ( vec2 v1 ) {
return new vec2 ( Math . Round ( v1 . x ) , Math . Round ( v1 . y ) ) ;
}
public static vec2 round ( vec2 v1 , int digits ) {
return new vec2 ( Math . Round ( v1 . x , digits ) , Math . Round ( v1 . y , digits ) ) ;
}
public static vec2 round ( vec2 v1 , MidpointRounding mode ) {
return new vec2 ( Math . Round ( v1 . x , mode ) , Math . Round ( v1 . y , mode ) ) ;
}
public static vec2 round ( vec2 v1 , int digits , MidpointRounding mode ) {
return new vec2 ( Math . Round ( v1 . x , digits , mode ) , Math . Round ( v1 . y , digits , mode ) ) ;
}
public vec2 round ( ) {
2019-02-25 22:49:19 +01:00
return new vec2 ( Math . Round ( x ) , Math . Round ( y ) ) ;
2019-02-25 21:00:59 +01:00
}
public vec2 round ( int digits ) {
2019-02-25 22:49:19 +01:00
return new vec2 ( Math . Round ( x , digits ) , Math . Round ( y , digits ) ) ;
2019-02-25 21:00:59 +01:00
}
public vec2 round ( MidpointRounding mode ) {
2019-02-25 22:49:19 +01:00
return new vec2 ( Math . Round ( x , mode ) , Math . Round ( y , mode ) ) ;
2019-02-25 21:00:59 +01:00
}
public vec2 round ( int digits , MidpointRounding mode ) {
2019-02-25 22:49:19 +01:00
return new vec2 ( Math . Round ( x , digits , mode ) , Math . Round ( y , digits , mode ) ) ;
2019-02-25 21:00:59 +01:00
}
public override string ToString ( ) {
2019-02-25 22:49:19 +01:00
return ToString ( null , null ) ;
2019-02-25 21:00:59 +01:00
}
public string ToVerbString ( ) {
string output = null ;
2019-02-25 22:49:19 +01:00
if ( isUnitVector ( ) ) {
2019-02-25 21:00:59 +01:00
output + = UNIT_VECTOR ;
}
else {
output + = POSITIONAL_VECTOR ;
}
2019-02-25 22:49:19 +01:00
output + = string . Format ( "( x={0}, y={1} )" , x , y ) ;
output + = magnitude + magnitude ;
2019-02-25 21:00:59 +01:00
return output ;
}
public string ToString ( string format , IFormatProvider formatProvider ) {
if ( format = = null | | format = = "" ) {
2019-02-25 22:49:19 +01:00
return string . Format ( "({0}, {1})" , x , y ) ;
2019-02-25 21:00:59 +01:00
}
char firstChar = format [ 0 ] ;
string remainder = null ;
if ( format . Length > 1 ) {
remainder = format . Substring ( 1 ) ;
}
switch ( firstChar ) {
2019-02-25 22:49:19 +01:00
case 'x' : return x . ToString ( remainder , formatProvider ) ;
case 'y' : return y . ToString ( remainder , formatProvider ) ;
2019-02-25 21:00:59 +01:00
default :
return String . Format (
"({0}, {1})" ,
2019-02-25 22:49:19 +01:00
x . ToString ( format , formatProvider ) ,
y . ToString ( format , formatProvider ) ) ;
2019-02-25 21:00:59 +01:00
}
}
public override int GetHashCode ( ) {
unchecked {
2019-02-25 22:49:19 +01:00
var hashCode = x . GetHashCode ( ) ;
hashCode = ( hashCode * 397 ) ^ y . GetHashCode ( ) ;
2019-02-25 21:00:59 +01:00
return hashCode ;
}
}
public override bool Equals ( object other ) {
if ( other is vec2 ) {
return other . Equals ( this ) ;
}
else {
return false ;
}
}
public bool Equals ( object other , double tolerance ) {
if ( other is vec2 ) {
2019-02-25 22:49:19 +01:00
return Equals ( ( vec2 ) other , tolerance ) ;
2019-02-25 21:00:59 +01:00
}
return false ;
}
public bool Equals ( vec2 other ) {
return
2019-02-25 22:49:19 +01:00
x . Equals ( other . x ) & &
y . Equals ( other . y ) ;
2019-02-25 21:00:59 +01:00
}
public bool Equals ( vec2 other , double tolerance ) {
return
2019-02-25 22:49:19 +01:00
x . almostEqualsWithAbsTolerance ( other . x , tolerance ) & &
y . almostEqualsWithAbsTolerance ( other . y , tolerance ) ;
2019-02-25 21:00:59 +01:00
}
public int CompareTo ( vec2 other ) {
if ( this < other ) {
return - 1 ;
}
if ( this > other ) {
return 1 ;
}
return 0 ;
}
public int CompareTo ( object other ) {
if ( other is vec2 ) {
2019-02-25 22:49:19 +01:00
return CompareTo ( ( vec2 ) other ) ;
2019-02-25 21:00:59 +01:00
}
throw new ArgumentException (
NON_VECTOR_COMPARISON + "\n" + ARGUMENT_TYPE + other . GetType ( ) . ToString ( ) ,
"other" ) ;
}
public int CompareTo ( vec2 other , double tolerance ) {
2019-02-25 22:49:19 +01:00
var bothInfinite = double . IsInfinity ( sumComponentSqrs ( ) ) & & double . IsInfinity ( other . sumComponentSqrs ( ) ) ;
2019-02-25 21:00:59 +01:00
2019-02-25 22:49:19 +01:00
if ( Equals ( other , tolerance ) | | bothInfinite ) {
2019-02-25 21:00:59 +01:00
return 0 ;
}
if ( this < other ) {
return - 1 ;
}
return 1 ;
}
public int CompareTo ( object other , double tolerance ) {
if ( other is vec2 ) {
2019-02-25 22:49:19 +01:00
return CompareTo ( ( vec2 ) other , tolerance ) ;
2019-02-25 21:00:59 +01:00
}
throw new ArgumentException (
NON_VECTOR_COMPARISON + "\n" + ARGUMENT_TYPE + other . GetType ( ) . ToString ( ) ,
"other" ) ;
}
public static bool isUnitVector ( vec2 v1 , double tolerance ) {
return v1 . magnitude . almostEqualsWithAbsTolerance ( 1 , tolerance ) ;
}
public static bool isUnitVector ( vec2 v1 ) {
return v1 . magnitude = = 1 ;
}
public bool isUnitVector ( ) {
return isUnitVector ( this ) ;
}
public bool isUnitVector ( double tolerance ) {
return isUnitVector ( this , tolerance ) ;
}
public static bool isNaN ( vec2 v1 ) {
return double . IsNaN ( v1 . x ) | | double . IsNaN ( v1 . y ) ;
}
public bool isNaN ( ) {
return isNaN ( this ) ;
}
public static readonly vec2 origin = new vec2 ( 0 , 0 ) ;
public static readonly vec2 right = new vec2 ( 1 , 0 ) ;
public static readonly vec2 left = new vec2 ( - 1 , 0 ) ;
public static readonly vec2 down = new vec2 ( 0 , 1 ) ;
public static readonly vec2 up = new vec2 ( 0 , - 1 ) ;
private const string NORMALIZE_NaN = "Cannot normalize a vector when it's magnitude is NaN" ;
private const string NORMALIZE_0 = "Cannot normalize a vector when it's magnitude is zero" ;
private const string NORMALIZE_Inf = "Cannot normalize a vector when it's magnitude is infinite except under special conditions" ;
private const string TWO_COMPONENTS = "Array must contain exactly two components (x, y)" ;
private const string INTERPOLATION_RANGE = "Control parameter must be a value between 0 & 1" ;
private const string NON_VECTOR_COMPARISON = "Cannot compare a vec2 to a non-vec2" ;
private const string ARGUMENT_TYPE = "The argument provided is a type of " ;
private const string ARGUMENT_VALUE = "The argument provided has a value of " ;
private const string ARGUMENT_LENGTH = "The argument provided has a length of " ;
private const string NEGATIVE_magnitude = "The magnitude of a vec2 must be a positive value, (i.e. greater than 0)" ;
private const string ORIGIN_VECTOR_magnitude = "Cannot change the magnitude of vec2(0, 0)" ;
private const string UNIT_VECTOR = "Unit vector composing of " ;
private const string POSITIONAL_VECTOR = "Positional vector composing of " ;
private const string MAGNITUDE = " of magnitude " ;
public static readonly vec2 minValue = new vec2 ( Double . MinValue , Double . MinValue ) ;
public static readonly vec2 maxValue = new vec2 ( Double . MaxValue , Double . MaxValue ) ;
public static readonly vec2 epsilon = new vec2 ( Double . Epsilon , Double . Epsilon ) ;
public static readonly vec2 zero = origin ;
public static readonly vec2 NaN = new vec2 ( double . NaN , double . NaN ) ;
}
}