< Summary

Information
Class: FixedMathSharp.Geometry.WideRayIntersection
Assembly: FixedMathSharp
File(s): /home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/WideRayIntersection.cs
Line coverage
100%
Covered lines: 431
Uncovered lines: 0
Coverable lines: 431
Total lines: 750
Line coverage: 100%
Branch coverage
100%
Covered branches: 110
Total branches: 110
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%
Intersects(...)100%22100%
TryGetInterval(...)100%11100%
TryGetInterval(...)100%22100%
Intersects(...)100%66100%
IntersectsWide(...)100%11100%
TryGetIntervalWide(...)100%11100%
Intersects(...)100%22100%
TryGetInterval(...)100%11100%
TryGetInterval(...)100%22100%
Intersects(...)100%66100%
IntersectsWide(...)100%11100%
TryGetIntervalWide(...)100%11100%
TrySolveInterval(...)100%2222100%
FindFloorUpperRoot(...)100%1010100%
RoundUpperRoot(...)100%1212100%
IsAtOrBeforeUpperRoot(...)100%11100%
EvaluateDerivative(...)100%11100%
Solve(...)100%1010100%
SolveEntry(...)100%2222100%
FindFloorRoot(...)100%1010100%
GetFloorRatioRaw(...)100%44100%
GetRadiusSquared(...)100%11100%
EvaluatePolynomial(...)100%11100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Wide/WideRayIntersection.cs

#LineLine coverage
 1//=======================================================================
 2// WideRayIntersection.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.Geometry;
 12
 13/// <summary>
 14/// Owns exact full-domain ray/circle and ray/sphere quadratic reduction.
 15/// </summary>
 16internal static class WideRayIntersection
 17{
 118    private static readonly Signed192 RawScale = Signed192.Signed(FixedMath.ONE_L);
 119    private static readonly Signed192 DoubleRawScale = Signed192.Signed(FixedMath.ONE_L * 2L);
 20
 21    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 22    public static Fixed64? Intersects(
 23        Vector2d position,
 24        Vector2d direction,
 25        FixedBoundCircle circle,
 26        Fixed64 maxParameter)
 27    {
 2728        if (maxParameter < Fixed64.Zero)
 129            return null;
 30
 2631        return IntersectsWide(
 2632            position,
 2633            direction,
 2634            circle.Center,
 2635            Signed192.Signed(circle.Radius.m_rawValue),
 2636            maxParameter);
 37    }
 38
 39    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 40    public static bool TryGetInterval(
 41        Vector2d position,
 42        Vector2d direction,
 43        FixedBoundCircle circle,
 44        Fixed64 maxParameter,
 45        out Fixed64 entry,
 46        out Fixed64 exit) =>
 2147        TryGetIntervalWide(
 2148            position,
 2149            direction,
 2150            circle.Center,
 2151            Signed192.Signed(circle.Radius.m_rawValue),
 2152            maxParameter,
 2153            out entry,
 2154            out exit);
 55
 56    public static bool TryGetInterval(
 57        Vector2d position,
 58        Vector2d direction,
 59        FixedBoundCircle circle,
 60        Fixed64 radiusExpansion,
 61        Fixed64 maxParameter,
 62        out Fixed64 entry,
 63        out Fixed64 exit)
 64    {
 265        if (radiusExpansion < Fixed64.Zero)
 166            throw new ArgumentOutOfRangeException(nameof(radiusExpansion), "Radius expansion must be non-negative.");
 67
 168        Signed192 expandedRadius = WideArithmetic.AddSigned192(
 169            Signed192.Signed(circle.Radius.m_rawValue),
 170            Signed192.Signed(radiusExpansion.m_rawValue));
 171        return TryGetIntervalWide(
 172            position,
 173            direction,
 174            circle.Center,
 175            expandedRadius,
 176            maxParameter,
 177            out entry,
 178            out exit);
 79    }
 80
 81    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 82    public static Fixed64? Intersects(
 83        Vector2d position,
 84        Vector2d direction,
 85        FixedBoundCircle circle,
 86        Fixed64 radiusExpansion,
 87        Fixed64 maxParameter)
 88    {
 689        if (radiusExpansion < Fixed64.Zero)
 190            throw new ArgumentOutOfRangeException(nameof(radiusExpansion), "Radius expansion must be non-negative.");
 591        if (radiusExpansion == Fixed64.Zero)
 192            return Intersects(position, direction, circle, maxParameter);
 493        if (maxParameter < Fixed64.Zero)
 194            return null;
 95
 396        Signed192 expandedRadius = WideArithmetic.AddSigned192(
 397            Signed192.Signed(circle.Radius.m_rawValue),
 398            Signed192.Signed(radiusExpansion.m_rawValue));
 399        return IntersectsWide(
 3100            position,
 3101            direction,
 3102            circle.Center,
 3103            expandedRadius,
 3104            maxParameter);
 105    }
 106
 107    private static Fixed64? IntersectsWide(
 108        Vector2d position,
 109        Vector2d direction,
 110        Vector2d center,
 111        Signed192 radius,
 112        Fixed64 maxParameter)
 113    {
 29114        Signed192 directionLengthSquared = WideGeometry.GetDifferenceDotProduct2D(
 29115            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero,
 29116            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero);
 29117        Signed192 projection = WideGeometry.GetDifferenceDotProduct2D(
 29118            position.X, center.X, position.Y, center.Y,
 29119            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero);
 29120        Signed192 distanceSquared = WideGeometry.GetDifferenceDotProduct2D(
 29121            position.X, center.X, position.Y, center.Y,
 29122            position.X, center.X, position.Y, center.Y);
 29123        return Solve(
 29124            directionLengthSquared,
 29125            projection,
 29126            WideArithmetic.SubtractSigned192(distanceSquared, GetRadiusSquared(radius)),
 29127            maxParameter);
 128    }
 129
 130    private static bool TryGetIntervalWide(
 131        Vector2d position,
 132        Vector2d direction,
 133        Vector2d center,
 134        Signed192 radius,
 135        Fixed64 maxParameter,
 136        out Fixed64 entry,
 137        out Fixed64 exit)
 138    {
 22139        Signed192 directionLengthSquared = WideGeometry.GetDifferenceDotProduct2D(
 22140            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero,
 22141            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero);
 22142        Signed192 projection = WideGeometry.GetDifferenceDotProduct2D(
 22143            position.X, center.X, position.Y, center.Y,
 22144            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero);
 22145        Signed192 distanceSquared = WideGeometry.GetDifferenceDotProduct2D(
 22146            position.X, center.X, position.Y, center.Y,
 22147            position.X, center.X, position.Y, center.Y);
 22148        return TrySolveInterval(
 22149            directionLengthSquared,
 22150            projection,
 22151            WideArithmetic.SubtractSigned192(distanceSquared, GetRadiusSquared(radius)),
 22152            maxParameter,
 22153            out entry,
 22154            out exit);
 155    }
 156
 157    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 158    public static Fixed64? Intersects(
 159        Vector3d position,
 160        Vector3d direction,
 161        FixedBoundSphere sphere,
 162        Fixed64 maxParameter)
 163    {
 25164        if (maxParameter < Fixed64.Zero)
 1165            return null;
 166
 24167        return IntersectsWide(
 24168            position,
 24169            direction,
 24170            sphere.Center,
 24171            Signed192.Signed(sphere.Radius.m_rawValue),
 24172            maxParameter);
 173    }
 174
 175    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 176    public static bool TryGetInterval(
 177        Vector3d position,
 178        Vector3d direction,
 179        FixedBoundSphere sphere,
 180        Fixed64 maxParameter,
 181        out Fixed64 entry,
 182        out Fixed64 exit) =>
 16183        TryGetIntervalWide(
 16184            position,
 16185            direction,
 16186            sphere.Center,
 16187            Signed192.Signed(sphere.Radius.m_rawValue),
 16188            maxParameter,
 16189            out entry,
 16190            out exit);
 191
 192    public static bool TryGetInterval(
 193        Vector3d position,
 194        Vector3d direction,
 195        FixedBoundSphere sphere,
 196        Fixed64 radiusExpansion,
 197        Fixed64 maxParameter,
 198        out Fixed64 entry,
 199        out Fixed64 exit)
 200    {
 2201        if (radiusExpansion < Fixed64.Zero)
 1202            throw new ArgumentOutOfRangeException(nameof(radiusExpansion), "Radius expansion must be non-negative.");
 203
 1204        Signed192 expandedRadius = WideArithmetic.AddSigned192(
 1205            Signed192.Signed(sphere.Radius.m_rawValue),
 1206            Signed192.Signed(radiusExpansion.m_rawValue));
 1207        return TryGetIntervalWide(
 1208            position,
 1209            direction,
 1210            sphere.Center,
 1211            expandedRadius,
 1212            maxParameter,
 1213            out entry,
 1214            out exit);
 215    }
 216
 217    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 218    public static Fixed64? Intersects(
 219        Vector3d position,
 220        Vector3d direction,
 221        FixedBoundSphere sphere,
 222        Fixed64 radiusExpansion,
 223        Fixed64 maxParameter)
 224    {
 6225        if (radiusExpansion < Fixed64.Zero)
 1226            throw new ArgumentOutOfRangeException(nameof(radiusExpansion), "Radius expansion must be non-negative.");
 5227        if (radiusExpansion == Fixed64.Zero)
 1228            return Intersects(position, direction, sphere, maxParameter);
 4229        if (maxParameter < Fixed64.Zero)
 1230            return null;
 231
 3232        Signed192 expandedRadius = WideArithmetic.AddSigned192(
 3233            Signed192.Signed(sphere.Radius.m_rawValue),
 3234            Signed192.Signed(radiusExpansion.m_rawValue));
 3235        return IntersectsWide(
 3236            position,
 3237            direction,
 3238            sphere.Center,
 3239            expandedRadius,
 3240            maxParameter);
 241    }
 242
 243    private static Fixed64? IntersectsWide(
 244        Vector3d position,
 245        Vector3d direction,
 246        Vector3d center,
 247        Signed192 radius,
 248        Fixed64 maxParameter)
 249    {
 27250        Signed192 directionLengthSquared = WideGeometry.GetDifferenceDotProduct3D(
 27251            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero, direction.Z, Fixed64.Zero,
 27252            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero, direction.Z, Fixed64.Zero);
 27253        Signed192 projection = WideGeometry.GetDifferenceDotProduct3D(
 27254            position.X, center.X, position.Y, center.Y, position.Z, center.Z,
 27255            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero, direction.Z, Fixed64.Zero);
 27256        Signed192 distanceSquared = WideGeometry.GetDifferenceDotProduct3D(
 27257            position.X, center.X, position.Y, center.Y, position.Z, center.Z,
 27258            position.X, center.X, position.Y, center.Y, position.Z, center.Z);
 27259        return Solve(
 27260            directionLengthSquared,
 27261            projection,
 27262            WideArithmetic.SubtractSigned192(distanceSquared, GetRadiusSquared(radius)),
 27263            maxParameter);
 264    }
 265
 266    private static bool TryGetIntervalWide(
 267        Vector3d position,
 268        Vector3d direction,
 269        Vector3d center,
 270        Signed192 radius,
 271        Fixed64 maxParameter,
 272        out Fixed64 entry,
 273        out Fixed64 exit)
 274    {
 17275        Signed192 directionLengthSquared = WideGeometry.GetDifferenceDotProduct3D(
 17276            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero, direction.Z, Fixed64.Zero,
 17277            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero, direction.Z, Fixed64.Zero);
 17278        Signed192 projection = WideGeometry.GetDifferenceDotProduct3D(
 17279            position.X, center.X, position.Y, center.Y, position.Z, center.Z,
 17280            direction.X, Fixed64.Zero, direction.Y, Fixed64.Zero, direction.Z, Fixed64.Zero);
 17281        Signed192 distanceSquared = WideGeometry.GetDifferenceDotProduct3D(
 17282            position.X, center.X, position.Y, center.Y, position.Z, center.Z,
 17283            position.X, center.X, position.Y, center.Y, position.Z, center.Z);
 17284        return TrySolveInterval(
 17285            directionLengthSquared,
 17286            projection,
 17287            WideArithmetic.SubtractSigned192(distanceSquared, GetRadiusSquared(radius)),
 17288            maxParameter,
 17289            out entry,
 17290            out exit);
 291    }
 292
 293    internal static bool TrySolveInterval(
 294        Signed192 directionLengthSquared,
 295        Signed192 projection,
 296        Signed192 constant,
 297        Fixed64 maxParameter,
 298        out Fixed64 entry,
 299        out Fixed64 exit)
 300    {
 955301        entry = default;
 955302        exit = default;
 955303        if (maxParameter < Fixed64.Zero)
 2304            return false;
 305
 953306        if (directionLengthSquared.IsZero)
 307        {
 10308            if (constant.Sign > 0)
 5309                return false;
 310
 5311            exit = maxParameter;
 5312            return true;
 313        }
 314
 943315        Signed320 discriminant = WideArithmetic.MultiplySubtract(
 943316            projection,
 943317            projection,
 943318            directionLengthSquared,
 943319            constant);
 943320        Signed192 squareRoot = default;
 943321        bool hasSquareRoot = false;
 943322        if (constant.Sign > 0)
 323        {
 923324            if (projection.Sign >= 0 || maxParameter == Fixed64.Zero || discriminant.Sign < 0)
 284325                return false;
 326
 639327            squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 639328            hasSquareRoot = true;
 639329            Fixed64? first = SolveEntry(
 639330                directionLengthSquared,
 639331                projection,
 639332                constant,
 639333                squareRoot,
 639334                maxParameter);
 639335            if (!first.HasValue)
 11336                return false;
 337
 628338            entry = first.Value;
 339        }
 340
 648341        long maxRaw = maxParameter.m_rawValue;
 648342        Signed192 maxNumerator = Signed192.Signed(maxRaw);
 648343        Signed320 valueAtMax = EvaluatePolynomial(
 648344            directionLengthSquared,
 648345            projection,
 648346            constant,
 648347            maxNumerator,
 648348            RawScale);
 648349        if (valueAtMax.Sign <= 0)
 350        {
 21351            exit = maxParameter;
 21352            return true;
 353        }
 354
 627355        if (discriminant.IsZero)
 356        {
 22357            exit = entry;
 22358            return true;
 359        }
 360
 605361        Signed192 negativeProjection = WideArithmetic.SubtractSigned192(default, projection);
 605362        if (!hasSquareRoot)
 14363            squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 364
 605365        long approximateRaw = GetFloorRatioRaw(
 605366            WideArithmetic.AddSigned192(negativeProjection, squareRoot),
 605367            directionLengthSquared);
 605368        long floorRootRaw = FindFloorUpperRoot(
 605369            directionLengthSquared,
 605370            projection,
 605371            constant,
 605372            approximateRaw,
 605373            maxRaw,
 605374            out bool exactRoot);
 605375        exit = RoundUpperRoot(
 605376            directionLengthSquared,
 605377            projection,
 605378            constant,
 605379            floorRootRaw,
 605380            exactRoot);
 605381        return true;
 382    }
 383
 384    private static long FindFloorUpperRoot(
 385        Signed192 directionLengthSquared,
 386        Signed192 projection,
 387        Signed192 constant,
 388        long approximateRaw,
 389        long maxRaw,
 390        out bool exactRoot)
 391    {
 605392        long lowRaw = Math.Max(0L, Math.Min(approximateRaw, maxRaw - 1L));
 605393        long highRaw = maxRaw;
 605394        long step = 1L;
 395
 687396        while (highRaw - lowRaw > 1L)
 397        {
 687398            long remaining = highRaw - lowRaw;
 687399            long candidateRaw = lowRaw + Math.Min(step, remaining);
 687400            if (!IsAtOrBeforeUpperRoot(
 687401                    directionLengthSquared,
 687402                    projection,
 687403                    constant,
 687404                    Signed192.Signed(candidateRaw),
 687405                    RawScale))
 406            {
 605407                highRaw = candidateRaw;
 605408                break;
 409            }
 410
 82411            lowRaw = candidateRaw;
 412            // Replacing the exact discriminant root by its floor can undershoot
 413            // the upper root by fewer than 2^32 parameter raw units, so this
 414            // doubling cannot overflow before the bracket is found.
 82415            step <<= 1;
 416        }
 417
 687418        while (highRaw - lowRaw > 1L)
 419        {
 82420            long middleRaw = lowRaw + ((highRaw - lowRaw) >> 1);
 82421            if (IsAtOrBeforeUpperRoot(
 82422                    directionLengthSquared,
 82423                    projection,
 82424                    constant,
 82425                    Signed192.Signed(middleRaw),
 82426                    RawScale))
 427            {
 38428                lowRaw = middleRaw;
 429            }
 430            else
 431            {
 44432                highRaw = middleRaw;
 433            }
 434        }
 435
 605436        Signed192 low = Signed192.Signed(lowRaw);
 605437        Signed320 lowValue = EvaluatePolynomial(
 605438            directionLengthSquared,
 605439            projection,
 605440            constant,
 605441            low,
 605442            RawScale);
 605443        Signed320 lowDerivative = EvaluateDerivative(
 605444            directionLengthSquared,
 605445            projection,
 605446            low,
 605447            RawScale);
 605448        exactRoot = lowValue.IsZero && lowDerivative.Sign >= 0;
 605449        return lowRaw;
 450    }
 451
 452    private static Fixed64 RoundUpperRoot(
 453        Signed192 directionLengthSquared,
 454        Signed192 projection,
 455        Signed192 constant,
 456        long floorRootRaw,
 457        bool exactRoot)
 458    {
 605459        if (exactRoot)
 50460            return Fixed64.FromRaw(floorRootRaw);
 461
 555462        Signed192 floorRoot = Signed192.Signed(floorRootRaw);
 555463        Signed192 midpointNumerator = WideArithmetic.AddSigned192(
 555464            WideArithmetic.AddSigned192(floorRoot, floorRoot),
 555465            Signed192.Signed(1L));
 555466        Signed320 midpointValue = EvaluatePolynomial(
 555467            directionLengthSquared,
 555468            projection,
 555469            constant,
 555470            midpointNumerator,
 555471            DoubleRawScale);
 555472        Signed320 midpointDerivative = EvaluateDerivative(
 555473            directionLengthSquared,
 555474            projection,
 555475            midpointNumerator,
 555476            DoubleRawScale);
 477
 555478        if (midpointValue.IsZero && midpointDerivative.Sign >= 0)
 479        {
 8480            return Fixed64.FromRaw((floorRootRaw & 1L) == 0L
 8481                ? floorRootRaw
 8482                : floorRootRaw + 1L);
 483        }
 484
 547485        bool roundUp = midpointDerivative.Sign <= 0 || midpointValue.Sign <= 0;
 547486        return Fixed64.FromRaw(roundUp ? floorRootRaw + 1L : floorRootRaw);
 487    }
 488
 489    private static bool IsAtOrBeforeUpperRoot(
 490        Signed192 directionLengthSquared,
 491        Signed192 projection,
 492        Signed192 constant,
 493        Signed192 timeNumerator,
 494        Signed192 timeDenominator) =>
 495        // Every probed candidate is strictly greater than the analytic upper
 496        // seed, whose floor cannot precede the vertex floor. Candidates are
 497        // therefore on the nondecreasing side of the quadratic.
 769498        EvaluatePolynomial(
 769499            directionLengthSquared,
 769500            projection,
 769501            constant,
 769502            timeNumerator,
 769503            timeDenominator).Sign <= 0;
 504
 505    private static Signed320 EvaluateDerivative(
 506        Signed192 directionLengthSquared,
 507        Signed192 projection,
 508        Signed192 timeNumerator,
 509        Signed192 timeDenominator) =>
 1160510        WideArithmetic.AddSigned320(
 1160511            WideArithmetic.MultiplySigned192(directionLengthSquared, timeNumerator),
 1160512            WideArithmetic.MultiplySigned192(projection, timeDenominator));
 513
 514    private static Fixed64? Solve(
 515        Signed192 directionLengthSquared,
 516        Signed192 projection,
 517        Signed192 constant,
 518        Fixed64 maxParameter)
 519    {
 56520        if (constant.Sign <= 0)
 7521            return Fixed64.Zero;
 49522        if (directionLengthSquared.IsZero || projection.Sign >= 0 || maxParameter == Fixed64.Zero)
 6523            return null;
 524
 43525        Signed320 discriminant = WideArithmetic.MultiplySubtract(
 43526            projection,
 43527            projection,
 43528            directionLengthSquared,
 43529            constant);
 43530        if (discriminant.Sign < 0)
 4531            return null;
 532
 39533        Signed192 squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 39534        return SolveEntry(
 39535            directionLengthSquared,
 39536            projection,
 39537            constant,
 39538            squareRoot,
 39539            maxParameter);
 540    }
 541
 542    private static Fixed64? SolveEntry(
 543        Signed192 directionLengthSquared,
 544        Signed192 projection,
 545        Signed192 constant,
 546        Signed192 squareRoot,
 547        Fixed64 maxParameter)
 548    {
 678549        Signed192 negativeProjection = WideArithmetic.SubtractSigned192(default, projection);
 678550        long approximateRaw = GetFloorRatioRaw(
 678551            constant,
 678552            WideArithmetic.AddSigned192(negativeProjection, squareRoot));
 678553        long maxRaw = maxParameter.m_rawValue;
 678554        Signed320 derivativeAtMax = WideArithmetic.SubtractSigned320(
 678555            WideArithmetic.MultiplySigned192(
 678556                directionLengthSquared,
 678557                Signed192.Signed(maxRaw)),
 678558            WideArithmetic.MultiplySigned192(negativeProjection, RawScale));
 678559        if (derivativeAtMax.Sign < 0
 678560            && EvaluatePolynomial(
 678561                directionLengthSquared,
 678562                projection,
 678563                constant,
 678564                Signed192.Signed(maxRaw),
 678565                RawScale).Sign > 0)
 566        {
 17567            return null;
 568        }
 569
 661570        long closestRaw = GetFloorRatioRaw(negativeProjection, directionLengthSquared);
 661571        long highRaw = closestRaw < maxRaw ? closestRaw : maxRaw;
 661572        Signed320 highValue = EvaluatePolynomial(
 661573            directionLengthSquared,
 661574            projection,
 661575            constant,
 661576            Signed192.Signed(highRaw),
 661577            RawScale);
 578
 579        long floorRootRaw;
 580        bool exactRoot;
 661581        if (highValue.Sign > 0)
 582        {
 9583            floorRootRaw = highRaw;
 9584            exactRoot = false;
 585        }
 586        else
 587        {
 652588            floorRootRaw = FindFloorRoot(
 652589                directionLengthSquared,
 652590                projection,
 652591                constant,
 652592                highRaw,
 652593                approximateRaw,
 652594                out exactRoot);
 595        }
 596
 661597        if (exactRoot || floorRootRaw == maxRaw)
 85598            return Fixed64.FromRaw(floorRootRaw);
 599
 576600        Signed192 floorRoot = Signed192.Signed(floorRootRaw);
 576601        Signed192 midpointNumerator = WideArithmetic.AddSigned192(
 576602            WideArithmetic.AddSigned192(floorRoot, floorRoot),
 576603            Signed192.Signed(1L));
 576604        Signed320 midpointValue = EvaluatePolynomial(
 576605            directionLengthSquared,
 576606            projection,
 576607            constant,
 576608            midpointNumerator,
 576609            DoubleRawScale);
 576610        Signed320 midpointDerivative = WideArithmetic.AddSigned320(
 576611            WideArithmetic.MultiplySigned192(directionLengthSquared, midpointNumerator),
 576612            WideArithmetic.MultiplySigned192(projection, DoubleRawScale));
 613
 576614        if (midpointValue.IsZero && midpointDerivative.Sign <= 0)
 615        {
 12616            return Fixed64.FromRaw((floorRootRaw & 1L) == 0L
 12617                ? floorRootRaw
 12618                : floorRootRaw + 1L);
 619        }
 620
 564621        bool roundUp = midpointDerivative.Sign < 0 && midpointValue.Sign > 0;
 564622        return Fixed64.FromRaw(roundUp ? floorRootRaw + 1L : floorRootRaw);
 623    }
 624
 625    private static long FindFloorRoot(
 626        Signed192 directionLengthSquared,
 627        Signed192 projection,
 628        Signed192 constant,
 629        long highRaw,
 630        long approximateRaw,
 631        out bool exactRoot)
 632    {
 652633        long candidateRaw = Math.Min(approximateRaw, highRaw);
 634
 652635        Signed320 candidateValue = EvaluatePolynomial(
 652636            directionLengthSquared,
 652637            projection,
 652638            constant,
 652639            Signed192.Signed(candidateRaw),
 652640            RawScale);
 652641        if (candidateValue.IsZero)
 642        {
 85643            exactRoot = true;
 85644            return candidateRaw;
 645        }
 646
 567647        if (candidateValue.Sign > 0)
 648        {
 649            // The analytic seed is never below the entry root, so its integer
 650            // floor cannot be below the root's integer floor. A positive value
 651            // proves that both floors are the same.
 563652            exactRoot = false;
 563653            return candidateRaw;
 654        }
 655
 4656        long lowRaw = 0L;
 4657        highRaw = candidateRaw;
 4658        long step = 1L;
 659        // Replacing the exact square root by its floor can overshoot the entry
 660        // root by fewer than 2^32 parameter raw units, so doubling cannot
 661        // overflow a signed 64-bit step before the bracket is found.
 104662        while (true)
 663        {
 108664            long nextRaw = highRaw - Math.Min(step, highRaw);
 108665            Signed320 nextValue = EvaluatePolynomial(
 108666                directionLengthSquared,
 108667                projection,
 108668                constant,
 108669                Signed192.Signed(nextRaw),
 108670                RawScale);
 108671            if (nextValue.Sign > 0)
 672            {
 4673                lowRaw = nextRaw;
 4674                break;
 675            }
 676
 104677            highRaw = nextRaw;
 104678            step <<= 1;
 679        }
 680
 108681        while (highRaw - lowRaw > 1L)
 682        {
 104683            long middleRaw = lowRaw + ((highRaw - lowRaw) >> 1);
 104684            Signed320 middleValue = EvaluatePolynomial(
 104685                directionLengthSquared,
 104686                projection,
 104687                constant,
 104688                Signed192.Signed(middleRaw),
 104689                RawScale);
 104690            if (middleValue.Sign > 0)
 55691                lowRaw = middleRaw;
 692            else
 49693                highRaw = middleRaw;
 694        }
 695
 696        // A downward correction is reachable only when the discriminant root
 697        // was not integral. The entry root therefore cannot be an integer raw
 698        // parameter inside this bracket.
 4699        exactRoot = false;
 4700        return lowRaw;
 701    }
 702
 703    private static long GetFloorRatioRaw(Signed192 numerator, Signed192 denominator)
 704    {
 1944705        Fixed64 rounded = Fixed64.GetSignedRatio(numerator, denominator);
 1944706        long raw = rounded.m_rawValue;
 1944707        if (raw <= 0L)
 21708            return raw;
 709
 1923710        Signed320 represented = WideArithmetic.MultiplySigned192(
 1923711            denominator,
 1923712            Signed192.Signed(raw));
 1923713        Signed320 exact = WideArithmetic.MultiplySigned192(numerator, RawScale);
 1923714        return WideArithmetic.CompareMagnitude(represented, exact) > 0 ? raw - 1L : raw;
 715    }
 716
 717    private static Signed192 GetRadiusSquared(Signed192 radius)
 718    {
 95719        _ = Signed192.TryNarrowSigned(
 95720            WideArithmetic.MultiplySigned192(radius, radius),
 95721            out Signed192 radiusSquared);
 95722        return radiusSquared;
 723    }
 724
 725    private static Signed320 EvaluatePolynomial(
 726        Signed192 directionLengthSquared,
 727        Signed192 projection,
 728        Signed192 constant,
 729        Signed192 timeNumerator,
 730        Signed192 timeDenominator)
 731    {
 4713732        _ = Signed192.TryNarrowSigned(
 4713733            WideArithmetic.MultiplySigned192(timeNumerator, timeNumerator),
 4713734            out Signed192 timeSquared);
 4713735        _ = Signed192.TryNarrowSigned(
 4713736            WideArithmetic.MultiplySigned192(timeNumerator, timeDenominator),
 4713737            out Signed192 timeProduct);
 4713738        _ = Signed192.TryNarrowSigned(
 4713739            WideArithmetic.MultiplySigned192(timeDenominator, timeDenominator),
 4713740            out Signed192 denominatorSquared);
 741
 4713742        Signed320 first = WideArithmetic.MultiplySigned192(directionLengthSquared, timeSquared);
 4713743        Signed320 second = WideArithmetic.MultiplySigned192(
 4713744            WideArithmetic.AddSigned192(projection, projection),
 4713745            timeProduct);
 4713746        Signed320 third = WideArithmetic.MultiplySigned192(constant, denominatorSquared);
 4713747        return WideArithmetic.AddSigned320(WideArithmetic.AddSigned320(first, second), third);
 748    }
 749
 750}

Methods/Properties

.cctor()
Intersects(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64)
TryGetInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetInterval(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
Intersects(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Geometry.FixedBoundCircle,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
IntersectsWide(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
TryGetIntervalWide(FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Vector2d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
Intersects(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Fixed64)
TryGetInterval(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TryGetInterval(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
Intersects(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
IntersectsWide(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
TryGetIntervalWide(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
TrySolveInterval(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&,FixedMathSharp.Fixed64&)
FindFloorUpperRoot(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Int64,System.Int64,System.Boolean&)
RoundUpperRoot(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Int64,System.Boolean)
IsAtOrBeforeUpperRoot(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
EvaluateDerivative(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)
Solve(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
SolveEntry(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64)
FindFloorRoot(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,System.Int64,System.Int64,System.Boolean&)
GetFloorRatioRaw(FixedMathSharp.Signed192,FixedMathSharp.Signed192)
GetRadiusSquared(FixedMathSharp.Signed192)
EvaluatePolynomial(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Signed192)