< Summary

Information
Line coverage
100%
Covered lines: 62
Uncovered lines: 0
Coverable lines: 62
Total lines: 240
Line coverage: 100%
Branch coverage
100%
Covered branches: 32
Total branches: 32
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Canonical()100%11100%
get_PositiveZForward()100%11100%
get_NegativeZForward()100%11100%
get_XForwardZUp()100%11100%
.ctor(...)100%1212100%
get_Right()100%11100%
get_Left()100%11100%
get_Up()100%11100%
get_Down()100%11100%
get_Forward()100%11100%
get_Backward()100%11100%
ToCanonicalDirection(...)100%11100%
FromCanonicalDirection(...)100%11100%
Equals(...)100%44100%
Equals(...)100%22100%
GetHashCode()100%11100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
AxisToVector(...)100%66100%
GetSignedComponent(...)100%66100%
GetAbsoluteAxis(...)100%11100%
IsDefinedAxis(...)100%22100%
IsPositiveAxis(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/CoordinateConventions/CoordinateConvention3d.cs

#LineLine coverage
 1//=======================================================================
 2// CoordinateConvention3d.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Runtime.CompilerServices;
 10
 11namespace FixedMathSharp;
 12
 13/// <summary>
 14/// Describes a stateless 3D direction convention relative to FixedMathSharp's canonical basis.
 15/// </summary>
 16/// <remarks>
 17/// FixedMathSharp's canonical 3D basis is <c>+X</c> right, <c>+Y</c> up, and <c>+Z</c> forward.
 18/// This type maps direction vector components between another explicit right/up/forward axis
 19/// basis and the FixedMathSharp canonical basis. Matrix storage, transform APIs, clip-space
 20/// depth, units, and origins remain adapter-level concerns.
 21/// </remarks>
 22public readonly struct CoordinateConvention3d : IEquatable<CoordinateConvention3d>
 23{
 24    /// <summary>
 25    /// The FixedMathSharp canonical convention: <c>+X</c> right, <c>+Y</c> up, <c>+Z</c> forward.
 26    /// </summary>
 227    public static CoordinateConvention3d Canonical => PositiveZForward;
 28
 29    /// <summary>
 30    /// The FixedMathSharp canonical convention: <c>+X</c> right, <c>+Y</c> up, <c>+Z</c> forward.
 31    /// </summary>
 532    public static CoordinateConvention3d PositiveZForward => new(Axis3d.PositiveX, Axis3d.PositiveY, Axis3d.PositiveZ);
 33
 34    /// <summary>
 35    /// A convention with <c>+X</c> right, <c>+Y</c> up, and <c>-Z</c> forward.
 36    /// </summary>
 437    public static CoordinateConvention3d NegativeZForward => new(Axis3d.PositiveX, Axis3d.PositiveY, Axis3d.NegativeZ);
 38
 39    /// <summary>
 40    /// A convention with <c>+Y</c> right, <c>+Z</c> up, and <c>+X</c> forward.
 41    /// </summary>
 342    public static CoordinateConvention3d XForwardZUp => new(Axis3d.PositiveY, Axis3d.PositiveZ, Axis3d.PositiveX);
 43
 44    /// <summary>
 45    /// Initializes a new convention with the specified semantic right, up, and forward axes.
 46    /// </summary>
 47    /// <param name="rightAxis">The signed axis used as semantic right.</param>
 48    /// <param name="upAxis">The signed axis used as semantic up.</param>
 49    /// <param name="forwardAxis">The signed axis used as semantic forward.</param>
 50    /// <exception cref="ArgumentOutOfRangeException">
 51    /// Thrown when any axis is not a defined <see cref="Axis3d"/> value.
 52    /// </exception>
 53    /// <exception cref="ArgumentException">
 54    /// Thrown when two semantic axes use the same absolute axis.
 55    /// </exception>
 56    public CoordinateConvention3d(Axis3d rightAxis, Axis3d upAxis, Axis3d forwardAxis)
 57    {
 2458        if (!IsDefinedAxis(rightAxis))
 259            throw new ArgumentOutOfRangeException(nameof(rightAxis), rightAxis, "Right axis must be a defined 3D axis.")
 60
 2261        if (!IsDefinedAxis(upAxis))
 162            throw new ArgumentOutOfRangeException(nameof(upAxis), upAxis, "Up axis must be a defined 3D axis.");
 63
 2164        if (!IsDefinedAxis(forwardAxis))
 165            throw new ArgumentOutOfRangeException(nameof(forwardAxis), forwardAxis, "Forward axis must be a defined 3D a
 66
 2067        int absoluteRightAxis = GetAbsoluteAxis(rightAxis);
 2068        int absoluteUpAxis = GetAbsoluteAxis(upAxis);
 2069        int absoluteForwardAxis = GetAbsoluteAxis(forwardAxis);
 70
 2071        if (absoluteRightAxis == absoluteUpAxis)
 172            throw new ArgumentException("Right and up axes must use different absolute axes.", nameof(upAxis));
 73
 1974        if (absoluteRightAxis == absoluteForwardAxis)
 175            throw new ArgumentException("Right and forward axes must use different absolute axes.", nameof(forwardAxis))
 76
 1877        if (absoluteUpAxis == absoluteForwardAxis)
 178            throw new ArgumentException("Up and forward axes must use different absolute axes.", nameof(forwardAxis));
 79
 1780        RightAxis = rightAxis;
 1781        UpAxis = upAxis;
 1782        ForwardAxis = forwardAxis;
 1783    }
 84
 85    /// <summary>
 86    /// Gets the signed axis used as semantic right in this convention.
 87    /// </summary>
 88    public Axis3d RightAxis { get; }
 89
 90    /// <summary>
 91    /// Gets the signed axis used as semantic up in this convention.
 92    /// </summary>
 93    public Axis3d UpAxis { get; }
 94
 95    /// <summary>
 96    /// Gets the signed axis used as semantic forward in this convention.
 97    /// </summary>
 98    public Axis3d ForwardAxis { get; }
 99
 100    /// <summary>
 101    /// Gets the semantic right direction expressed in this convention's component basis.
 102    /// </summary>
 103    public Vector3d Right
 104    {
 105        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 8106        get => AxisToVector(RightAxis);
 107    }
 108
 109    /// <summary>
 110    /// Gets the semantic left direction expressed in this convention's component basis.
 111    /// </summary>
 112    public Vector3d Left
 113    {
 114        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 4115        get => -Right;
 116    }
 117
 118    /// <summary>
 119    /// Gets the semantic up direction expressed in this convention's component basis.
 120    /// </summary>
 121    public Vector3d Up
 122    {
 123        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 8124        get => AxisToVector(UpAxis);
 125    }
 126
 127    /// <summary>
 128    /// Gets the semantic down direction expressed in this convention's component basis.
 129    /// </summary>
 130    public Vector3d Down
 131    {
 132        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 4133        get => -Up;
 134    }
 135
 136    /// <summary>
 137    /// Gets the semantic forward direction expressed in this convention's component basis.
 138    /// </summary>
 139    public Vector3d Forward
 140    {
 141        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 8142        get => AxisToVector(ForwardAxis);
 143    }
 144
 145    /// <summary>
 146    /// Gets the semantic backward direction expressed in this convention's component basis.
 147    /// </summary>
 148    public Vector3d Backward
 149    {
 150        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 4151        get => -Forward;
 152    }
 153
 154    /// <summary>
 155    /// Converts a direction vector from this convention into FixedMathSharp's canonical convention.
 156    /// </summary>
 157    /// <param name="direction">The direction vector expressed in this convention.</param>
 158    /// <returns>The same semantic direction expressed in the canonical <c>+Z</c>-forward convention.</returns>
 159    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 25160    public Vector3d ToCanonicalDirection(Vector3d direction) => new(
 25161        GetSignedComponent(direction, RightAxis),
 25162        GetSignedComponent(direction, UpAxis),
 25163        GetSignedComponent(direction, ForwardAxis));
 164
 165    /// <summary>
 166    /// Converts a direction vector from FixedMathSharp's canonical convention into this convention.
 167    /// </summary>
 168    /// <param name="canonicalDirection">The direction vector expressed in FixedMathSharp's canonical convention.</param
 169    /// <returns>The same semantic direction expressed in this convention.</returns>
 170    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 171    public Vector3d FromCanonicalDirection(Vector3d canonicalDirection) =>
 22172        (AxisToVector(RightAxis) * canonicalDirection.X) +
 22173        (AxisToVector(UpAxis) * canonicalDirection.Y) +
 22174        (AxisToVector(ForwardAxis) * canonicalDirection.Z);
 175
 176    /// <inheritdoc />
 177    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 178    public bool Equals(CoordinateConvention3d other) =>
 10179        RightAxis == other.RightAxis &&
 10180        UpAxis == other.UpAxis &&
 10181        ForwardAxis == other.ForwardAxis;
 182
 183    /// <inheritdoc />
 3184    public override bool Equals(object? obj) => obj is CoordinateConvention3d other && Equals(other);
 185
 186    /// <inheritdoc />
 187    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 188    public override int GetHashCode() =>
 2189        ((int)RightAxis * 397) ^
 2190        ((int)UpAxis * 31) ^
 2191        (int)ForwardAxis;
 192
 193    /// <summary>
 194    /// Determines whether two conventions are equal.
 195    /// </summary>
 196    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2197    public static bool operator ==(CoordinateConvention3d left, CoordinateConvention3d right) => left.Equals(right);
 198
 199    /// <summary>
 200    /// Determines whether two conventions are not equal.
 201    /// </summary>
 202    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2203    public static bool operator !=(CoordinateConvention3d left, CoordinateConvention3d right) => !left.Equals(right);
 204
 205    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 206    private static Vector3d AxisToVector(Axis3d axis)
 207    {
 90208        Fixed64 value = IsPositiveAxis(axis) ? Fixed64.One : -Fixed64.One;
 90209        int absoluteAxis = GetAbsoluteAxis(axis);
 210
 90211        if (absoluteAxis == 0)
 30212            return new Vector3d(value, Fixed64.Zero, Fixed64.Zero);
 213
 60214        return absoluteAxis == 1
 60215            ? new Vector3d(Fixed64.Zero, value, Fixed64.Zero)
 60216            : new Vector3d(Fixed64.Zero, Fixed64.Zero, value);
 217    }
 218
 219    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 220    private static Fixed64 GetSignedComponent(Vector3d direction, Axis3d axis)
 221    {
 75222        int absoluteAxis = GetAbsoluteAxis(axis);
 75223        Fixed64 component = absoluteAxis == 0
 75224            ? direction.X
 75225            : absoluteAxis == 1
 75226                ? direction.Y
 75227                : direction.Z;
 228
 75229        return IsPositiveAxis(axis) ? component : -component;
 230    }
 231
 232    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 225233    private static int GetAbsoluteAxis(Axis3d axis) => (int)axis >> 1;
 234
 235    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 67236    private static bool IsDefinedAxis(Axis3d axis) => axis >= Axis3d.PositiveX && axis <= Axis3d.NegativeZ;
 237
 238    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 165239    private static bool IsPositiveAxis(Axis3d axis) => ((int)axis & 1) == 0;
 240}