Skip to content
This repository was archived by the owner on Jun 28, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
89 changes: 48 additions & 41 deletions Robust.Shared.Maths/Angle.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using Robust.Shared.Utility;

Expand All @@ -19,6 +18,9 @@ namespace Robust.Shared.Maths
/// </summary>
public readonly double Theta;

private const double PiOver2 = Math.PI / 2.0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you sure the compiler does not already do this for you as a very obvious optimization?

private const double TwoPi = 2.0 * Math.PI;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just use Math.Tau


/// <summary>
/// Angle in degrees.
/// </summary>
Expand All @@ -44,7 +46,7 @@ public Angle(Vector2 dir)

public static Angle FromWorldVec(Vector2 dir)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentation.
Make sure to mention why this is different for world vectors

{
return new Angle(dir) + Math.PI / 2;
return new Angle(Math.Atan2(dir.Y, dir.X) + PiOver2);
}

/// <summary>
Expand All @@ -56,52 +58,51 @@ public static Angle FromWorldVec(Vector2 dir)
/// where an angle of zero is usually considered "south" (0, -1).
/// </remarks>
/// <returns>Unit Direction Vector</returns>
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()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add documentation while you are at it

{
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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new Vector2((float) x, (float) y);
return new Vector2((float)x, (float)y);

formatting convention
isn't your IDE reading the .editorconfig correctly? This should be a warning

}

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure the compiler doesn't just do this for you?


public readonly Direction GetDir()
public Direction GetDir()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs

{
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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs

{
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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (Direction) ((int) ((ang + Offset) * InvSegment) & 7);
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()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs

{
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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (Direction) (((int) ((ang + CardinalOffset) * InvCardinalSegment) * 2) & 7);
return (Direction) (((int)((ang + CardinalOffset) * InvCardinalSegment) * 2) & 7);

}

/// <summary>
Expand All @@ -120,7 +121,7 @@ public Angle RoundToCardinalAngle()
/// <param name="vec">Vector to rotate.</param>
/// <returns>New rotated vector.</returns>
[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;
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}

/// <summary>
/// Removes revolutions from a positive or negative angle to make it as small as possible.
/// </summary>
[Pure]
public readonly Angle Reduced()
public Angle Reduced()
{
return new(Reduce(Theta));
}
Expand All @@ -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;
}

/// <inheritdoc />
public readonly bool Equals(Angle other)
public bool Equals(Angle other)
{
return Theta.Equals(other.Theta);
}

/// <inheritdoc />
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);
}

/// <inheritdoc />
public readonly override int GetHashCode()
public override int GetHashCode()
{

return Theta.GetHashCode();

}

public static bool operator ==(Angle a, Angle b)
Expand All @@ -226,13 +224,13 @@ public readonly override int GetHashCode()
}

[Pure]
public readonly Angle Opposite()
public Angle Opposite()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs, same below

{
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));
}
Expand All @@ -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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (Direction) ((int) ((ang + Offset) * InvSegment) & 7);
return (Direction)((int) ((ang + Offset) * InvSegment) & 7);

}

/// <summary>
Expand All @@ -262,8 +269,8 @@ public static Angle Lerp(in Angle a, in Angle b, float factor)
/// </summary>
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;
}

/// <summary>
Expand Down
Loading