I would like to ask about the code below:
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct AngleDeg
{
public readonly double Degrees; // immutable struct
public AngleDeg(double degrees = 0)
{
Degrees = degrees;
}
public override string ToString()
{
return $"{Degrees:F2}°";
}
#region Conversion
public static implicit operator AngleDeg(double rawAngleInDeg)
{
return new AngleDeg(rawAngleInDeg);
}
public static implicit operator double(AngleDeg v)
{
return v.Degrees;
}
// TODO: Implement arithmetic operations with operator overloads
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public AngleRad ToRadians()
{
// I could have used an explicit cast operator overload, but I think that a simple cast is not verbose/indicative enough.
return new AngleRad(Degrees / 180.0 * System.Math.PI);
}
#endregion
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct AngleRad
{
public readonly double Radians; // immutable struct
public AngleRad(double radians = 0)
{
Radians = radians;
}
public override string ToString()
{
// I want the debugger to display angles in rounded degrees. Much easier to picture angles-in-degrees in my head
return $"{Radians / System.Math.PI * 180:F2}°";
}
#region Conversion
public static implicit operator AngleRad(double rawAngleInRad)
{
return new AngleRad(rawAngleInRad);
}
public static implicit operator double(AngleRad v)
{
return v.Radians;
}
// TODO: Implement arithmetic operations with operator overloads
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public AngleDeg ToDegrees()
{
// I could have used an explicit cast operator overload, but I think that a simple cast is not verbose/indicative enough.
return new AngleDeg(Radians / System.Math.PI * 180);
}
#endregion
}
We have tons of geometry related code in our codebase with lots of calculations including angles. We use double-precision floating point numbers to represent angles right now.
We usually measure angles in radians during the calculations, mainly because they all boil down to System.Math
's static methods which expect angles to be in radians. However, it is a nuisance to debug such a code, because a double in the range of 0 and 2PI is nowhere near as informative as an angle in the range of 0 and 360°. Also when these angles displayed to a user, or a user has to type in an angle we always have to use degrees. A conversion must happen somewhere between the GUI-related layers and the maths calculation layers.
These are the pros I can think of:
- ToString() override always displays angle in degrees (great help for the debugger)
- Compile time checking of incommensurate angles on the boundaries of code using degrees and code using radians.
- Can write some handy utility methods, like ClampInRangeOf0And360Degrees().
Possible cons:
- Performance cost? Is there any?
- All codes using these structs will have a dependency on them. Using System.Double is maybe more 'reusable code'
The question is: What do you think of the possible drawbacks, pitfalls if we used these structs in a performance critical application? What about readability and code smell?
AngleDeg
andAngleRad
. Are you really saving anything from doing this? Most people would just typeAngleD
and hit tab anyways, soAngleDegree
/AngleRadian
reads properly and doesn't change anything in that case. \$\endgroup\$Angle
class with a double Value property and a Type enum for either Degrees or Radians. \$\endgroup\$ToDegrees()
andToRadians()
in each others'struct
s, why not have an implicit conversion to/from as you do withdouble
? \$\endgroup\$in the range of 0 and 2PI [...] in the range of 0 and 360°
Do you allow angles to be negative? \$\endgroup\$