< Summary

Line coverage
100%
Covered lines: 314
Uncovered lines: 0
Coverable lines: 314
Total lines: 894
Line coverage: 100%
Branch coverage
100%
Covered branches: 154
Total branches: 154
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 2: .ctor(...)100%11100%
File 2: .ctor(...)100%11100%
File 2: get_Radius()100%11100%
File 2: set_Radius(...)100%11100%
File 2: get_Min()100%11100%
File 2: get_Max()100%11100%
File 2: get_State()100%11100%
File 2: CreateFromBoundingBox(...)100%22100%
File 2: CreateFromFrustum(...)100%11100%
File 2: CreateFromPoints(...)100%66100%
File 2: CreateFromPoints(...)100%22100%
File 2: CreateFromPoints(...)100%11100%
File 2: CreateMerged(...)100%44100%
File 2: MergeNonContaining(...)100%88100%
File 2: CreateFromPointList(...)100%1818100%
File 2: CreateFromPointSpan(...)100%1818100%
File 2: CreateFromFrustumCorners(...)100%1616100%
File 2: CreateFromExtremePairs(...)100%88100%
File 2: ExpandToContain(...)100%22100%
File 2: ContainsSphere(...)100%22100%
File 2: TryGetMergedRadius(...)100%44100%
File 2: TryGetRequiredRadius(...)100%22100%
File 2: GetDistanceRoot(...)100%11100%
File 2: TryCreatePositiveRaw(...)100%22100%
File 2: GetFartherEndpoint(...)100%22100%
File 2: GetRawDistance(...)100%22100%
File 2: CreateUnrepresentableRadiusException()100%11100%
File 2: Contains(...)100%11100%
File 2: ContainsStrict(...)100%22100%
File 2: Contains(...)100%11100%
File 2: Contains(...)100%66100%
File 2: Contains(...)100%88100%
File 2: Intersects(...)100%11100%
File 2: Intersects(...)100%11100%
File 2: IntersectsStrict(...)100%11100%
File 2: IntersectsStrict(...)100%44100%
File 2: Intersects(...)100%11100%
File 2: Intersects(...)100%11100%
File 2: Intersects(...)100%11100%
File 2: ProjectPoint(...)100%22100%
File 2: ClampPoint(...)100%22100%
File 2: DistanceToSurface(...)100%11100%
File 2: Transform(...)100%11100%
File 2: Deconstruct(...)100%11100%
File 2: ContainsBoxLike(...)100%1818100%
File 2: GetMaxBasisScale(...)100%11100%
File 2: NormalizeRadius(...)100%11100%
File 2: op_Equality(...)100%11100%
File 2: op_Inequality(...)100%11100%
File 2: Equals(...)100%22100%
File 2: Equals(...)100%22100%
File 2: GetHashCode()100%11100%
File 2: ToString()100%11100%
File 2: ToString(...)100%11100%
File 2: TryFormat(...)100%1010100%

File(s)

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Bounds/FixedBoundSphere.BoundingSphereState.cs

#LineLine coverage
 1//=======================================================================
 2// FixedBoundSphere.BoundingSphereState.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.Collections.Generic;
 10using System.Globalization;
 11using System.Runtime.CompilerServices;
 12using System.Text.Json.Serialization;
 13using MemoryPack;
 14
 15namespace FixedMathSharp.Geometry;
 16
 17/// <content>BoundingSphereState declaration for FixedBoundSphere.</content>
 18public partial struct FixedBoundSphere
 19{
 20    /// <summary>
 21    /// Represents the normalized serializable state of a three-dimensional spherical bound.
 22    /// </summary>
 23    [Serializable]
 24    [MemoryPackable]
 25    public readonly partial struct BoundingSphereState
 26    {
 27        /// <inheritdoc cref="FixedBoundSphere.Center"/>
 28        [JsonInclude]
 29        [MemoryPackInclude]
 30        public readonly Vector3d Center;
 31
 32        /// <inheritdoc cref="FixedBoundSphere.Radius"/>
 33        [JsonInclude]
 34        [MemoryPackInclude]
 35        public readonly Fixed64 Radius;
 36
 37        /// <summary>
 38        /// Initializes a normalized state from center and radius.
 39        /// </summary>
 40        [JsonConstructor]
 41        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 42        public BoundingSphereState(Vector3d center, Fixed64 radius)
 43        {
 744            Center = center;
 745            Radius = NormalizeRadius(radius);
 746        }
 47    }
 48}

/home/runner/work/FixedMathSharp/FixedMathSharp/src/FixedMathSharp/Geometry/Bounds/FixedBoundSphere.cs

#LineLine coverage
 1//=======================================================================
 2// FixedBoundSphere.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.Collections.Generic;
 10using System.Globalization;
 11using System.Runtime.CompilerServices;
 12using System.Text.Json.Serialization;
 13using MemoryPack;
 14
 15namespace FixedMathSharp.Geometry;
 16
 17/// <summary>
 18/// Represents a spherical bounding volume with fixed-point precision, optimized for fast,
 19/// rotationally invariant spatial checks in 3D space.
 20/// </summary>
 21/// <remarks>
 22/// The FixedBoundSphere provides a simple yet effective way to represent the spatial extent of objects,
 23/// especially when rotational invariance is required.
 24/// Compared to FixedBoundBox, it offers faster intersection checks but is less precise in
 25/// tightly fitting non-spherical objects.
 26///
 27/// Use Cases:
 28/// - Ideal for broad-phase collision detection, proximity checks, and culling in physics engines and rendering pipeline
 29/// - Useful when fast, rotationally invariant checks are needed, such as detecting overlaps or distances between moving
 30/// - Suitable for encapsulating objects with roughly spherical shapes or objects that rotate frequently, where the boun
 31/// </remarks>
 32[Serializable]
 33[MemoryPackable]
 34public partial struct FixedBoundSphere : IEquatable<FixedBoundSphere>, IFormattable
 35#if NET10_0_OR_GREATER
 36    , ISpanFormattable
 37#endif
 38{
 39
 40    #region Fields
 41
 42    /// <summary>
 43    /// The radius backing field.
 44    /// </summary>
 45    [JsonIgnore]
 46    [MemoryPackIgnore]
 47    private Fixed64 _radius;
 48
 49    #endregion
 50
 51    #region Constructors
 52
 53    /// <summary>
 54    /// Initializes a new instance of the FixedBoundSphere struct with the specified center and radius.
 55    /// </summary>
 56    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 57    public FixedBoundSphere(Vector3d center, Fixed64 radius)
 58    {
 27259        Center = center;
 27260        _radius = NormalizeRadius(radius);
 27261    }
 62
 63    /// <summary>
 64    /// Initializes a new instance from serialized or caller-provided state.
 65    /// </summary>
 66    [JsonConstructor]
 67    public FixedBoundSphere(BoundingSphereState state)
 68    {
 369        Center = state.Center;
 370        _radius = state.Radius;
 371    }
 72
 73    #endregion
 74
 75    #region Properties
 76
 77    /// <summary>
 78    /// The center point of the sphere.
 79    /// </summary>
 80    [JsonIgnore]
 81    [MemoryPackIgnore]
 82    public Vector3d Center { get; set; }
 83
 84    /// <summary>
 85    /// The non-negative radius of the sphere. Assigned values are normalized by absolute value.
 86    /// </summary>
 87    [JsonIgnore]
 88    [MemoryPackIgnore]
 89    public Fixed64 Radius
 90    {
 91        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 118492        get => _radius;
 93
 94        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 195        set => _radius = NormalizeRadius(value);
 96    }
 97
 98    /// <summary>
 99    /// Gets the coordinates of the minimum corner of the bounding box that contains the sphere.
 100    /// </summary>
 101    /// <remarks>
 102    /// The minimum corner is calculated by subtracting the radius from each component of the sphere's center.
 103    /// This property is useful for spatial queries and bounding box calculations.
 104    /// </remarks>
 105    [JsonIgnore]
 106    [MemoryPackIgnore]
 107    public Vector3d Min
 108    {
 109        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3110        get => Center - new Vector3d(Radius, Radius, Radius);
 111    }
 112
 113    /// <summary>
 114    /// Gets the coordinates of the maximum corner of the bounding box that contains the sphere.
 115    /// </summary>
 116    /// <remarks>
 117    /// The maximum corner is calculated as the center of the sphere plus the radius in each dimension.
 118    /// This property is useful for spatial queries and bounding volume calculations.
 119    /// </remarks>
 120    [JsonIgnore]
 121    [MemoryPackIgnore]
 122    public Vector3d Max
 123    {
 124        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3125        get => Center + new Vector3d(Radius, Radius, Radius);
 126    }
 127
 128    /// <summary>
 129    /// Gets the current normalized state of the sphere.
 130    /// </summary>
 131    [JsonInclude]
 132    [MemoryPackInclude]
 3133    public BoundingSphereState State => new(Center, Radius);
 134
 135    #endregion
 136
 137    #region Methods (Static)
 138
 139    /// <summary>
 140    /// Creates a bounding sphere that contains the specified axis-aligned bounding box.
 141    /// </summary>
 142    /// <exception cref="OverflowException">
 143    /// Thrown when this construction requires an unrepresentable radius.
 144    /// </exception>
 145    public static FixedBoundSphere CreateFromBoundingBox(FixedBoundBox box)
 146    {
 4147        Vector3d center = Vector3d.Midpoint(box.Min, box.Max);
 4148        Vector3d farthestCorner = new(
 4149            GetFartherEndpoint(box.Min.X, box.Max.X, center.X),
 4150            GetFartherEndpoint(box.Min.Y, box.Max.Y, center.Y),
 4151            GetFartherEndpoint(box.Min.Z, box.Max.Z, center.Z));
 4152        if (!TryGetRequiredRadius(center, farthestCorner, Fixed64.Zero, out Fixed64 radius))
 1153            throw CreateUnrepresentableRadiusException();
 154
 3155        return new FixedBoundSphere(center, radius);
 156    }
 157
 158    /// <summary>
 159    /// Creates a bounding sphere that contains the specified frustum.
 160    /// </summary>
 161    /// <exception cref="OverflowException">
 162    /// Thrown when this construction requires an unrepresentable radius.
 163    /// </exception>
 164    public static FixedBoundSphere CreateFromFrustum(FixedBoundFrustum frustum)
 165    {
 7166        return CreateFromFrustumCorners(frustum);
 167    }
 168
 169    /// <summary>
 170    /// Creates a bounding sphere that contains the specified points.
 171    /// </summary>
 172    /// <exception cref="OverflowException">
 173    /// Thrown when this construction requires an unrepresentable radius.
 174    /// </exception>
 175    public static FixedBoundSphere CreateFromPoints(IEnumerable<Vector3d> points)
 176    {
 6177        if (points is null)
 1178            throw new ArgumentNullException(nameof(points), "Cannot create a bounding sphere from a null collection of p
 179
 5180        if (points is IReadOnlyList<Vector3d> pointList)
 4181            return CreateFromPointList(pointList);
 182
 1183        var materialized = new List<Vector3d>();
 10184        foreach (Vector3d point in points)
 4185            materialized.Add(point);
 186
 1187        return CreateFromPointList(materialized);
 188    }
 189
 190    /// <summary>
 191    /// Creates a bounding sphere that contains the specified points.
 192    /// </summary>
 193    /// <exception cref="OverflowException">
 194    /// Thrown when this construction requires an unrepresentable radius.
 195    /// </exception>
 196    public static FixedBoundSphere CreateFromPoints(Vector3d[] points)
 197    {
 10198        if (points is null)
 1199            throw new ArgumentNullException(nameof(points), "Cannot create a bounding sphere from a null collection of p
 200
 9201        return CreateFromPoints(points.AsSpan());
 202    }
 203
 204    /// <summary>
 205    /// Creates a bounding sphere that contains the specified points.
 206    /// </summary>
 207    /// <exception cref="OverflowException">
 208    /// Thrown when this construction requires an unrepresentable radius.
 209    /// </exception>
 210    public static FixedBoundSphere CreateFromPoints(ReadOnlySpan<Vector3d> points)
 211    {
 14212        return CreateFromPointSpan(points);
 213    }
 214
 215    /// <summary>
 216    /// Creates a deterministic sphere that contains the two specified spheres.
 217    /// </summary>
 218    /// <exception cref="OverflowException">
 219    /// Thrown when this construction cannot produce a containing sphere with a
 220    /// representable radius.
 221    /// </exception>
 222    public static FixedBoundSphere CreateMerged(FixedBoundSphere original, FixedBoundSphere additional)
 223    {
 13224        if (ContainsSphere(original, additional))
 2225            return original;
 226
 11227        if (ContainsSphere(additional, original))
 2228            return additional;
 229
 9230        return MergeNonContaining(original, additional);
 231    }
 232
 233    private static FixedBoundSphere MergeNonContaining(
 234        FixedBoundSphere original,
 235        FixedBoundSphere additional)
 236    {
 237
 36238        GetDistanceRoot(original.Center, additional.Center, out Signed192 distanceFloor, out Signed192 distanceRemainder
 36239        if (!TryGetMergedRadius(
 36240                distanceFloor,
 36241                distanceRemainder,
 36242                original.Radius,
 36243                additional.Radius,
 36244                out Fixed64 radius))
 245        {
 1246            throw CreateUnrepresentableRadiusException();
 247        }
 248
 35249        Signed192 firstGap = Signed192.Signed(
 35250            (radius - original.Radius).m_rawValue);
 35251        Signed192 secondGap = Signed192.Signed(
 35252            (radius - additional.Radius).m_rawValue);
 35253        Signed192 gapSum = WideArithmetic.AddSigned192(firstGap, secondGap);
 35254        Vector3d center = new(
 35255            WideGeometry.InterpolateCoordinate(
 35256                original.Center.X,
 35257                additional.Center.X,
 35258                firstGap,
 35259                gapSum),
 35260            WideGeometry.InterpolateCoordinate(
 35261                original.Center.Y,
 35262                additional.Center.Y,
 35263                firstGap,
 35264                gapSum),
 35265            WideGeometry.InterpolateCoordinate(
 35266                original.Center.Z,
 35267                additional.Center.Z,
 35268                firstGap,
 35269                gapSum));
 270
 35271        var merged = new FixedBoundSphere(center, radius);
 35272        bool containsOriginal = ContainsSphere(merged, original);
 35273        bool containsAdditional = ContainsSphere(merged, additional);
 35274        if (containsOriginal & containsAdditional)
 15275            return merged;
 276
 20277        bool originalRadiusRepresentable = TryGetRequiredRadius(
 20278            center,
 20279            original.Center,
 20280            original.Radius,
 20281            out Fixed64 originalRequired);
 20282        bool additionalRadiusRepresentable = TryGetRequiredRadius(
 20283            center,
 20284            additional.Center,
 20285            additional.Radius,
 20286            out Fixed64 additionalRequired);
 20287        if (!(originalRadiusRepresentable & additionalRadiusRepresentable))
 288        {
 1289            throw CreateUnrepresentableRadiusException();
 290        }
 291
 19292        return new FixedBoundSphere(
 19293            center,
 19294            originalRequired >= additionalRequired ? originalRequired : additionalRequired);
 295    }
 296
 297    private static FixedBoundSphere CreateFromPointList(IReadOnlyList<Vector3d> points)
 298    {
 5299        if (points.Count == 0)
 1300            throw new ArgumentException("At least one point is required to create a bounding sphere.");
 301
 4302        Vector3d minX = points[0];
 4303        Vector3d maxX = points[0];
 4304        Vector3d minY = points[0];
 4305        Vector3d maxY = points[0];
 4306        Vector3d minZ = points[0];
 4307        Vector3d maxZ = points[0];
 308
 24309        for (int i = 1; i < points.Count; i++)
 310        {
 8311            Vector3d point = points[i];
 312
 9313            if (point.X < minX.X) minX = point;
 11314            if (point.X > maxX.X) maxX = point;
 9315            if (point.Y < minY.Y) minY = point;
 10316            if (point.Y > maxY.Y) maxY = point;
 9317            if (point.Z < minZ.Z) minZ = point;
 11318            if (point.Z > maxZ.Z) maxZ = point;
 319        }
 320
 4321        FixedBoundSphere sphere = CreateFromExtremePairs(minX, maxX, minY, maxY, minZ, maxZ);
 32322        for (int i = 0; i < points.Count; i++)
 12323            ExpandToContain(ref sphere, points[i]);
 324
 4325        return sphere;
 326    }
 327
 328    private static FixedBoundSphere CreateFromPointSpan(ReadOnlySpan<Vector3d> points)
 329    {
 14330        if (points.Length == 0)
 2331            throw new ArgumentException("At least one point is required to create a bounding sphere.");
 332
 12333        Vector3d minX = points[0];
 12334        Vector3d maxX = points[0];
 12335        Vector3d minY = points[0];
 12336        Vector3d maxY = points[0];
 12337        Vector3d minZ = points[0];
 12338        Vector3d maxZ = points[0];
 339
 80340        for (int i = 1; i < points.Length; i++)
 341        {
 28342            Vector3d point = points[i];
 343
 30344            if (point.X < minX.X) minX = point;
 40345            if (point.X > maxX.X) maxX = point;
 31346            if (point.Y < minY.Y) minY = point;
 37347            if (point.Y > maxY.Y) maxY = point;
 31348            if (point.Z < minZ.Z) minZ = point;
 30349            if (point.Z > maxZ.Z) maxZ = point;
 350        }
 351
 12352        FixedBoundSphere sphere = CreateFromExtremePairs(minX, maxX, minY, maxY, minZ, maxZ);
 98353        for (int i = 0; i < points.Length; i++)
 38354            ExpandToContain(ref sphere, points[i]);
 355
 11356        return sphere;
 357    }
 358
 359    private static FixedBoundSphere CreateFromFrustumCorners(FixedBoundFrustum frustum)
 360    {
 7361        Vector3d minX = frustum.GetCorner(0);
 7362        Vector3d maxX = minX;
 7363        Vector3d minY = minX;
 7364        Vector3d maxY = minX;
 7365        Vector3d minZ = minX;
 7366        Vector3d maxZ = minX;
 367
 112368        for (int i = 1; i < FixedBoundFrustum.CornerCount; i++)
 369        {
 49370            Vector3d point = frustum.GetCorner(i);
 371
 51372            if (point.X < minX.X) minX = point;
 56373            if (point.X > maxX.X) maxX = point;
 56374            if (point.Y < minY.Y) minY = point;
 51375            if (point.Y > maxY.Y) maxY = point;
 50376            if (point.Z < minZ.Z) minZ = point;
 58377            if (point.Z > maxZ.Z) maxZ = point;
 378        }
 379
 7380        FixedBoundSphere sphere = CreateFromExtremePairs(minX, maxX, minY, maxY, minZ, maxZ);
 126381        for (int i = 0; i < FixedBoundFrustum.CornerCount; i++)
 56382            ExpandToContain(ref sphere, frustum.GetCorner(i));
 383
 7384        return sphere;
 385    }
 386
 387    private static FixedBoundSphere CreateFromExtremePairs(
 388        Vector3d minX,
 389        Vector3d maxX,
 390        Vector3d minY,
 391        Vector3d maxY,
 392        Vector3d minZ,
 393        Vector3d maxZ)
 394    {
 23395        Vector3d min = minX;
 23396        Vector3d max = maxX;
 23397        if (Vector3d.CompareDistanceSquared(minY, maxY, min, max) > 0)
 398        {
 13399            min = minY;
 13400            max = maxY;
 401        }
 23402        if (Vector3d.CompareDistanceSquared(minZ, maxZ, min, max) > 0)
 403        {
 5404            min = minZ;
 5405            max = maxZ;
 406        }
 407
 23408        Vector3d center = Vector3d.Midpoint(min, max);
 23409        Vector3d farthest = Vector3d.CompareDistanceSquared(center, min, center, max) > 0
 23410            ? min
 23411            : max;
 23412        if (!TryGetRequiredRadius(center, farthest, Fixed64.Zero, out Fixed64 radius))
 413        {
 1414            throw CreateUnrepresentableRadiusException();
 415        }
 416
 22417        return new FixedBoundSphere(center, radius);
 418    }
 419
 420    private static void ExpandToContain(ref FixedBoundSphere sphere, Vector3d point)
 421    {
 106422        if (sphere.Contains(point))
 79423            return;
 424
 27425        sphere = MergeNonContaining(sphere, new FixedBoundSphere(point, Fixed64.Zero));
 27426    }
 427
 428    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 429    private static bool ContainsSphere(FixedBoundSphere outer, FixedBoundSphere inner)
 430    {
 94431        if (outer.Radius < inner.Radius)
 5432            return false;
 433
 89434        return WideGeometry.CompareDistanceToRadiusSum(
 89435            outer.Center,
 89436            inner.Center,
 89437            outer.Radius - inner.Radius,
 89438            Fixed64.Zero) <= 0;
 439    }
 440
 441    private static bool TryGetMergedRadius(
 442        Signed192 distanceFloor,
 443        Signed192 distanceRemainder,
 444        Fixed64 firstRadius,
 445        Fixed64 secondRadius,
 446        out Fixed64 radius)
 447    {
 36448        Signed192 sum = WideArithmetic.AddSigned192(
 36449            distanceFloor,
 36450            Signed192.Signed(firstRadius.m_rawValue));
 36451        sum = WideArithmetic.AddSigned192(
 36452            sum,
 36453            Signed192.Signed(secondRadius.m_rawValue));
 36454        bool roundUp = (sum.Low & 1UL) != 0UL || !distanceRemainder.IsZero;
 36455        WideArithmetic.GetMagnitude(sum, out ulong high, out ulong middle, out ulong low);
 36456        WideArithmetic.ShiftRightOne(ref high, ref middle, ref low);
 36457        if (roundUp)
 458        {
 30459            Signed192 rounded = WideArithmetic.AddSigned192(
 30460                new Signed192(high, middle, low),
 30461                Signed192.Signed(1L));
 30462            high = rounded.High;
 30463            middle = rounded.Middle;
 30464            low = rounded.Low;
 465        }
 466
 36467        return TryCreatePositiveRaw(high, middle, low, out radius);
 468    }
 469
 470    private static bool TryGetRequiredRadius(
 471        Vector3d center,
 472        Vector3d enclosedCenter,
 473        Fixed64 enclosedRadius,
 474        out Fixed64 radius)
 475    {
 67476        GetDistanceRoot(center, enclosedCenter, out Signed192 distanceFloor, out Signed192 distanceRemainder);
 67477        if (!distanceRemainder.IsZero)
 478        {
 55479            distanceFloor = WideArithmetic.AddSigned192(
 55480                distanceFloor,
 55481                Signed192.Signed(1L));
 482        }
 483
 67484        Signed192 required = WideArithmetic.AddSigned192(
 67485            distanceFloor,
 67486            Signed192.Signed(enclosedRadius.m_rawValue));
 67487        WideArithmetic.GetMagnitude(required, out ulong high, out ulong middle, out ulong low);
 67488        return TryCreatePositiveRaw(high, middle, low, out radius);
 489    }
 490
 491    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 492    private static void GetDistanceRoot(
 493        Vector3d first,
 494        Vector3d second,
 495        out Signed192 floor,
 496        out Signed192 remainder)
 497    {
 103498        Signed192 squaredDistance = WideGeometry.GetDifferenceDotProduct3D(
 103499            first.X, second.X, first.Y, second.Y, first.Z, second.Z,
 103500            first.X, second.X, first.Y, second.Y, first.Z, second.Z);
 103501        floor = WideArithmetic.GetFloorSquareRoot(
 103502            Signed320.ExtendValue(squaredDistance),
 103503            out remainder);
 103504    }
 505
 506    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 507    private static bool TryCreatePositiveRaw(
 508        ulong high,
 509        ulong middle,
 510        ulong low,
 511        out Fixed64 value)
 512    {
 103513        if ((high | middle | (low >> 63)) != 0UL)
 514        {
 4515            value = default;
 4516            return false;
 517        }
 518
 99519        value = Fixed64.FromRaw((long)low);
 99520        return true;
 521    }
 522
 523    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 524    private static Fixed64 GetFartherEndpoint(Fixed64 first, Fixed64 second, Fixed64 center)
 525    {
 12526        ulong firstDistance = GetRawDistance(first.m_rawValue, center.m_rawValue);
 12527        ulong secondDistance = GetRawDistance(second.m_rawValue, center.m_rawValue);
 12528        return firstDistance > secondDistance ? first : second;
 529    }
 530
 531    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 532    private static ulong GetRawDistance(long first, long second) =>
 24533        first >= second
 24534            ? unchecked((ulong)first - (ulong)second)
 24535            : unchecked((ulong)second - (ulong)first);
 536
 537    private static OverflowException CreateUnrepresentableRadiusException() =>
 4538        new("A containing sphere produced for the supplied geometry requires an unrepresentable radius.");
 539
 540    #endregion
 541
 542    #region Methods (Instance)
 543
 544    /// <summary>
 545    /// Checks if a point is inside the sphere.
 546    /// </summary>
 547    /// <param name="point">The point to check.</param>
 548    /// <returns>True if the point is inside the sphere, otherwise false.</returns>
 549    public bool Contains(Vector3d point)
 550    {
 227551        return WideGeometry.CompareDistanceToRadiusSum(Center, point, Radius, Fixed64.Zero) <= 0;
 552    }
 553
 554    /// <summary>
 555    /// Returns whether the point lies strictly inside this sphere.
 556    /// </summary>
 557    /// <remarks>
 558    /// Boundary points and every point tested against a zero-radius sphere
 559    /// return <see langword="false"/>.
 560    /// </remarks>
 561    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 562    public bool ContainsStrict(Vector3d point) =>
 3563        Radius > Fixed64.Zero
 3564        && WideGeometry.CompareDistanceToRadiusSum(Center, point, Radius, Fixed64.Zero) < 0;
 565
 566    /// <summary>
 567    /// Tests a bounding box against this sphere.
 568    /// </summary>
 569    public FixedEnclosureType Contains(FixedBoundBox box)
 570    {
 13571        return ContainsBoxLike(box.Min, box.Max);
 572    }
 573
 574    /// <summary>
 575    /// Tests another sphere against this sphere.
 576    /// </summary>
 577    public FixedEnclosureType Contains(FixedBoundSphere sphere)
 578    {
 19579        if (WideGeometry.CompareDistanceToRadiusSum(Center, sphere.Center, Radius, sphere.Radius) > 0)
 5580            return FixedEnclosureType.Disjoint;
 581
 14582        Fixed64 radiusDifference = Radius - sphere.Radius;
 14583        if (radiusDifference >= Fixed64.Zero
 14584            && WideGeometry.CompareDistanceToRadiusSum(
 14585                Center,
 14586                sphere.Center,
 14587                radiusDifference,
 14588                Fixed64.Zero) <= 0)
 7589            return FixedEnclosureType.Contains;
 590
 7591        return FixedEnclosureType.Intersects;
 592    }
 593
 594    /// <summary>
 595    /// Tests a frustum against this sphere.
 596    /// </summary>
 597    public FixedEnclosureType Contains(FixedBoundFrustum frustum)
 598    {
 3599        bool containsAllCorners = true;
 600
 22601        for (int i = 0; i < FixedBoundFrustum.CornerCount; i++)
 602        {
 10603            if (!Contains(frustum.GetCorner(i)))
 604            {
 2605                containsAllCorners = false;
 2606                break;
 607            }
 608        }
 609
 3610        if (containsAllCorners)
 1611            return FixedEnclosureType.Contains;
 612
 2613        return frustum.Intersects(this)
 2614            ? FixedEnclosureType.Intersects
 2615            : FixedEnclosureType.Disjoint;
 616    }
 617
 618    /// <summary>
 619    /// Checks whether a bounding box intersects this sphere, including boundary-only contact.
 620    /// </summary>
 621    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6622    public bool Intersects(FixedBoundBox box) => Contains(box) != FixedEnclosureType.Disjoint;
 623
 624    /// <summary>
 625    /// Checks whether another sphere intersects this sphere, including boundary-only contact.
 626    /// </summary>
 627    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 6628    public bool Intersects(FixedBoundSphere sphere) => Contains(sphere) != FixedEnclosureType.Disjoint;
 629
 630    /// <summary>
 631    /// Checks whether a bounding box overlaps this sphere with positive volume.
 632    /// </summary>
 633    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 3634    public bool IntersectsStrict(FixedBoundBox box) => box.IntersectsStrict(this);
 635
 636    /// <summary>
 637    /// Checks whether another sphere overlaps this sphere with positive volume.
 638    /// </summary>
 639    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 640    public bool IntersectsStrict(FixedBoundSphere sphere)
 641    {
 4642        return Radius > Fixed64.Zero
 4643            && sphere.Radius > Fixed64.Zero
 4644            && WideGeometry.CompareDistanceToRadiusSum(Center, sphere.Center, Radius, sphere.Radius) < 0;
 645    }
 646
 647    /// <summary>
 648    /// Checks whether a frustum intersects this sphere.
 649    /// </summary>
 650    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 651    public bool Intersects(FixedBoundFrustum frustum)
 652    {
 1653        return frustum.Intersects(this);
 654    }
 655
 656    /// <summary>
 657    /// Classifies this sphere relative to a plane.
 658    /// </summary>
 659    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1660    public FixedPlaneIntersectionType Intersects(FixedPlane plane) => plane.Intersects(this);
 661
 662    /// <summary>
 663    /// Finds the first forward ray intersection with this sphere.
 664    /// </summary>
 665    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1666    public Fixed64? Intersects(FixedRay ray) => ray.Intersects(this);
 667
 668    /// <summary>
 669    /// Projects a point onto the bounding sphere. If the point is outside the sphere, it returns the closest point on t
 670    /// </summary>
 671    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 672    public Vector3d ProjectPoint(Vector3d point)
 673    {
 3674        var direction = point - Center;
 4675        if (direction.IsZero) return Center; // If the point is the center, return the center itself
 676
 2677        return Center + direction.NormalizeInPlace() * Radius;
 678    }
 679
 680    /// <summary>
 681    /// Clamps a point to this sphere, returning the point unchanged when it is already inside the sphere.
 682    /// </summary>
 683    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 684    public Vector3d ClampPoint(Vector3d point)
 685    {
 3686        if (Contains(point))
 2687            return point;
 688
 1689        return ProjectPoint(point);
 690    }
 691
 692    /// <summary>
 693    /// Calculates the distance from a point to the surface of the sphere.
 694    /// </summary>
 695    /// <param name="point">The point to calculate the distance from.</param>
 696    /// <returns>The distance from the point to the surface of the sphere.</returns>
 697    public Fixed64 DistanceToSurface(Vector3d point)
 698    {
 4699        return Vector3d.Distance(Center, point) - Radius;
 700    }
 701
 702    /// <summary>
 703    /// Creates a sphere that contains this sphere transformed by the specified matrix.
 704    /// </summary>
 705    public FixedBoundSphere Transform(Fixed4x4 matrix)
 706    {
 1707        Vector3d center = Fixed4x4.TransformPoint(matrix, Center);
 1708        Fixed64 scale = GetMaxBasisScale(matrix);
 709
 1710        return new FixedBoundSphere(center, Radius * scale);
 711    }
 712
 713    /// <summary>
 714    /// Deconstructs this sphere into its center and radius.
 715    /// </summary>
 716    public void Deconstruct(out Vector3d center, out Fixed64 radius)
 717    {
 1718        center = Center;
 1719        radius = Radius;
 1720    }
 721
 722    private FixedEnclosureType ContainsBoxLike(Vector3d min, Vector3d max)
 723    {
 13724        bool containsAllCorners =
 13725            Contains(new Vector3d(min.X, min.Y, min.Z)) &&
 13726            Contains(new Vector3d(max.X, min.Y, min.Z)) &&
 13727            Contains(new Vector3d(min.X, max.Y, min.Z)) &&
 13728            Contains(new Vector3d(max.X, max.Y, min.Z)) &&
 13729            Contains(new Vector3d(min.X, min.Y, max.Z)) &&
 13730            Contains(new Vector3d(max.X, min.Y, max.Z)) &&
 13731            Contains(new Vector3d(min.X, max.Y, max.Z)) &&
 13732            Contains(new Vector3d(max.X, max.Y, max.Z));
 733
 13734        if (containsAllCorners)
 3735            return FixedEnclosureType.Contains;
 736
 10737        Vector3d closest = new(
 10738            FixedMath.Clamp(Center.X, min.X, max.X),
 10739            FixedMath.Clamp(Center.Y, min.Y, max.Y),
 10740            FixedMath.Clamp(Center.Z, min.Z, max.Z));
 741
 10742        return WideGeometry.CompareDistanceToRadiusSum(Center, closest, Radius, Fixed64.Zero) <= 0
 10743            ? FixedEnclosureType.Intersects
 10744            : FixedEnclosureType.Disjoint;
 745    }
 746
 747    private static Fixed64 GetMaxBasisScale(Fixed4x4 matrix)
 748    {
 1749        Fixed64 row0 = matrix.M11 * matrix.M11 + matrix.M12 * matrix.M12 + matrix.M13 * matrix.M13;
 1750        Fixed64 row1 = matrix.M21 * matrix.M21 + matrix.M22 * matrix.M22 + matrix.M23 * matrix.M23;
 1751        Fixed64 row2 = matrix.M31 * matrix.M31 + matrix.M32 * matrix.M32 + matrix.M33 * matrix.M33;
 752
 1753        return FixedMath.Sqrt(FixedMath.Max(row0, FixedMath.Max(row1, row2)));
 754    }
 755
 756    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 280757    private static Fixed64 NormalizeRadius(Fixed64 radius) => FixedMath.Abs(radius);
 758
 759    #endregion
 760
 761    #region Operators
 762
 763    /// <summary>
 764    /// Determines whether two FixedBoundSphere instances are equal.
 765    /// </summary>
 766    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2767    public static bool operator ==(FixedBoundSphere left, FixedBoundSphere right) => left.Equals(right);
 768
 769    /// <summary>
 770    /// Determines whether two FixedBoundSphere instances are not equal.
 771    /// </summary>
 772    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1773    public static bool operator !=(FixedBoundSphere left, FixedBoundSphere right) => !left.Equals(right);
 774
 775    #endregion
 776
 777    #region Equality and HashCode Overrides
 778
 779    /// <inheritdoc/>
 780    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2781    public override bool Equals(object? obj) => obj is FixedBoundSphere other && Equals(other);
 782
 783    /// <inheritdoc/>
 784    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 11785    public bool Equals(FixedBoundSphere other) => Center.Equals(other.Center) && Radius.Equals(other.Radius);
 786
 787    /// <inheritdoc/>
 788    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 789    public override int GetHashCode()
 790    {
 791        unchecked
 792        {
 2793            int hash = 17;
 2794            hash = hash * 23 + Center.GetHashCode();
 2795            hash = hash * 23 + Radius.GetHashCode();
 2796            return hash;
 797        }
 798    }
 799
 800    /// <summary>
 801    /// Returns a string that represents the current FixedBoundSphere.
 802    /// </summary>
 1803    public override string ToString() => ToString(null, CultureInfo.InvariantCulture);
 804
 805    /// <summary>
 806    /// Returns a string that represents the current FixedBoundSphere.
 807    /// </summary>
 808    public string ToString(string? format, IFormatProvider? formatProvider)
 809    {
 2810        FixedBoundSphere value = this;
 2811        return FixedDiagnosticsFormatter.ToString((Span<char> destination, out int charsWritten) =>
 2812            value.TryFormat(destination, out charsWritten, format.AsSpan(), formatProvider));
 813    }
 814
 815    /// <summary>
 816    /// Formats this sphere into the provided destination buffer.
 817    /// </summary>
 818    public bool TryFormat(
 819        Span<char> destination,
 820        out int charsWritten,
 821        ReadOnlySpan<char> format,
 822        IFormatProvider? provider)
 823    {
 44824        int written = 0;
 44825        if (!FixedDiagnosticsFormatter.Append("{Center:", destination, ref written) ||
 44826            !Center.TryFormat(destination[written..], out int centerChars, format, provider))
 827        {
 27828            charsWritten = 0;
 27829            return false;
 830        }
 831
 17832        written += centerChars;
 17833        if (!FixedDiagnosticsFormatter.Append(" Radius:", destination, ref written) ||
 17834            !FixedDiagnosticsFormatter.Append(Radius, destination, ref written, format, provider) ||
 17835            !FixedDiagnosticsFormatter.Append('}', destination, ref written))
 836        {
 14837            charsWritten = 0;
 14838            return false;
 839        }
 840
 3841        charsWritten = written;
 3842        return true;
 843    }
 844
 845    #endregion
 846}

Methods/Properties

.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
.ctor(FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
.ctor(FixedMathSharp.Geometry.FixedBoundSphere/BoundingSphereState)
get_Radius()
set_Radius(FixedMathSharp.Fixed64)
get_Min()
get_Max()
get_State()
CreateFromBoundingBox(FixedMathSharp.Geometry.FixedBoundBox)
CreateFromFrustum(FixedMathSharp.Geometry.FixedBoundFrustum)
CreateFromPoints(System.Collections.Generic.IEnumerable`1<FixedMathSharp.Vector3d>)
CreateFromPoints(FixedMathSharp.Vector3d[])
CreateFromPoints(System.ReadOnlySpan`1<FixedMathSharp.Vector3d>)
CreateMerged(FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Geometry.FixedBoundSphere)
MergeNonContaining(FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Geometry.FixedBoundSphere)
CreateFromPointList(System.Collections.Generic.IReadOnlyList`1<FixedMathSharp.Vector3d>)
CreateFromPointSpan(System.ReadOnlySpan`1<FixedMathSharp.Vector3d>)
CreateFromFrustumCorners(FixedMathSharp.Geometry.FixedBoundFrustum)
CreateFromExtremePairs(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
ExpandToContain(FixedMathSharp.Geometry.FixedBoundSphere&,FixedMathSharp.Vector3d)
ContainsSphere(FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Geometry.FixedBoundSphere)
TryGetMergedRadius(FixedMathSharp.Signed192,FixedMathSharp.Signed192,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
TryGetRequiredRadius(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64&)
GetDistanceRoot(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Signed192&,FixedMathSharp.Signed192&)
TryCreatePositiveRaw(System.UInt64,System.UInt64,System.UInt64,FixedMathSharp.Fixed64&)
GetFartherEndpoint(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
GetRawDistance(System.Int64,System.Int64)
CreateUnrepresentableRadiusException()
Contains(FixedMathSharp.Vector3d)
ContainsStrict(FixedMathSharp.Vector3d)
Contains(FixedMathSharp.Geometry.FixedBoundBox)
Contains(FixedMathSharp.Geometry.FixedBoundSphere)
Contains(FixedMathSharp.Geometry.FixedBoundFrustum)
Intersects(FixedMathSharp.Geometry.FixedBoundBox)
Intersects(FixedMathSharp.Geometry.FixedBoundSphere)
IntersectsStrict(FixedMathSharp.Geometry.FixedBoundBox)
IntersectsStrict(FixedMathSharp.Geometry.FixedBoundSphere)
Intersects(FixedMathSharp.Geometry.FixedBoundFrustum)
Intersects(FixedMathSharp.Geometry.FixedPlane)
Intersects(FixedMathSharp.Geometry.FixedRay)
ProjectPoint(FixedMathSharp.Vector3d)
ClampPoint(FixedMathSharp.Vector3d)
DistanceToSurface(FixedMathSharp.Vector3d)
Transform(FixedMathSharp.Fixed4x4)
Deconstruct(FixedMathSharp.Vector3d&,FixedMathSharp.Fixed64&)
ContainsBoxLike(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetMaxBasisScale(FixedMathSharp.Fixed4x4)
NormalizeRadius(FixedMathSharp.Fixed64)
op_Equality(FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Geometry.FixedBoundSphere)
op_Inequality(FixedMathSharp.Geometry.FixedBoundSphere,FixedMathSharp.Geometry.FixedBoundSphere)
Equals(System.Object)
Equals(FixedMathSharp.Geometry.FixedBoundSphere)
GetHashCode()
ToString()
ToString(System.String,System.IFormatProvider)
TryFormat(System.Span`1<System.Char>,System.Int32&,System.ReadOnlySpan`1<System.Char>,System.IFormatProvider)