< Summary

Information
Class: FixedMathSharp.Geometry.FixedRay2d
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/Rays/FixedRay2d.cs
Line coverage
100%
Covered lines: 125
Uncovered lines: 0
Coverable lines: 125
Total lines: 412
Line coverage: 100%
Branch coverage
100%
Covered branches: 38
Total branches: 38
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetPoint(...)100%11100%
TryGetPoint(...)100%44100%
Intersects(...)100%44100%
Intersects(...)100%11100%
Intersects(...)100%11100%
Intersects(...)100%11100%
TryGetIntersectionInterval(...)100%11100%
TryGetIntersectionInterval(...)100%11100%
TryGetCapsuleIntersectionInterval(...)100%11100%
TryGetCapsuleIntersectionInterval(...)100%66100%
TryGetCapsuleIntersectionInterval(...)100%11100%
TryGetCapsuleIntersectionInterval(...)100%1010100%
Deconstruct(...)100%11100%
ClipAxis(...)100%1010100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
Equals(...)100%22100%
Equals(...)100%22100%
GetHashCode()100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Primitives/Rays/FixedRay2d.cs

#LineLine coverage
 1//=======================================================================
 2// FixedRay2d.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;
 10using System.Text.Json.Serialization;
 11using MemoryPack;
 12
 13namespace FixedMathSharp.Geometry;
 14
 15/// <summary>
 16/// Represents a ray with an origin and direction in two-dimensional fixed-point space.
 17/// </summary>
 18/// <remarks>
 19/// The direction is not normalized by construction. Intersection methods return
 20/// the ray parameter for the first forward hit; when <see cref="Direction"/> is
 21/// normalized, that parameter is also the distance from <see cref="Position"/>.
 22/// </remarks>
 23[Serializable]
 24[MemoryPackable]
 25public partial struct FixedRay2d : IEquatable<FixedRay2d>
 26{
 27    #region Fields
 28
 29    /// <summary>
 30    /// The origin of the ray.
 31    /// </summary>
 32    [JsonInclude]
 33    [MemoryPackOrder(0)]
 34    public Vector2d Position;
 35
 36    /// <summary>
 37    /// The direction of the ray.
 38    /// </summary>
 39    [JsonInclude]
 40    [MemoryPackOrder(1)]
 41    public Vector2d Direction;
 42
 43    #endregion
 44
 45    #region Constructors
 46
 47    /// <summary>
 48    /// Initializes a new ray with the specified origin and direction.
 49    /// </summary>
 50    [JsonConstructor]
 51    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 52    public FixedRay2d(Vector2d position, Vector2d direction)
 53    {
 7954        Position = position;
 7955        Direction = direction;
 7956    }
 57
 58    #endregion
 59
 60    #region Methods
 61
 62    /// <summary>
 63    /// Gets the point at the specified ray parameter.
 64    /// </summary>
 65    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 266    public Vector2d GetPoint(Fixed64 parameter) => new(
 267        Fixed64.MultiplyAdd(Direction.X, parameter, Position.X),
 268        Fixed64.MultiplyAdd(Direction.Y, parameter, Position.Y));
 69
 70    /// <summary>
 71    /// Attempts to get the point at the specified ray parameter with one final
 72    /// round-half-to-even conversion per coordinate.
 73    /// </summary>
 74    /// <param name="parameter">The parametric distance along the ray direction.</param>
 75    /// <param name="point">
 76    /// The point when every final coordinate is representable; otherwise,
 77    /// <see langword="default"/>.
 78    /// </param>
 79    /// <returns>
 80    /// <see langword="true"/> when every final coordinate is representable;
 81    /// otherwise, <see langword="false"/>.
 82    /// </returns>
 83    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 84    public readonly bool TryGetPoint(Fixed64 parameter, out Vector2d point)
 85    {
 286        if (!Fixed64.TryMultiplyAdd(Direction.X, parameter, Position.X, out Fixed64 x)
 287            || !Fixed64.TryMultiplyAdd(Direction.Y, parameter, Position.Y, out Fixed64 y))
 88        {
 189            point = default;
 190            return false;
 91        }
 92
 193        point = new Vector2d(x, y);
 194        return true;
 95    }
 96
 97    /// <summary>
 98    /// Finds the first forward intersection with the specified bounding area, including boundary-only contact.
 99    /// </summary>
 100    public Fixed64? Intersects(FixedBoundArea area)
 101    {
 14102        Fixed64 tMin = Fixed64.Zero;
 14103        Fixed64 tMax = Fixed64.MaxValue;
 104
 14105        if (!ClipAxis(Position.X, Direction.X, area.Min.X, area.Max.X, ref tMin, ref tMax))
 3106            return null;
 107
 11108        if (!ClipAxis(Position.Y, Direction.Y, area.Min.Y, area.Max.Y, ref tMin, ref tMax))
 1109            return null;
 110
 10111        return tMin;
 112    }
 113
 114    /// <summary>
 115    /// Finds the first forward intersection with the specified bounding circle, including boundary-only contact.
 116    /// </summary>
 117    public Fixed64? Intersects(FixedBoundCircle circle) =>
 21118        WideRayIntersection.Intersects(Position, Direction, circle, Fixed64.MaxValue);
 119
 120    /// <summary>
 121    /// Finds the first forward intersection with the specified bounding circle
 122    /// at or before <paramref name="maxParameter"/>.
 123    /// </summary>
 124    /// <remarks>
 125    /// Offset differences, quadratic products, the discriminant, and root
 126    /// ordering are evaluated without fixed-point saturation. The returned
 127    /// parameter uses deterministic round-half-to-even conversion.
 128    /// </remarks>
 129    public Fixed64? Intersects(FixedBoundCircle circle, Fixed64 maxParameter) =>
 5130        WideRayIntersection.Intersects(Position, Direction, circle, maxParameter);
 131
 132    /// <summary>
 133    /// Finds the first forward intersection with the specified bounding circle,
 134    /// expanded by <paramref name="radiusExpansion"/>, at or before
 135    /// <paramref name="maxParameter"/>.
 136    /// </summary>
 137    /// <remarks>
 138    /// The two radii are combined in wide arithmetic, so their sum may exceed
 139    /// <see cref="Fixed64.MaxValue"/> without saturation.
 140    /// </remarks>
 141    /// <exception cref="ArgumentOutOfRangeException">
 142    /// <paramref name="radiusExpansion"/> is negative.
 143    /// </exception>
 144    public Fixed64? Intersects(
 145        FixedBoundCircle circle,
 146        Fixed64 radiusExpansion,
 147        Fixed64 maxParameter) =>
 6148        WideRayIntersection.Intersects(Position, Direction, circle, radiusExpansion, maxParameter);
 149
 150    /// <summary>
 151    /// Gets the closed parameter interval where this ray overlaps the circle,
 152    /// clipped to <c>[0, <paramref name="maxParameter"/>]</c>.
 153    /// </summary>
 154    /// <remarks>
 155    /// Direction need not be normalized. Exact root clipping precedes
 156    /// deterministic round-half-to-even conversion of both endpoints.
 157    /// </remarks>
 158    public bool TryGetIntersectionInterval(
 159        FixedBoundCircle circle,
 160        Fixed64 maxParameter,
 161        out Fixed64 entry,
 162        out Fixed64 exit) =>
 21163        WideRayIntersection.TryGetInterval(
 21164            Position,
 21165            Direction,
 21166            circle,
 21167            maxParameter,
 21168            out entry,
 21169            out exit);
 170
 171    /// <summary>
 172    /// Gets the closed parameter interval where this ray overlaps the circle
 173    /// expanded by <paramref name="radiusExpansion"/>, clipped to
 174    /// <c>[0, <paramref name="maxParameter"/>]</c>.
 175    /// </summary>
 176    /// <exception cref="ArgumentOutOfRangeException">
 177    /// <paramref name="radiusExpansion"/> is negative.
 178    /// </exception>
 179    public bool TryGetIntersectionInterval(
 180        FixedBoundCircle circle,
 181        Fixed64 radiusExpansion,
 182        Fixed64 maxParameter,
 183        out Fixed64 entry,
 184        out Fixed64 exit) =>
 2185        WideRayIntersection.TryGetInterval(
 2186            Position,
 2187            Direction,
 2188            circle,
 2189            radiusExpansion,
 2190            maxParameter,
 2191            out entry,
 2192            out exit);
 193
 194    /// <summary>
 195    /// Gets the closed parameter interval where this ray overlaps a capsule,
 196    /// clipped to <c>[0, <paramref name="maxParameter"/>]</c>.
 197    /// </summary>
 198    public readonly bool TryGetCapsuleIntersectionInterval(
 199        FixedSegment2d capsuleAxis,
 200        Fixed64 radius,
 201        Fixed64 maxParameter,
 202        out Fixed64 entryParameter,
 203        out Fixed64 exitParameter) =>
 7204        TryGetCapsuleIntersectionInterval(
 7205            capsuleAxis,
 7206            radius,
 7207            Fixed64.Zero,
 7208            maxParameter,
 7209            out entryParameter,
 7210            out exitParameter,
 7211            out _,
 7212            out _);
 213
 214    /// <summary>
 215    /// Gets the closed parameter interval where this ray overlaps a radially
 216    /// expanded capsule and reports exact bounded-endpoint containment.
 217    /// </summary>
 218    /// <remarks>
 219    /// Direction need not be normalized. When it is normalized, returned
 220    /// parameters are physical distances. The origin is tested inclusively;
 221    /// the point at <paramref name="maxParameter"/> is tested strictly.
 222    /// </remarks>
 223    public readonly bool TryGetCapsuleIntersectionInterval(
 224        FixedSegment2d capsuleAxis,
 225        Fixed64 radius,
 226        Fixed64 radiusExpansion,
 227        Fixed64 maxParameter,
 228        out Fixed64 entryParameter,
 229        out Fixed64 exitParameter,
 230        out bool originContained,
 231        out bool maximumContainedStrict)
 232    {
 13233        if (radius < Fixed64.Zero)
 1234            throw new ArgumentOutOfRangeException(nameof(radius));
 12235        if (radiusExpansion < Fixed64.Zero)
 1236            throw new ArgumentOutOfRangeException(nameof(radiusExpansion));
 11237        if (maxParameter < Fixed64.Zero)
 238        {
 1239            entryParameter = default;
 1240            exitParameter = default;
 1241            originContained = false;
 1242            maximumContainedStrict = false;
 1243            return false;
 244        }
 245
 10246        return WideFiniteAxisIntersection.TryGetCapsuleInterval(
 10247            this,
 10248            maxParameter,
 10249            capsuleAxis,
 10250            radius,
 10251            radiusExpansion,
 10252            out entryParameter,
 10253            out exitParameter,
 10254            out originContained,
 10255            out maximumContainedStrict);
 256    }
 257
 258    /// <summary>
 259    /// Gets the closed parameter interval where this ray overlaps a centered
 260    /// capsule, clipped to <c>[0, <paramref name="maxParameter"/>]</c>.
 261    /// </summary>
 262    public readonly bool TryGetCapsuleIntersectionInterval(
 263        Vector2d center,
 264        Vector2d axisDirection,
 265        Fixed64 axisLength,
 266        Fixed64 radius,
 267        Fixed64 maxParameter,
 268        out Fixed64 entryParameter,
 269        out Fixed64 exitParameter) =>
 4270        TryGetCapsuleIntersectionInterval(
 4271            center,
 4272            axisDirection,
 4273            axisLength,
 4274            radius,
 4275            Fixed64.Zero,
 4276            maxParameter,
 4277            out entryParameter,
 4278            out exitParameter,
 4279            out _,
 4280            out _);
 281
 282    /// <summary>
 283    /// Gets the closed parameter interval where this ray overlaps a centered,
 284    /// radially expanded capsule and reports exact endpoint containment.
 285    /// </summary>
 286    public readonly bool TryGetCapsuleIntersectionInterval(
 287        Vector2d center,
 288        Vector2d axisDirection,
 289        Fixed64 axisLength,
 290        Fixed64 radius,
 291        Fixed64 radiusExpansion,
 292        Fixed64 maxParameter,
 293        out Fixed64 entryParameter,
 294        out Fixed64 exitParameter,
 295        out bool originContained,
 296        out bool maximumContainedStrict)
 297    {
 9298        if (!axisDirection.IsNormalized())
 1299            throw new ArgumentException("Capsule axis direction must be normalized.", nameof(axisDirection));
 8300        if (axisLength < Fixed64.Zero)
 1301            throw new ArgumentOutOfRangeException(nameof(axisLength));
 7302        if (radius < Fixed64.Zero)
 1303            throw new ArgumentOutOfRangeException(nameof(radius));
 6304        if (radiusExpansion < Fixed64.Zero)
 1305            throw new ArgumentOutOfRangeException(nameof(radiusExpansion));
 5306        if (maxParameter < Fixed64.Zero)
 307        {
 1308            entryParameter = default;
 1309            exitParameter = default;
 1310            originContained = false;
 1311            maximumContainedStrict = false;
 1312            return false;
 313        }
 314
 4315        return WideFiniteAxisIntersection.TryGetCapsuleInterval(
 4316            this,
 4317            maxParameter,
 4318            center,
 4319            axisDirection,
 4320            axisLength,
 4321            radius,
 4322            radiusExpansion,
 4323            out entryParameter,
 4324            out exitParameter,
 4325            out originContained,
 4326            out maximumContainedStrict);
 327    }
 328
 329    /// <summary>
 330    /// Deconstructs the ray into origin and direction.
 331    /// </summary>
 332    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 333    public void Deconstruct(out Vector2d position, out Vector2d direction)
 334    {
 1335        position = Position;
 1336        direction = Direction;
 1337    }
 338
 339    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 340    private static bool ClipAxis(
 341        Fixed64 position,
 342        Fixed64 direction,
 343        Fixed64 min,
 344        Fixed64 max,
 345        ref Fixed64 tMin,
 346        ref Fixed64 tMax)
 347    {
 25348        if (direction == Fixed64.Zero)
 11349            return position >= min && position <= max;
 350
 14351        Fixed64 t1 = (min - position) / direction;
 14352        Fixed64 t2 = (max - position) / direction;
 353
 14354        if (t1 > t2)
 3355            (t2, t1) = (t1, t2);
 356
 14357        if (t1 > tMin)
 8358            tMin = t1;
 359
 14360        if (t2 < tMax)
 11361            tMax = t2;
 362
 14363        return tMin <= tMax;
 364    }
 365
 366    #endregion
 367
 368    #region Operators
 369
 370    /// <summary>
 371    /// Determines whether two rays are equal.
 372    /// </summary>
 373    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2374    public static bool operator ==(FixedRay2d left, FixedRay2d right) => left.Equals(right);
 375
 376    /// <summary>
 377    /// Determines whether two rays are not equal.
 378    /// </summary>
 379    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2380    public static bool operator !=(FixedRay2d left, FixedRay2d right) => !left.Equals(right);
 381
 382    #endregion
 383
 384    #region Equality
 385
 386    /// <inheritdoc />
 387    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 388    public bool Equals(FixedRay2d other)
 389    {
 9390        return Position == other.Position && Direction == other.Direction;
 391    }
 392
 393    /// <inheritdoc />
 394    public override bool Equals(object? obj)
 395    {
 2396        return obj is FixedRay2d other && Equals(other);
 397    }
 398
 399    /// <inheritdoc />
 400    public override int GetHashCode()
 401    {
 402        unchecked
 403        {
 2404            int hash = 17;
 2405            hash = (hash * 31) + Position.StateHash;
 2406            hash = (hash * 31) + Direction.StateHash;
 2407            return hash;
 408        }
 409    }
 410
 411    #endregion
 412}

Methods/Properties

.ctor(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d)
GetPoint(FixedMathSharp.Fixed64)
TryGetPoint(FixedMathSharp.Fixed64,FixedMathSharp.Vector2d&)
Intersects(FixedMathSharp.Geometry.FixedBoundArea)
Intersects(FixedMathSharp.Geometry.FixedBoundCircle)
Intersects(FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64)
Intersects(FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
TryGetIntersectionInterval(FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetIntersectionInterval(FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetCapsuleIntersectionInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetCapsuleIntersectionInterval(FixedMathSharp.Geometry.FixedSegment2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
TryGetCapsuleIntersectionInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetCapsuleIntersectionInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&,System.Boolean&,System.Boolean&)
Deconstruct(FixedMathSharp.Vector2d&,FixedMathSharp.Vector2d&)
ClipAxis(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
op_Equality(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Geometry.FixedRay2d)
op_Inequality(FixedMathSharp.Geometry.FixedRay2d,FixedMathSharp.Geometry.FixedRay2d)
Equals(FixedMathSharp.Geometry.FixedRay2d)
Equals(System.Object)
GetHashCode()