< 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    {
 695301        entry = default;
 695302        exit = default;
 695303        if (maxParameter < Fixed64.Zero)
 2304            return false;
 305
 693306        if (directionLengthSquared.IsZero)
 307        {
 12308            if (constant.Sign > 0)
 6309                return false;
 310
 6311            exit = maxParameter;
 6312            return true;
 313        }
 314
 681315        Signed320 discriminant = WideArithmetic.MultiplySubtract(
 681316            projection,
 681317            projection,
 681318            directionLengthSquared,
 681319            constant);
 681320        Signed192 squareRoot = default;
 681321        bool hasSquareRoot = false;
 681322        if (constant.Sign > 0)
 323        {
 665324            if (projection.Sign >= 0 || maxParameter == Fixed64.Zero || discriminant.Sign < 0)
 284325                return false;
 326
 381327            squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 381328            hasSquareRoot = true;
 381329            Fixed64? first = SolveEntry(
 381330                directionLengthSquared,
 381331                projection,
 381332                constant,
 381333                squareRoot,
 381334                maxParameter);
 381335            if (!first.HasValue)
 11336                return false;
 337
 370338            entry = first.Value;
 339        }
 340
 386341        long maxRaw = maxParameter.m_rawValue;
 386342        Signed192 maxNumerator = Signed192.Signed(maxRaw);
 386343        Signed320 valueAtMax = EvaluatePolynomial(
 386344            directionLengthSquared,
 386345            projection,
 386346            constant,
 386347            maxNumerator,
 386348            RawScale);
 386349        if (valueAtMax.Sign <= 0)
 350        {
 19351            exit = maxParameter;
 19352            return true;
 353        }
 354
 367355        if (discriminant.IsZero)
 356        {
 18357            exit = entry;
 18358            return true;
 359        }
 360
 349361        Signed192 negativeProjection = WideArithmetic.SubtractSigned192(default, projection);
 349362        if (!hasSquareRoot)
 14363            squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 364
 349365        long approximateRaw = GetFloorRatioRaw(
 349366            WideArithmetic.AddSigned192(negativeProjection, squareRoot),
 349367            directionLengthSquared);
 349368        long floorRootRaw = FindFloorUpperRoot(
 349369            directionLengthSquared,
 349370            projection,
 349371            constant,
 349372            approximateRaw,
 349373            maxRaw,
 349374            out bool exactRoot);
 349375        exit = RoundUpperRoot(
 349376            directionLengthSquared,
 349377            projection,
 349378            constant,
 349379            floorRootRaw,
 349380            exactRoot);
 349381        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    {
 349392        long lowRaw = Math.Max(0L, Math.Min(approximateRaw, maxRaw - 1L));
 349393        long highRaw = maxRaw;
 349394        long step = 1L;
 395
 431396        while (highRaw - lowRaw > 1L)
 397        {
 431398            long remaining = highRaw - lowRaw;
 431399            long candidateRaw = lowRaw + Math.Min(step, remaining);
 431400            if (!IsAtOrBeforeUpperRoot(
 431401                    directionLengthSquared,
 431402                    projection,
 431403                    constant,
 431404                    Signed192.Signed(candidateRaw),
 431405                    RawScale))
 406            {
 349407                highRaw = candidateRaw;
 349408                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
 431418        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
 349436        Signed192 low = Signed192.Signed(lowRaw);
 349437        Signed320 lowValue = EvaluatePolynomial(
 349438            directionLengthSquared,
 349439            projection,
 349440            constant,
 349441            low,
 349442            RawScale);
 349443        Signed320 lowDerivative = EvaluateDerivative(
 349444            directionLengthSquared,
 349445            projection,
 349446            low,
 349447            RawScale);
 349448        exactRoot = lowValue.IsZero && lowDerivative.Sign >= 0;
 349449        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    {
 349459        if (exactRoot)
 49460            return Fixed64.FromRaw(floorRootRaw);
 461
 300462        Signed192 floorRoot = Signed192.Signed(floorRootRaw);
 300463        Signed192 midpointNumerator = WideArithmetic.AddSigned192(
 300464            WideArithmetic.AddSigned192(floorRoot, floorRoot),
 300465            Signed192.Signed(1L));
 300466        Signed320 midpointValue = EvaluatePolynomial(
 300467            directionLengthSquared,
 300468            projection,
 300469            constant,
 300470            midpointNumerator,
 300471            DoubleRawScale);
 300472        Signed320 midpointDerivative = EvaluateDerivative(
 300473            directionLengthSquared,
 300474            projection,
 300475            midpointNumerator,
 300476            DoubleRawScale);
 477
 300478        if (midpointValue.IsZero && midpointDerivative.Sign >= 0)
 479        {
 8480            return Fixed64.FromRaw((floorRootRaw & 1L) == 0L
 8481                ? floorRootRaw
 8482                : floorRootRaw + 1L);
 483        }
 484
 292485        bool roundUp = midpointDerivative.Sign <= 0 || midpointValue.Sign <= 0;
 292486        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.
 513498        EvaluatePolynomial(
 513499            directionLengthSquared,
 513500            projection,
 513501            constant,
 513502            timeNumerator,
 513503            timeDenominator).Sign <= 0;
 504
 505    private static Signed320 EvaluateDerivative(
 506        Signed192 directionLengthSquared,
 507        Signed192 projection,
 508        Signed192 timeNumerator,
 509        Signed192 timeDenominator) =>
 649510        WideArithmetic.AddSigned320(
 649511            WideArithmetic.MultiplySigned192(directionLengthSquared, timeNumerator),
 649512            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    {
 420549        Signed192 negativeProjection = WideArithmetic.SubtractSigned192(default, projection);
 420550        long approximateRaw = GetFloorRatioRaw(
 420551            constant,
 420552            WideArithmetic.AddSigned192(negativeProjection, squareRoot));
 420553        long maxRaw = maxParameter.m_rawValue;
 420554        Signed320 derivativeAtMax = WideArithmetic.SubtractSigned320(
 420555            WideArithmetic.MultiplySigned192(
 420556                directionLengthSquared,
 420557                Signed192.Signed(maxRaw)),
 420558            WideArithmetic.MultiplySigned192(negativeProjection, RawScale));
 420559        if (derivativeAtMax.Sign < 0
 420560            && EvaluatePolynomial(
 420561                directionLengthSquared,
 420562                projection,
 420563                constant,
 420564                Signed192.Signed(maxRaw),
 420565                RawScale).Sign > 0)
 566        {
 17567            return null;
 568        }
 569
 403570        long closestRaw = GetFloorRatioRaw(negativeProjection, directionLengthSquared);
 403571        long highRaw = closestRaw < maxRaw ? closestRaw : maxRaw;
 403572        Signed320 highValue = EvaluatePolynomial(
 403573            directionLengthSquared,
 403574            projection,
 403575            constant,
 403576            Signed192.Signed(highRaw),
 403577            RawScale);
 578
 579        long floorRootRaw;
 580        bool exactRoot;
 403581        if (highValue.Sign > 0)
 582        {
 9583            floorRootRaw = highRaw;
 9584            exactRoot = false;
 585        }
 586        else
 587        {
 394588            floorRootRaw = FindFloorRoot(
 394589                directionLengthSquared,
 394590                projection,
 394591                constant,
 394592                highRaw,
 394593                approximateRaw,
 394594                out exactRoot);
 595        }
 596
 403597        if (exactRoot || floorRootRaw == maxRaw)
 82598            return Fixed64.FromRaw(floorRootRaw);
 599
 321600        Signed192 floorRoot = Signed192.Signed(floorRootRaw);
 321601        Signed192 midpointNumerator = WideArithmetic.AddSigned192(
 321602            WideArithmetic.AddSigned192(floorRoot, floorRoot),
 321603            Signed192.Signed(1L));
 321604        Signed320 midpointValue = EvaluatePolynomial(
 321605            directionLengthSquared,
 321606            projection,
 321607            constant,
 321608            midpointNumerator,
 321609            DoubleRawScale);
 321610        Signed320 midpointDerivative = WideArithmetic.AddSigned320(
 321611            WideArithmetic.MultiplySigned192(directionLengthSquared, midpointNumerator),
 321612            WideArithmetic.MultiplySigned192(projection, DoubleRawScale));
 613
 321614        if (midpointValue.IsZero && midpointDerivative.Sign <= 0)
 615        {
 12616            return Fixed64.FromRaw((floorRootRaw & 1L) == 0L
 12617                ? floorRootRaw
 12618                : floorRootRaw + 1L);
 619        }
 620
 309621        bool roundUp = midpointDerivative.Sign < 0 && midpointValue.Sign > 0;
 309622        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    {
 394633        long candidateRaw = Math.Min(approximateRaw, highRaw);
 634
 394635        Signed320 candidateValue = EvaluatePolynomial(
 394636            directionLengthSquared,
 394637            projection,
 394638            constant,
 394639            Signed192.Signed(candidateRaw),
 394640            RawScale);
 394641        if (candidateValue.IsZero)
 642        {
 82643            exactRoot = true;
 82644            return candidateRaw;
 645        }
 646
 312647        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.
 308652            exactRoot = false;
 308653            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    {
 1172705        Fixed64 rounded = Fixed64.GetSignedRatio(numerator, denominator);
 1172706        long raw = rounded.m_rawValue;
 1172707        if (raw <= 0L)
 21708            return raw;
 709
 1151710        Signed320 represented = WideArithmetic.MultiplySigned192(
 1151711            denominator,
 1151712            Signed192.Signed(raw));
 1151713        Signed320 exact = WideArithmetic.MultiplySigned192(numerator, RawScale);
 1151714        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    {
 2913732        _ = Signed192.TryNarrowSigned(
 2913733            WideArithmetic.MultiplySigned192(timeNumerator, timeNumerator),
 2913734            out Signed192 timeSquared);
 2913735        _ = Signed192.TryNarrowSigned(
 2913736            WideArithmetic.MultiplySigned192(timeNumerator, timeDenominator),
 2913737            out Signed192 timeProduct);
 2913738        _ = Signed192.TryNarrowSigned(
 2913739            WideArithmetic.MultiplySigned192(timeDenominator, timeDenominator),
 2913740            out Signed192 denominatorSquared);
 741
 2913742        Signed320 first = WideArithmetic.MultiplySigned192(directionLengthSquared, timeSquared);
 2913743        Signed320 second = WideArithmetic.MultiplySigned192(
 2913744            WideArithmetic.AddSigned192(projection, projection),
 2913745            timeProduct);
 2913746        Signed320 third = WideArithmetic.MultiplySigned192(constant, denominatorSquared);
 2913747        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)