-
-
Save vpenades/3d0b52e1bfc046191e685473ad006ab7 to your computer and use it in GitHub Desktop.
Angle struct POC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Represents a single-precision floating-point angle. | |
/// </summary> | |
/// <remarks> | |
/// Angle is stored in Radians. | |
/// </remarks> | |
[System.Diagnostics.DebuggerDisplay("{Degrees}°")] | |
public readonly struct Angle : IEquatable<Angle>, IComparable<Angle> | |
{ | |
#region constants | |
public const Single PI = (float)Math.PI; | |
public const Single PI2 = (float)(Math.PI * 2); // TWOPI | |
public const Single RadiansToDegreesScale = (Single)(180.0 / Math.PI); | |
public const Single DegreesToRadiansScale = (Single)(Math.PI / 180.0); | |
public static readonly Angle Zero = InRadians(0); | |
public static readonly Angle Positive90 = InDegrees(90); | |
public static readonly Angle Positive180 = InDegrees(180); | |
public static readonly Angle Positive270 = InDegrees(270); | |
public static readonly Angle Positive360 = InDegrees(360); | |
public static readonly Angle Negative90 = InDegrees(-90); | |
public static readonly Angle Negative180 = InDegrees(-180); | |
public static readonly Angle Negative270 = InDegrees(-270); | |
public static readonly Angle Negative360 = InDegrees(-360); | |
#endregion | |
#region lifecycle | |
public static Angle ArcSin(float value) { return InRadians( Math.Asin(value.Clamp(-1, 1)) ); } | |
public static Angle ArcCos(float value) { return InRadians( Math.Acos(value.Clamp(-1, 1)) ); } | |
public static Angle ArcTan(float value) { return InRadians( Math.Atan(value) ); } | |
public static Angle ArcTan(float y, float x) { return InRadians( Math.Atan2(y, x) ); } | |
public static Angle ArcTan(Vector2 vector) { return InRadians(Math.Atan2(vector.Y, vector.X)); } | |
public static Angle Radians(double radians) { return new Angle((float)radians); } | |
public static Angle InRadians(float radians) { return new Angle(radians); } | |
public static Angle InDegrees(float degrees) { return new Angle(degrees * DegreesToRadiansScale); } | |
public static Angle BetweenVectors(Vector2 a, Vector2 b) // Between | |
{ | |
a = Vector2.Normalize(a); | |
b = Vector2.Normalize(b); | |
return ArcCos(Vector2.Dot(a, b)); | |
} | |
public static Angle BetweenVectorsCW(Vector2 a, Vector2 b) // Between | |
{ | |
a = Vector2.Normalize(a); | |
b = Vector2.Normalize(b); | |
var c = a.X * b.Y - a.Y * b.X; // cross | |
var d = Vector2.Dot(a, b); | |
return c >= 0 | |
? ArcCos(d) | |
: -ArcCos(d); | |
} | |
public static Angle BetweenVectorsCCW(Vector2 a, Vector2 b) | |
{ | |
a = Vector2.Normalize(a); | |
b = Vector2.Normalize(b); | |
var c = a.X * b.Y - a.Y * b.X; // cross | |
var d = Vector2.Dot(a, b); | |
return c >= 0 | |
? -ArcCos(d) | |
: ArcCos(d); | |
} | |
public static Angle BetweenVectors(Vector3 a, Vector3 b) | |
{ | |
a = Vector3.Normalize(a); | |
b = Vector3.Normalize(b); | |
return ArcCos(Vector3.Dot(a, b)); | |
} | |
public Angle(float radians) { Radians = radians; } | |
#endregion | |
#region data | |
public readonly Single Radians; | |
public override int GetHashCode() { return Radians.GetHashCode(); } | |
public override bool Equals(object obj) { return obj is Angle other && this.Radians == other.Radians; } | |
public bool Equals(Angle other) { return this.Radians == other.Radians; } | |
public static bool operator ==(Angle a, Angle b) { return a.Radians == b.Radians; } | |
public static bool operator !=(Angle a, Angle b) { return a.Radians != b.Radians; } | |
public static bool operator <(Angle a, Angle b) { return a.Radians < b.Radians; } | |
public static bool operator >(Angle a, Angle b) { return a.Radians > b.Radians; } | |
public static bool operator <=(Angle a, Angle b) { return a.Radians <= b.Radians; } | |
public static bool operator >=(Angle a, Angle b) { return a.Radians >= b.Radians; } | |
public int CompareTo(Angle other) { return this.Radians.CompareTo(other.Radians); } | |
#endregion | |
#region properties | |
public bool IsFinite => !float.IsNaN(Radians) && !float.IsInfinity(Radians); | |
public Single Degrees => Radians * RadiansToDegreesScale; | |
#endregion | |
#region API | |
public Angle WithOffset(Angle angle) { return this + angle; } | |
public Angle Clamped(Angle min, Angle max) | |
{ | |
if (this.Radians < min.Radians) return min; | |
if (this.Radians > max.Radians) return max; | |
return this; | |
} | |
#endregion | |
#region operators | |
public static Angle operator +(Angle a) { return a; } | |
public static Angle operator -(Angle a) { return new Angle(-a.Radians); } | |
public static Angle operator +(Angle a, Angle b) { return new Angle(a.Radians + b.Radians); } | |
public static Angle operator -(Angle a, Angle b) { return new Angle(a.Radians - b.Radians); } | |
public static Angle operator *(Angle a, float multiplier) { return new Angle(a.Radians * multiplier); } | |
public static Angle operator *(float multiplier, Angle a) { return new Angle(a.Radians * multiplier); } | |
public static Angle operator /(Angle a, float divisor) { return new Angle(a.Radians / divisor); } | |
public static float operator /(Angle a, Angle b) { return a.Radians / b.Radians; } | |
#endregion | |
#region math API | |
public static Angle Negate(Angle angle) { return new Angle(-angle.Radians); } | |
public static Angle Abs(Angle angle) { return new Angle(Math.Abs(angle.Radians)); } | |
public static Angle Lerp(Angle a, Angle b, float amount) { return new Angle((a.Radians * (1 - amount)) + (b.Radians * amount)); } | |
public static Angle Max(Angle a, Angle b) { return a.Radians > b.Radians ? a : b; } | |
public static Angle Min(Angle a, Angle b) { return a.Radians < b.Radians ? a : b; } | |
/// <summary> | |
/// Normalizes the input value in the range of 0-360 degrees. | |
/// </summary> | |
/// <param name="angle">The input angle.</param> | |
/// <returns>The normalized angle.</returns> | |
public static Angle Normalize360(Angle angle) | |
{ | |
var r = angle.Radians % PI2; | |
if (r < 0) r += PI2; | |
return new Angle(r); | |
} | |
#endregion | |
#region System.Numerics Factory | |
public Vector2 CreateVectorCCW(float length) | |
{ | |
var x = + length * (float)Math.Cos(Radians); | |
var y = + length * (float)Math.Sin(Radians); | |
return new Vector2(x, y); | |
} | |
public Vector2 CreateVectorCW(float length) | |
{ | |
var x = + length * (float)Math.Cos(Radians); | |
var y = - length * (float)Math.Sin(Radians); | |
return new Vector2(x, y); | |
} | |
public Matrix3x2 CreateRotation() { return Matrix3x2.CreateRotation(Radians); } | |
public Matrix4x4 CreateRotationX() { return Matrix4x4.CreateRotationX(Radians); } | |
public Matrix4x4 CreateRotationY() { return Matrix4x4.CreateRotationY(Radians); } | |
public Matrix4x4 CreateRotationZ() { return Matrix4x4.CreateRotationZ(Radians); } | |
#endregion | |
#region nested types | |
private readonly struct _EqualityComparer : IEqualityComparer<Angle> | |
{ | |
public bool Equals(Angle x, Angle y) { return x.Radians == y.Radians; } | |
public int GetHashCode(Angle obj) { return obj.GetHashCode(); } | |
} | |
/// <summary> | |
/// Two angles pointing in the same "direction" are considered equal. | |
/// </summary> | |
private readonly struct _EquivalentComparer : IEqualityComparer<Angle> | |
{ | |
public bool Equals(Angle x, Angle y) { return Normalize360(x).Radians == Normalize360(y).Radians; } | |
public int GetHashCode(Angle obj) { return Normalize360(obj).GetHashCode(); } | |
} | |
public static IEqualityComparer<Angle> EqualityComparer => new _EqualityComparer(); | |
public static IEqualityComparer<Angle> EquivalentComparer => new _EquivalentComparer(); | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment