< 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: 59
Uncovered lines: 0
Coverable lines: 59
Total lines: 341
Line coverage: 100%
Branch coverage
97%
Covered branches: 39
Total branches: 40
Branch coverage: 97.5%
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%11100%
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(...)83.33%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 MemoryPack;
 9using System;
 10using System.Globalization;
 11using System.Runtime.CompilerServices;
 12using System.Text.Json.Serialization;
 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    {
 5968        if (enforceOrder)
 69        {
 5870            Min = min < max ? min : max;
 5871            Max = min < max ? max : min;
 72        }
 73        else
 74        {
 175            Min = min;
 176            Max = max;
 77        }
 178    }
 79
 80    #endregion
 81
 82    #region Properties
 83
 84    /// <summary>
 85    /// The length of the range, computed as Max - Min.
 86    /// </summary>
 87    [JsonIgnore]
 88    [MemoryPackIgnore]
 89    public Fixed64 Length
 90    {
 91        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 192        get => Max - Min;
 93    }
 94
 95    /// <summary>
 96    /// The midpoint of the range.
 97    /// </summary>
 98    [JsonIgnore]
 99    [MemoryPackIgnore]
 100    public Fixed64 MidPoint
 101    {
 102        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1103        get => (Min + Max) * Fixed64.Half;
 104    }
 105
 106    #endregion
 107
 108    #region Methods (Instance)
 109
 110    /// <summary>
 111    /// Sets the minimum and maximum values for the range.
 112    /// </summary>
 113    /// <param name="min">The new minimum value.</param>
 114    /// <param name="max">The new maximum value.</param>
 115    public void SetMinMax(Fixed64 min, Fixed64 max)
 116    {
 1117        Min = min;
 1118        Max = max;
 1119    }
 120
 121    /// <summary>
 122    /// Adds a value to both the minimum and maximum of the range.
 123    /// </summary>
 124    /// <param name="val">The value to add.</param>
 125    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 126    public void AddInPlace(Fixed64 val)
 127    {
 1128        Min += val;
 1129        Max += val;
 1130    }
 131
 132    /// <summary>
 133    /// Determines whether the specified value is within the range, with an option to include or exclude the upper bound
 134    /// </summary>
 135    /// <param name="x">The value to check.</param>
 136    /// <param name="includeMax">If true, the upper bound (Max) is included in the range check; otherwise, the upper bou
 137    /// <returns>True if the value is within the range; otherwise, false.</returns>
 138    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 139    public bool InRange(Fixed64 x, bool includeMax = false)
 140    {
 9141        return includeMax ? x >= Min && x <= Max : x >= Min && x < Max;
 142    }
 143
 144    /// <summary>
 145    /// Checks whether this range overlaps with the specified range, ensuring no adjacent edges are considered overlaps.
 146    /// </summary>
 147    /// <param name="other">The range to compare.</param>
 148    /// <returns>True if the ranges overlap; otherwise, false.</returns>
 149    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 150    public bool Overlaps(FixedRange other)
 151    {
 9152        return Min < other.Max && Max > other.Min;
 153    }
 154
 155    #endregion
 156
 157    #region Range Operations
 158
 159    /// <summary>
 160    /// Determines the direction from one range to another.
 161    /// If they don't overlap, returns -1 or 1 depending on the relative position.
 162    /// </summary>
 163    /// <param name="range1">The first range.</param>
 164    /// <param name="range2">The second range.</param>
 165    /// <param name="sign">The direction between ranges (-1 or 1).</param>
 166    /// <returns>True if the ranges don't overlap, false if they do.</returns>
 167    public static bool GetDirection(FixedRange range1, FixedRange range2, out Fixed64? sign)
 168    {
 3169        sign = null;
 3170        if (!range1.Overlaps(range2))
 171        {
 3172            if (range1.Max < range2.Min) sign = -Fixed64.One;
 1173            else sign = Fixed64.One;
 2174            return true;
 175        }
 1176        return false;
 177    }
 178
 179    /// <summary>
 180    /// Calculates the overlap depth between two ranges.
 181    /// Assumes the ranges are sorted (min and max are correctly assigned).
 182    /// </summary>
 183    /// <param name="rangeA">The first range.</param>
 184    /// <param name="rangeB">The second range.</param>
 185    /// <returns>The depth of the overlap between the ranges.</returns>
 186    public static Fixed64 ComputeOverlapDepth(FixedRange rangeA, FixedRange rangeB)
 187    {
 188        // Check if one range is completely within the other
 6189        bool isRangeAInsideB = rangeA.Min >= rangeB.Min && rangeA.Max <= rangeB.Max;
 6190        bool isRangeBInsideA = rangeB.Min >= rangeA.Min && rangeB.Max <= rangeA.Max;
 6191        if (isRangeAInsideB)
 1192            return rangeA.Max - rangeB.Min; // The size of rangeA
 5193        else if (isRangeBInsideA)
 1194            return rangeB.Max - rangeA.Min; // The size of rangeB
 195
 196        // Calculate overlap between the two ranges
 4197        Fixed64 overlapEnd = FixedMath.Min(rangeA.Max, rangeB.Max);
 4198        Fixed64 overlapStart = FixedMath.Max(rangeA.Min, rangeB.Min);
 4199        Fixed64 overlap = overlapEnd - overlapStart;
 200
 4201        return overlap > Fixed64.Zero ? overlap : Fixed64.Zero;
 202    }
 203
 204    /// <summary>
 205    /// Checks for overlap between two ranges and calculates the vector of overlap depth.
 206    /// </summary>
 207    /// <param name="origin">The origin vector.</param>
 208    /// <param name="range1">The first range.</param>
 209    /// <param name="range2">The second range.</param>
 210    /// <param name="limit">The overlap limit to check.</param>
 211    /// <param name="sign">The direction sign to consider.</param>
 212    /// <param name="output">The overlap vector and depth, if any.</param>
 213    /// <returns>True if overlap occurs and is below the limit, otherwise false.</returns>
 214    public static bool CheckOverlap(Vector3d origin, FixedRange range1, FixedRange range2, Fixed64 limit, Fixed64 sign, 
 215    {
 2216        output = null;
 2217        Fixed64 overlap = ComputeOverlapDepth(range1, range2);
 218
 219        // If the overlap is smaller than the current minimum, update the minimum
 2220        if (overlap < limit)
 221        {
 1222            output = (origin * overlap * sign, overlap);
 1223            return true;
 224        }
 1225        return false;
 226    }
 227
 228    #endregion
 229
 230    #region Operators
 231
 232    /// <summary>
 233    /// Adds two FixedRange instances by summing their minimum and maximum values.
 234    /// </summary>
 235    /// <param name="left">The first FixedRange to add.</param>
 236    /// <param name="right">The second FixedRange to add.</param>
 237    /// <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
 238    /// specified ranges.</returns>
 239    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 240    public static FixedRange operator +(FixedRange left, FixedRange right)
 241    {
 1242        return new FixedRange(left.Min + right.Min, left.Max + right.Max);
 243    }
 244
 245    /// <summary>
 246    /// Subtracts the minimum and maximum values of one FixedRange from another and returns the resulting FixedRange.
 247    /// </summary>
 248    /// <param name="left">The FixedRange instance to subtract from.</param>
 249    /// <param name="right">The FixedRange instance whose values are subtracted.</param>
 250    /// <returns>A FixedRange whose Min and Max values are the result of subtracting the corresponding values of right f
 251    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 252    public static FixedRange operator -(FixedRange left, FixedRange right)
 253    {
 1254        return new FixedRange(left.Min - right.Min, left.Max - right.Max);
 255    }
 256
 257    /// <summary>
 258    /// Determines whether two FixedRange instances are equal.
 259    /// </summary>
 260    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1261    public static bool operator ==(FixedRange left, FixedRange right) => left.Equals(right);
 262
 263    /// <summary>
 264    /// Determines whether two FixedRange instances are not equal.
 265    /// </summary>
 266    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1267    public static bool operator !=(FixedRange left, FixedRange right) => !left.Equals(right);
 268
 269    #endregion
 270
 271    #region Conversion
 272
 273    /// <summary>
 274    /// Returns a string that represents the FixedRange instance, formatted as "Min - Max".
 275    /// </summary>
 276    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1277    public override string ToString() => ToString(null, CultureInfo.InvariantCulture);
 278
 279    /// <summary>
 280    /// Returns a string that represents the FixedRange instance, formatted as "Min - Max".
 281    /// </summary>
 282    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 283    public string ToString(string? format, IFormatProvider? formatProvider)
 284    {
 2285        FixedRange value = this;
 2286        return FixedDiagnosticsFormatter.ToString((Span<char> destination, out int charsWritten) =>
 2287            value.TryFormat(destination, out charsWritten, format.AsSpan(), formatProvider));
 288    }
 289
 290    /// <summary>
 291    /// Formats this range into the provided destination buffer.
 292    /// </summary>
 293    public bool TryFormat(
 294        Span<char> destination,
 295        out int charsWritten,
 296        ReadOnlySpan<char> format,
 297        IFormatProvider? provider)
 298    {
 4299        int written = 0;
 4300        if (!FixedDiagnosticsFormatter.Append(Min, destination, ref written, format, provider) ||
 4301            !FixedDiagnosticsFormatter.Append(" - ", destination, ref written) ||
 4302            !FixedDiagnosticsFormatter.Append(Max, destination, ref written, format, provider))
 303        {
 1304            charsWritten = 0;
 1305            return false;
 306        }
 307
 3308        charsWritten = written;
 3309        return true;
 310    }
 311
 312    #endregion
 313
 314    #region Equality and HashCode Overrides
 315
 316    /// <inheritdoc/>
 317    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 318    public override bool Equals(object? obj)
 319    {
 2320        return obj is FixedRange other && Equals(other);
 321    }
 322
 323    /// <inheritdoc/>
 324    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 325    public bool Equals(FixedRange other)
 326    {
 8327        return other.Min == Min && other.Max == Max;
 328    }
 329
 330    /// <summary>
 331    /// Computes the hash code for the FixedRange instance.
 332    /// </summary>
 333    /// <returns>The hash code of the range.</returns>
 334    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 335    public override int GetHashCode()
 336    {
 2337        return Min.GetHashCode() ^ Max.GetHashCode();
 338    }
 339
 340    #endregion
 341}