diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 97198b19c..a525e5c13 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -58,6 +58,7 @@ END TEMPLATE-->
* Simplify and optimise Box2.Contains(Vector2)
* Optimise ComponentRegistry deserialization slightly.
* Optimise Box2Rotated.TransformBox slightly.
+* Update Angle to use double constantly internally and optimise some methods.
## 277.1.0
diff --git a/Robust.Shared.Maths/Angle.cs b/Robust.Shared.Maths/Angle.cs
index 27c00e496..1a967f775 100644
--- a/Robust.Shared.Maths/Angle.cs
+++ b/Robust.Shared.Maths/Angle.cs
@@ -1,6 +1,5 @@
using System;
using System.Numerics;
-using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using Robust.Shared.Utility;
@@ -19,6 +18,9 @@ namespace Robust.Shared.Maths
///
public readonly double Theta;
+ private const double PiOver2 = Math.PI / 2.0;
+ private const double TwoPi = 2.0 * Math.PI;
+
///
/// Angle in degrees.
///
@@ -44,7 +46,7 @@ public Angle(Vector2 dir)
public static Angle FromWorldVec(Vector2 dir)
{
- return new Angle(dir) + Math.PI / 2;
+ return new Angle(Math.Atan2(dir.Y, dir.X) + PiOver2);
}
///
@@ -56,52 +58,51 @@ public static Angle FromWorldVec(Vector2 dir)
/// where an angle of zero is usually considered "south" (0, -1).
///
/// Unit Direction Vector
- public readonly Vector2 ToVec()
+ public Vector2 ToVec()
{
var x = Math.Cos(Theta);
var y = Math.Sin(Theta);
return new Vector2((float) x, (float) y);
}
- public readonly Vector2 ToWorldVec()
+ public Vector2 ToWorldVec()
{
- return (this - MathHelper.PiOver2).ToVec();
+ var theta = Theta - PiOver2;
+ var x = Math.Cos(theta);
+ var y = Math.Sin(theta);
+ return new Vector2((float) x, (float) y);
}
private const double Segment = 2 * Math.PI / 8.0; // Cut the circle into 8 pieces
private const double Offset = Segment / 2.0; // offset the pieces by 1/2 their size
+ private const double InvSegment = 1.0 / Segment;
- public readonly Direction GetDir()
+ public Direction GetDir()
{
- var ang = Theta % (2 * Math.PI);
-
- if (ang < 0) // convert -PI > PI to 0 > 2PI
- ang += 2 * Math.PI;
-
- return (Direction) (Math.Floor((ang + Offset) / Segment) % 8);
+ return GetDir(Theta);
}
public Direction RotateDir(Direction dir)
{
- var ang = (Theta + Segment * (int)dir) % (2 * Math.PI);
+ var ang = (Theta + Segment * (int)dir) % TwoPi;
if (ang < 0)
- ang += 2 * Math.PI;
+ ang += TwoPi;
- return (Direction)(Math.Floor((ang + Offset) / Segment) % 8);
+ return (Direction) ((int) ((ang + Offset) * InvSegment) & 7);
}
private const double CardinalSegment = 2 * Math.PI / 4.0; // Cut the circle into 4 pieces
private const double CardinalOffset = CardinalSegment / 2.0; // offset the pieces by 1/2 their size
+ private const double InvCardinalSegment = 1.0 / CardinalSegment;
[Pure]
- public readonly Direction GetCardinalDir()
+ public Direction GetCardinalDir()
{
- var ang = Theta % (2 * Math.PI);
-
- if (ang < 0.0f) // convert -PI > PI to 0 > 2PI
- ang += 2 * Math.PI;
+ var ang = Theta % TwoPi;
+ if (ang < 0) // convert -PI > PI to 0 > 2PI
+ ang += TwoPi;
- return (Direction) (Math.Floor((ang + CardinalOffset) / CardinalSegment) * 2 % 8);
+ return (Direction) (((int) ((ang + CardinalOffset) * InvCardinalSegment) * 2) & 7);
}
///
@@ -120,7 +121,7 @@ public Angle RoundToCardinalAngle()
/// Vector to rotate.
/// New rotated vector.
[Pure]
- public readonly Vector2 RotateVec(in Vector2 vec)
+ public Vector2 RotateVec(in Vector2 vec)
{
// No calculation necessery when theta is zero
if (Theta == 0) return vec;
@@ -155,8 +156,8 @@ private static bool EqualsApprox(Angle a, Angle b)
// The second two expressions cover an edge case where one number is barely non-negative while the other number is negative.
// In this case, the negative number will get FlipPositived to ~2pi and the comparison will give a false negative.
return MathHelper.CloseToPercent(aPositive, bPositive)
- || MathHelper.CloseToPercent(aPositive + MathHelper.TwoPi, bPositive)
- || MathHelper.CloseToPercent(aPositive, bPositive + MathHelper.TwoPi);
+ || MathHelper.CloseToPercent(aPositive + TwoPi, bPositive)
+ || MathHelper.CloseToPercent(aPositive, bPositive + TwoPi);
}
private static bool EqualsApprox(Angle a, Angle b, double tolerance)
@@ -171,15 +172,15 @@ private static bool EqualsApprox(Angle a, Angle b, double tolerance)
// The second two expressions cover an edge case where one number is barely non-negative while the other number is negative.
// In this case, the negative number will get FlipPositived to ~2pi and the comparison will give a false negative.
return MathHelper.CloseToPercent(aPositive, bPositive, tolerance)
- || MathHelper.CloseToPercent(aPositive + MathHelper.TwoPi, bPositive, tolerance)
- || MathHelper.CloseToPercent(aPositive, bPositive + MathHelper.TwoPi, tolerance);
+ || MathHelper.CloseToPercent(aPositive + TwoPi, bPositive, tolerance)
+ || MathHelper.CloseToPercent(aPositive, bPositive + TwoPi, tolerance);
}
///
/// Removes revolutions from a positive or negative angle to make it as small as possible.
///
[Pure]
- public readonly Angle Reduced()
+ public Angle Reduced()
{
return new(Reduce(Theta));
}
@@ -190,29 +191,26 @@ public readonly Angle Reduced()
private static double Reduce(double theta)
{
// int truncates value (round to 0)
- var aTurns = (int) (theta / (2 * Math.PI));
- return theta - aTurns * (2 * Math.PI);
+ var aTurns = (int) (theta / TwoPi);
+ return theta - aTurns * TwoPi;
}
///
- public readonly bool Equals(Angle other)
+ public bool Equals(Angle other)
{
return Theta.Equals(other.Theta);
}
///
- public readonly override bool Equals(object? obj)
+ public override bool Equals(object? obj)
{
- if (ReferenceEquals(null, obj)) return false;
return obj is Angle angle && Equals(angle);
}
///
- public readonly override int GetHashCode()
+ public override int GetHashCode()
{
-
return Theta.GetHashCode();
-
}
public static bool operator ==(Angle a, Angle b)
@@ -226,13 +224,13 @@ public readonly override int GetHashCode()
}
[Pure]
- public readonly Angle Opposite()
+ public Angle Opposite()
{
- return new Angle(FlipPositive(Theta-Math.PI));
+ return new Angle(FlipPositive(Theta - Math.PI));
}
[Pure]
- public readonly Angle FlipPositive()
+ public Angle FlipPositive()
{
return new(FlipPositive(Theta));
}
@@ -245,7 +243,16 @@ private static double FlipPositive(double theta)
if (theta >= 0)
return theta;
- return theta + 2 * Math.PI;
+ return theta + TwoPi;
+ }
+
+ private static Direction GetDir(double theta)
+ {
+ var ang = theta % TwoPi;
+ if (ang < 0) // convert -PI > PI to 0 > 2PI
+ ang += TwoPi;
+
+ return (Direction) ((int) ((ang + Offset) * InvSegment) & 7);
}
///
@@ -262,8 +269,8 @@ public static Angle Lerp(in Angle a, in Angle b, float factor)
///
public static Angle ShortestDistance(in Angle a, in Angle b)
{
- var delta = (b - a) % Math.Tau;
- return 2 * delta % Math.Tau - delta;
+ var delta = (b - a) % TwoPi;
+ return 2 * delta % TwoPi - delta;
}
///