< Summary

Information
Class: FixedMathSharp.FixedRange
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/FixedRange.cs
Line coverage
100%
Covered lines: 61
Uncovered lines: 0
Coverable lines: 61
Total lines: 351
Line coverage: 100%
Branch coverage
100%
Covered branches: 42
Total branches: 42
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%66100%
get_Length()100%22100%
get_MidPoint()100%11100%
SetMinMax(...)100%11100%
AddInPlace(...)100%11100%
InRange(...)100%66100%
Overlaps(...)100%22100%
GetDirection(...)100%44100%
ComputeOverlapDepth(...)100%1010100%
CheckOverlap(...)100%22100%
op_Addition(...)100%11100%
op_Subtraction(...)100%11100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%
ToString()100%11100%
ToString(...)100%11100%
TryFormat(...)100%66100%
Equals(...)100%22100%
Equals(...)100%22100%
GetHashCode()100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Numerics/Scalars/FixedRange.cs

#LineLine coverage
 1//=======================================================================
 2// FixedRange.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.Globalization;
 10using System.Runtime.CompilerServices;
 11using System.Text.Json.Serialization;
 12using MemoryPack;
 13
 14namespace FixedMathSharp;
 15
 16/// <summary>
 17/// Represents a range of values with fixed precision.
 18/// </summary>
 19[Serializable]
 20[MemoryPackable]
 21public partial struct FixedRange : IEquatable<FixedRange>, IFormattable
 22#if NET8_0_OR_GREATER
 23    , ISpanFormattable
 24#endif
 25{
 26    #region Static Readonly Fields
 27
 28    /// <summary>
 29    /// The smallest possible range.
 30    /// </summary>
 131    public static readonly FixedRange MinRange = new(Fixed64.MinValue, Fixed64.MinValue);
 32
 33    /// <summary>
 34    /// The largest possible range.
 35    /// </summary>
 136    public static readonly FixedRange MaxRange = new(Fixed64.MaxValue, Fixed64.MaxValue);
 37
 38    #endregion
 39
 40    #region Fields
 41
 42    /// <summary>
 43    /// Gets the minimum value of the range.
 44    /// </summary>
 45    [JsonInclude]
 46    [MemoryPackOrder(0)]
 47    public Fixed64 Min;
 48
 49    /// <summary>
 50    /// Gets the maximum value of the range.
 51    /// </summary>
 52    [JsonInclude]
 53    [MemoryPackOrder(1)]
 54    public Fixed64 Max;
 55
 56    #endregion
 57
 58    #region Constructors
 59
 60    /// <summary>
 61    /// Initializes a new instance of the FixedRange structure with the specified minimum and maximum values.
 62    /// </summary>
 63    /// <param name="min">The minimum value of the range.</param>
 64    /// <param name="max">The maximum value of the range.</param>
 65    /// <param name="enforceOrder">If true, ensures that Min is less than or equal to Max.</param>
 66    public FixedRange(Fixed64 min, Fixed64 max, bool enforceOrder = true)
 67    {
 9468        if (enforceOrder)
 69        {
 9170            Min = min < max ? min : max;
 9171            Max = min < max ? max : min;
 72        }
 73        else
 74        {
 375            Min = min;
 376            Max = max;
 77        }
 378    }
 79
 80    #endregion
 81
 82    #region Properties
 83
 84    /// <summary>
 85    /// The exact signed length of the range, computed as <see cref="Max"/> minus
 86    /// <see cref="Min"/>.
 87    /// </summary>
 88    /// <exception cref="OverflowException">
 89    /// The signed endpoint difference is outside the representable scalar domain.
 90    /// </exception>
 91    [JsonIgnore]
 92    [MemoryPackIgnore]
 93    public Fixed64 Length
 94    {
 95        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 96        get
 97        {
 498            if (!Fixed64.TrySubtract(Max, Min, out Fixed64 length))
 199                throw new OverflowException("The range length is outside the representable Fixed64 domain.");
 100
 3101            return length;
 102        }
 103    }
 104
 105    /// <summary>
 106    /// The nearest-even Q32.32 midpoint of the range.
 107    /// </summary>
 108    [JsonIgnore]
 109    [MemoryPackIgnore]
 110    public Fixed64 MidPoint
 111    {
 112        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3113        get => FixedMath.Midpoint(Min, Max);
 114    }
 115
 116    #endregion
 117
 118    #region Methods (Instance)
 119
 120    /// <summary>
 121    /// Sets the minimum and maximum values for the range.
 122    /// </summary>
 123    /// <param name="min">The new minimum value.</param>
 124    /// <param name="max">The new maximum value.</param>
 125    public void SetMinMax(Fixed64 min, Fixed64 max)
 126    {
 1127        Min = min;
 1128        Max = max;
 1129    }
 130
 131    /// <summary>
 132    /// Adds a value to both the minimum and maximum of the range.
 133    /// </summary>
 134    /// <param name="val">The value to add.</param>
 135    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 136    public void AddInPlace(Fixed64 val)
 137    {
 1138        Min += val;
 1139        Max += val;
 1140    }
 141
 142    /// <summary>
 143    /// Determines whether the specified value is within the range, with an option to include or exclude the upper bound
 144    /// </summary>
 145    /// <param name="x">The value to check.</param>
 146    /// <param name="includeMax">If true, the upper bound (Max) is included in the range check; otherwise, the upper bou
 147    /// <returns>True if the value is within the range; otherwise, false.</returns>
 148    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 149    public bool InRange(Fixed64 x, bool includeMax = false)
 150    {
 9151        return includeMax ? x >= Min && x <= Max : x >= Min && x < Max;
 152    }
 153
 154    /// <summary>
 155    /// Checks whether this range overlaps with the specified range, ensuring no adjacent edges are considered overlaps.
 156    /// </summary>
 157    /// <param name="other">The range to compare.</param>
 158    /// <returns>True if the ranges overlap; otherwise, false.</returns>
 159    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 160    public bool Overlaps(FixedRange other)
 161    {
 9162        return Min < other.Max && Max > other.Min;
 163    }
 164
 165    #endregion
 166
 167    #region Range Operations
 168
 169    /// <summary>
 170    /// Determines the direction from one range to another.
 171    /// If they don't overlap, returns -1 or 1 depending on the relative position.
 172    /// </summary>
 173    /// <param name="range1">The first range.</param>
 174    /// <param name="range2">The second range.</param>
 175    /// <param name="sign">The direction between ranges (-1 or 1).</param>
 176    /// <returns>True if the ranges don't overlap, false if they do.</returns>
 177    public static bool GetDirection(FixedRange range1, FixedRange range2, out Fixed64? sign)
 178    {
 3179        sign = null;
 3180        if (!range1.Overlaps(range2))
 181        {
 3182            if (range1.Max < range2.Min) sign = -Fixed64.One;
 1183            else sign = Fixed64.One;
 2184            return true;
 185        }
 1186        return false;
 187    }
 188
 189    /// <summary>
 190    /// Calculates the overlap depth between two ranges.
 191    /// Assumes the ranges are sorted (min and max are correctly assigned).
 192    /// </summary>
 193    /// <param name="rangeA">The first range.</param>
 194    /// <param name="rangeB">The second range.</param>
 195    /// <returns>The depth of the overlap between the ranges.</returns>
 196    public static Fixed64 ComputeOverlapDepth(FixedRange rangeA, FixedRange rangeB)
 197    {
 198        // Check if one range is completely within the other
 6199        bool isRangeAInsideB = rangeA.Min >= rangeB.Min && rangeA.Max <= rangeB.Max;
 6200        bool isRangeBInsideA = rangeB.Min >= rangeA.Min && rangeB.Max <= rangeA.Max;
 6201        if (isRangeAInsideB)
 1202            return rangeA.Max - rangeB.Min; // The size of rangeA
 5203        else if (isRangeBInsideA)
 1204            return rangeB.Max - rangeA.Min; // The size of rangeB
 205
 206        // Calculate overlap between the two ranges
 4207        Fixed64 overlapEnd = FixedMath.Min(rangeA.Max, rangeB.Max);
 4208        Fixed64 overlapStart = FixedMath.Max(rangeA.Min, rangeB.Min);
 4209        Fixed64 overlap = overlapEnd - overlapStart;
 210
 4211        return overlap > Fixed64.Zero ? overlap : Fixed64.Zero;
 212    }
 213
 214    /// <summary>
 215    /// Checks for overlap between two ranges and calculates the vector of overlap depth.
 216    /// </summary>
 217    /// <param name="origin">The origin vector.</param>
 218    /// <param name="range1">The first range.</param>
 219    /// <param name="range2">The second range.</param>
 220    /// <param name="limit">The overlap limit to check.</param>
 221    /// <param name="sign">The direction sign to consider.</param>
 222    /// <param name="output">The overlap vector and depth, if any.</param>
 223    /// <returns>True if overlap occurs and is below the limit, otherwise false.</returns>
 224    public static bool CheckOverlap(Vector3d origin, FixedRange range1, FixedRange range2, Fixed64 limit, Fixed64 sign, 
 225    {
 2226        output = null;
 2227        Fixed64 overlap = ComputeOverlapDepth(range1, range2);
 228
 229        // If the overlap is smaller than the current minimum, update the minimum
 2230        if (overlap < limit)
 231        {
 1232            output = (origin * overlap * sign, overlap);
 1233            return true;
 234        }
 1235        return false;
 236    }
 237
 238    #endregion
 239
 240    #region Operators
 241
 242    /// <summary>
 243    /// Adds two FixedRange instances by summing their minimum and maximum values.
 244    /// </summary>
 245    /// <param name="left">The first FixedRange to add.</param>
 246    /// <param name="right">The second FixedRange to add.</param>
 247    /// <returns>A new FixedRange whose Min is the sum of the Min values and whose Max is the sum of the Max values of t
 248    /// specified ranges.</returns>
 249    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 250    public static FixedRange operator +(FixedRange left, FixedRange right)
 251    {
 1252        return new FixedRange(left.Min + right.Min, left.Max + right.Max);
 253    }
 254
 255    /// <summary>
 256    /// Subtracts the minimum and maximum values of one FixedRange from another and returns the resulting FixedRange.
 257    /// </summary>
 258    /// <param name="left">The FixedRange instance to subtract from.</param>
 259    /// <param name="right">The FixedRange instance whose values are subtracted.</param>
 260    /// <returns>A FixedRange whose Min and Max values are the result of subtracting the corresponding values of right f
 261    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 262    public static FixedRange operator -(FixedRange left, FixedRange right)
 263    {
 1264        return new FixedRange(left.Min - right.Min, left.Max - right.Max);
 265    }
 266
 267    /// <summary>
 268    /// Determines whether two FixedRange instances are equal.
 269    /// </summary>
 270    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1271    public static bool operator ==(FixedRange left, FixedRange right) => left.Equals(right);
 272
 273    /// <summary>
 274    /// Determines whether two FixedRange instances are not equal.
 275    /// </summary>
 276    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1277    public static bool operator !=(FixedRange left, FixedRange right) => !left.Equals(right);
 278
 279    #endregion
 280
 281    #region Conversion
 282
 283    /// <summary>
 284    /// Returns a string that represents the FixedRange instance, formatted as "Min - Max".
 285    /// </summary>
 286    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1287    public override string ToString() => ToString(null, CultureInfo.InvariantCulture);
 288
 289    /// <summary>
 290    /// Returns a string that represents the FixedRange instance, formatted as "Min - Max".
 291    /// </summary>
 292    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 293    public string ToString(string? format, IFormatProvider? formatProvider)
 294    {
 2295        FixedRange value = this;
 2296        return FixedDiagnosticsFormatter.ToString((Span<char> destination, out int charsWritten) =>
 2297            value.TryFormat(destination, out charsWritten, format.AsSpan(), formatProvider));
 298    }
 299
 300    /// <summary>
 301    /// Formats this range into the provided destination buffer.
 302    /// </summary>
 303    public bool TryFormat(
 304        Span<char> destination,
 305        out int charsWritten,
 306        ReadOnlySpan<char> format,
 307        IFormatProvider? provider)
 308    {
 15309        int written = 0;
 15310        if (!FixedDiagnosticsFormatter.Append(Min, destination, ref written, format, provider) ||
 15311            !FixedDiagnosticsFormatter.Append(" - ", destination, ref written) ||
 15312            !FixedDiagnosticsFormatter.Append(Max, destination, ref written, format, provider))
 313        {
 12314            charsWritten = 0;
 12315            return false;
 316        }
 317
 3318        charsWritten = written;
 3319        return true;
 320    }
 321
 322    #endregion
 323
 324    #region Equality and HashCode Overrides
 325
 326    /// <inheritdoc/>
 327    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 328    public override bool Equals(object? obj)
 329    {
 2330        return obj is FixedRange other && Equals(other);
 331    }
 332
 333    /// <inheritdoc/>
 334    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 335    public bool Equals(FixedRange other)
 336    {
 8337        return other.Min == Min && other.Max == Max;
 338    }
 339
 340    /// <summary>
 341    /// Computes the hash code for the FixedRange instance.
 342    /// </summary>
 343    /// <returns>The hash code of the range.</returns>
 344    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 345    public override int GetHashCode()
 346    {
 2347        return Min.GetHashCode() ^ Max.GetHashCode();
 348    }
 349
 350    #endregion
 351}