< 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    {
 435301        entry = default;
 435302        exit = default;
 435303        if (maxParameter < Fixed64.Zero)
 2304            return false;
 305
 433306        if (directionLengthSquared.IsZero)
 307        {
 12308            if (constant.Sign > 0)
 6309                return false;
 310
 6311            exit = maxParameter;
 6312            return true;
 313        }
 314
 421315        Signed320 discriminant = WideArithmetic.MultiplySubtract(
 421316            projection,
 421317            projection,
 421318            directionLengthSquared,
 421319            constant);
 421320        Signed192 squareRoot = default;
 421321        bool hasSquareRoot = false;
 421322        if (constant.Sign > 0)
 323        {
 406324            if (projection.Sign >= 0 || maxParameter == Fixed64.Zero || discriminant.Sign < 0)
 284325                return false;
 326
 122327            squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 122328            hasSquareRoot = true;
 122329            Fixed64? first = SolveEntry(
 122330                directionLengthSquared,
 122331                projection,
 122332                constant,
 122333                squareRoot,
 122334                maxParameter);
 122335            if (!first.HasValue)
 10336                return false;
 337
 112338            entry = first.Value;
 339        }
 340
 127341        long maxRaw = maxParameter.m_rawValue;
 127342        Signed192 maxNumerator = Signed192.Signed(maxRaw);
 127343        Signed320 valueAtMax = EvaluatePolynomial(
 127344            directionLengthSquared,
 127345            projection,
 127346            constant,
 127347            maxNumerator,
 127348            RawScale);
 127349        if (valueAtMax.Sign <= 0)
 350        {
 18351            exit = maxParameter;
 18352            return true;
 353        }
 354
 109355        if (discriminant.IsZero)
 356        {
 18357            exit = entry;
 18358            return true;
 359        }
 360
 91361        Signed192 negativeProjection = WideArithmetic.SubtractSigned192(default, projection);
 91362        if (!hasSquareRoot)
 13363            squareRoot = WideArithmetic.GetFloorSquareRoot(discriminant, out _);
 364
 91365        long approximateRaw = GetFloorRatioRaw(
 91366            WideArithmetic.AddSigned192(negativeProjection, squareRoot),
 91367            directionLengthSquared);
 91368        long floorRootRaw = FindFloorUpperRoot(
 91369            directionLengthSquared,
 91370            projection,
 91371            constant,
 91372            approximateRaw,
 91373            maxRaw,
 91374            out bool exactRoot);
 91375        exit = RoundUpperRoot(
 91376            directionLengthSquared,
 91377            projection,
 91378            constant,
 91379            floorRootRaw,
 91380            exactRoot);
 91381        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    {
 91392        long lowRaw = Math.Max(0L, Math.Min(approximateRaw, maxRaw - 1L));
 91393        long highRaw = maxRaw;
 91394        long step = 1L;
 395
 173396        while (highRaw - lowRaw > 1L)
 397        {
 173398            long remaining = highRaw - lowRaw;
 173399            long candidateRaw = lowRaw + Math.Min(step, remaining);
 173400            if (!IsAtOrBeforeUpperRoot(
 173401                    directionLengthSquared,
 173402                    projection,
 173403                    constant,
 173404                    Signed192.Signed(candidateRaw),
 173405                    RawScale))
 406            {
 91407                highRaw = candidateRaw;
 91408                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
 173418        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
 91436        Signed192 low = Signed192.Signed(lowRaw);
 91437        Signed320 lowValue = EvaluatePolynomial(
 91438            directionLengthSquared,
 91439            projection,
 91440            constant,
 91441            low,
 91442            RawScale);
 91443        Signed320 lowDerivative = EvaluateDerivative(
 91444            directionLengthSquared,
 91445            projection,
 91446            low,
 91447            RawScale);
 91448        exactRoot = lowValue.IsZero && lowDerivative.Sign >= 0;
 91449        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    {
 91459        if (exactRoot)
 49460            return Fixed64.FromRaw(floorRootRaw);
 461
 42462        Signed192 floorRoot = Signed192.Signed(floorRootRaw);
 42463        Signed192 midpointNumerator = WideArithmetic.AddSigned192(
 42464            WideArithmetic.AddSigned192(floorRoot, floorRoot),
 42465            Signed192.Signed(1L));
 42466        Signed320 midpointValue = EvaluatePolynomial(
 42467            directionLengthSquared,
 42468            projection,
 42469            constant,
 42470            midpointNumerator,
 42471            DoubleRawScale);
 42472        Signed320 midpointDerivative = EvaluateDerivative(
 42473            directionLengthSquared,
 42474            projection,
 42475            midpointNumerator,
 42476            DoubleRawScale);
 477
 42478        if (midpointValue.IsZero && midpointDerivative.Sign >= 0)
 479        {
 8480            return Fixed64.FromRaw((floorRootRaw & 1L) == 0L
 8481                ? floorRootRaw
 8482                : floorRootRaw + 1L);
 483        }
 484
 34485        bool roundUp = midpointDerivative.Sign <= 0 || midpointValue.Sign <= 0;
 34486        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.
 255498        EvaluatePolynomial(
 255499            directionLengthSquared,
 255500            projection,
 255501            constant,
 255502            timeNumerator,
 255503            timeDenominator).Sign <= 0;
 504
 505    private static Signed320 EvaluateDerivative(
 506        Signed192 directionLengthSquared,
 507        Signed192 projection,
 508        Signed192 timeNumerator,
 509        Signed192 timeDenominator) =>
 133510        WideArithmetic.AddSigned320(
 133511            WideArithmetic.MultiplySigned192(directionLengthSquared, timeNumerator),
 133512            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    {
 161549        Signed192 negativeProjection = WideArithmetic.SubtractSigned192(default, projection);
 161550        long approximateRaw = GetFloorRatioRaw(
 161551            constant,
 161552            WideArithmetic.AddSigned192(negativeProjection, squareRoot));
 161553        long maxRaw = maxParameter.m_rawValue;
 161554        Signed320 derivativeAtMax = WideArithmetic.SubtractSigned320(
 161555            WideArithmetic.MultiplySigned192(
 161556                directionLengthSquared,
 161557                Signed192.Signed(maxRaw)),
 161558            WideArithmetic.MultiplySigned192(negativeProjection, RawScale));
 161559        if (derivativeAtMax.Sign < 0
 161560            && EvaluatePolynomial(
 161561                directionLengthSquared,
 161562                projection,
 161563                constant,
 161564                Signed192.Signed(maxRaw),
 161565                RawScale).Sign > 0)
 566        {
 16567            return null;
 568        }
 569
 145570        long closestRaw = GetFloorRatioRaw(negativeProjection, directionLengthSquared);
 145571        long highRaw = closestRaw < maxRaw ? closestRaw : maxRaw;
 145572        Signed320 highValue = EvaluatePolynomial(
 145573            directionLengthSquared,
 145574            projection,
 145575            constant,
 145576            Signed192.Signed(highRaw),
 145577            RawScale);
 578
 579        long floorRootRaw;
 580        bool exactRoot;
 145581        if (highValue.Sign > 0)
 582        {
 9583            floorRootRaw = highRaw;
 9584            exactRoot = false;
 585        }
 586        else
 587        {
 136588            floorRootRaw = FindFloorRoot(
 136589                directionLengthSquared,
 136590                projection,
 136591                constant,
 136592                highRaw,
 136593                approximateRaw,
 136594                out exactRoot);
 595        }
 596
 145597        if (exactRoot || floorRootRaw == maxRaw)
 82598            return Fixed64.FromRaw(floorRootRaw);
 599
 63600        Signed192 floorRoot = Signed192.Signed(floorRootRaw);
 63601        Signed192 midpointNumerator = WideArithmetic.AddSigned192(
 63602            WideArithmetic.AddSigned192(floorRoot, floorRoot),
 63603            Signed192.Signed(1L));
 63604        Signed320 midpointValue = EvaluatePolynomial(
 63605            directionLengthSquared,
 63606            projection,
 63607            constant,
 63608            midpointNumerator,
 63609            DoubleRawScale);
 63610        Signed320 midpointDerivative = WideArithmetic.AddSigned320(
 63611            WideArithmetic.MultiplySigned192(directionLengthSquared, midpointNumerator),
 63612            WideArithmetic.MultiplySigned192(projection, DoubleRawScale));
 613
 63614        if (midpointValue.IsZero && midpointDerivative.Sign <= 0)
 615        {
 12616            return Fixed64.FromRaw((floorRootRaw & 1L) == 0L
 12617                ? floorRootRaw
 12618                : floorRootRaw + 1L);
 619        }
 620
 51621        bool roundUp = midpointDerivative.Sign < 0 && midpointValue.Sign > 0;
 51622        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    {
 136633        long candidateRaw = Math.Min(approximateRaw, highRaw);
 634
 136635        Signed320 candidateValue = EvaluatePolynomial(
 136636            directionLengthSquared,
 136637            projection,
 136638            constant,
 136639            Signed192.Signed(candidateRaw),
 136640            RawScale);
 136641        if (candidateValue.IsZero)
 642        {
 82643            exactRoot = true;
 82644            return candidateRaw;
 645        }
 646
 54647        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.
 50652            exactRoot = false;
 50653            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    {
 397705        Fixed64 rounded = Fixed64.GetSignedRatio(numerator, denominator);
 397706        long raw = rounded.m_rawValue;
 397707        if (raw <= 0L)
 21708            return raw;
 709
 376710        Signed320 represented = WideArithmetic.MultiplySigned192(
 376711            denominator,
 376712            Signed192.Signed(raw));
 376713        Signed320 exact = WideArithmetic.MultiplySigned192(numerator, RawScale);
 376714        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    {
 1105732        _ = Signed192.TryNarrowSigned(
 1105733            WideArithmetic.MultiplySigned192(timeNumerator, timeNumerator),
 1105734            out Signed192 timeSquared);
 1105735        _ = Signed192.TryNarrowSigned(
 1105736            WideArithmetic.MultiplySigned192(timeNumerator, timeDenominator),
 1105737            out Signed192 timeProduct);
 1105738        _ = Signed192.TryNarrowSigned(
 1105739            WideArithmetic.MultiplySigned192(timeDenominator, timeDenominator),
 1105740            out Signed192 denominatorSquared);
 741
 1105742        Signed320 first = WideArithmetic.MultiplySigned192(directionLengthSquared, timeSquared);
 1105743        Signed320 second = WideArithmetic.MultiplySigned192(
 1105744            WideArithmetic.AddSigned192(projection, projection),
 1105745            timeProduct);
 1105746        Signed320 third = WideArithmetic.MultiplySigned192(constant, denominatorSquared);
 1105747        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)